diff options
436 files changed, 8709 insertions, 1997 deletions
@@ -9,6 +9,7 @@ hackbod@google.com jjaggi@google.com jsharkey@android.com jsharkey@google.com +lorenzo@google.com michaelwr@google.com nandana@google.com narayan@google.com diff --git a/apct-tests/perftests/core/src/android/mtp_perf/AppFusePerfTest.java b/apct-tests/perftests/core/src/android/mtp_perf/AppFusePerfTest.java new file mode 100644 index 000000000000..fcbfc7212351 --- /dev/null +++ b/apct-tests/perftests/core/src/android/mtp_perf/AppFusePerfTest.java @@ -0,0 +1,122 @@ +/* + * 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 android.mtp_perf; + +import android.app.Activity; +import android.content.Context; +import android.os.Bundle; +import android.os.ParcelFileDescriptor; +import android.os.ProxyFileDescriptorCallback; +import android.os.storage.StorageManager; +import android.system.ErrnoException; + +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.LargeTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.io.IOException; + +@RunWith(AndroidJUnit4.class) +@LargeTest +public class AppFusePerfTest { + static final int SIZE = 10 * 1024 * 1024; // 10MB + + @Test + public void testReadWriteFile() throws IOException { + final Context context = InstrumentationRegistry.getInstrumentation().getTargetContext(); + final StorageManager storageManager = context.getSystemService(StorageManager.class); + + final byte[] bytes = new byte[SIZE]; + final int samples = 100; + final double[] readTime = new double[samples]; + final double[] writeTime = new double[samples]; + + for (int i = 0; i < samples; i++) { + final ParcelFileDescriptor fd = storageManager.openProxyFileDescriptor( + ParcelFileDescriptor.MODE_READ_ONLY, new TestCallback()); + try (final ParcelFileDescriptor.AutoCloseInputStream stream = + new ParcelFileDescriptor.AutoCloseInputStream(fd)) { + final long startTime = System.nanoTime(); + stream.read(bytes); + readTime[i] = (System.nanoTime() - startTime) / 1000.0 / 1000.0; + } + } + + for (int i = 0; i < samples; i++) { + final ParcelFileDescriptor fd = storageManager.openProxyFileDescriptor( + ParcelFileDescriptor.MODE_WRITE_ONLY | ParcelFileDescriptor.MODE_TRUNCATE, + new TestCallback()); + try (final ParcelFileDescriptor.AutoCloseOutputStream stream = + new ParcelFileDescriptor.AutoCloseOutputStream(fd)) { + final long startTime = System.nanoTime(); + stream.write(bytes); + writeTime[i] = (System.nanoTime() - startTime) / 1000.0 / 1000.0; + } + } + + double readAverage = 0; + double writeAverage = 0; + double readSquaredAverage = 0; + double writeSquaredAverage = 0; + for (int i = 0; i < samples; i++) { + readAverage += readTime[i]; + writeAverage += writeTime[i]; + readSquaredAverage += readTime[i] * readTime[i]; + writeSquaredAverage += writeTime[i] * writeTime[i]; + } + + readAverage /= samples; + writeAverage /= samples; + readSquaredAverage /= samples; + writeSquaredAverage /= samples; + + final Bundle results = new Bundle(); + results.putDouble("readAverage", readAverage); + results.putDouble("readStandardDeviation", + Math.sqrt(readSquaredAverage - readAverage * readAverage)); + results.putDouble("writeAverage", writeAverage); + results.putDouble("writeStandardDeviation", + Math.sqrt(writeSquaredAverage - writeAverage * writeAverage)); + InstrumentationRegistry.getInstrumentation().sendStatus(Activity.RESULT_OK, results); + } + + private static class TestCallback extends ProxyFileDescriptorCallback { + @Override + public long onGetSize() throws ErrnoException { + return SIZE; + } + + @Override + public int onRead(long offset, int size, byte[] data) throws ErrnoException { + return size; + } + + @Override + public int onWrite(long offset, int size, byte[] data) throws ErrnoException { + return size; + } + + @Override + public void onFsync() throws ErrnoException {} + + @Override + public void onRelease() {} + } +} diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java index df0a0ee38674..35dadf06f4b8 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java @@ -1978,9 +1978,12 @@ public class JobSchedulerService extends com.android.server.SystemService JobStatus runNow = (JobStatus) message.obj; // runNow can be null, which is a controller's way of indicating that its // state is such that all ready jobs should be run immediately. - if (runNow != null && isReadyToBeExecutedLocked(runNow)) { - mJobPackageTracker.notePending(runNow); - addOrderedItem(mPendingJobs, runNow, sPendingJobComparator); + if (runNow != null) { + if (!isCurrentlyActiveLocked(runNow) + && isReadyToBeExecutedLocked(runNow)) { + mJobPackageTracker.notePending(runNow); + addOrderedItem(mPendingJobs, runNow, sPendingJobComparator); + } } else { queueReadyJobsForExecutionLocked(); } diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java index 6ddafadaa871..d5130dc97bbc 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java @@ -217,20 +217,6 @@ public final class ConnectivityController extends RestrictingController implemen return jobs != null && jobs.size() > 0; } - @VisibleForTesting - @GuardedBy("mLock") - boolean wouldBeReadyWithConnectivityLocked(JobStatus jobStatus) { - final boolean networkAvailable = isNetworkAvailable(jobStatus); - if (DEBUG) { - Slog.v(TAG, "wouldBeReadyWithConnectivityLocked: " + jobStatus.toShortString() - + " networkAvailable=" + networkAvailable); - } - // If the network isn't available, then requesting an exception won't help. - - return networkAvailable && wouldBeReadyWithConstraintLocked(jobStatus, - JobStatus.CONSTRAINT_CONNECTIVITY); - } - /** * Tell NetworkPolicyManager not to block a UID's network connection if that's the only * thing stopping a job from running. @@ -243,7 +229,8 @@ public final class ConnectivityController extends RestrictingController implemen } // Always check the full job readiness stat in case the component has been disabled. - if (wouldBeReadyWithConnectivityLocked(jobStatus)) { + if (wouldBeReadyWithConstraintLocked(jobStatus, JobStatus.CONSTRAINT_CONNECTIVITY) + && isNetworkAvailable(jobStatus)) { if (DEBUG) { Slog.i(TAG, "evaluateStateLocked finds job " + jobStatus + " would be ready."); } 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 2d55aa5d5495..a02f8de5e292 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 @@ -43,6 +43,7 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.pm.PackageManagerInternal; import android.os.BatteryManager; import android.os.BatteryManagerInternal; import android.os.Handler; @@ -64,6 +65,7 @@ import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.ArrayUtils; import com.android.server.JobSchedulerBackgroundThread; import com.android.server.LocalServices; import com.android.server.job.ConstantsProto; @@ -507,6 +509,8 @@ public final class QuotaController extends StateController { QcConstants.DEFAULT_EJ_LIMIT_RESTRICTED_MS }; + private long mEjLimitSpecialAdditionMs = QcConstants.DEFAULT_EJ_LIMIT_SPECIAL_ADDITION_MS; + /** * The period of time used to calculate expedited job sessions. Apps can only have expedited job * sessions totalling {@link #mEJLimitsMs}[bucket within this period of time (without factoring @@ -517,22 +521,26 @@ public final class QuotaController extends StateController { /** * Length of time used to split an app's top time into chunks. */ - public long mEJTopAppTimeChunkSizeMs = QcConstants.DEFAULT_EJ_TOP_APP_TIME_CHUNK_SIZE_MS; + private long mEJTopAppTimeChunkSizeMs = QcConstants.DEFAULT_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. */ - public long mEJRewardTopAppMs = QcConstants.DEFAULT_EJ_REWARD_TOP_APP_MS; + private long mEJRewardTopAppMs = QcConstants.DEFAULT_EJ_REWARD_TOP_APP_MS; /** * How much EJ quota to give back to an app based on each non-top user interaction. */ - public long mEJRewardInteractionMs = QcConstants.DEFAULT_EJ_REWARD_INTERACTION_MS; + private long mEJRewardInteractionMs = QcConstants.DEFAULT_EJ_REWARD_INTERACTION_MS; /** * How much EJ quota to give back to an app based on each notification seen event. */ - public long mEJRewardNotificationSeenMs = QcConstants.DEFAULT_EJ_REWARD_NOTIFICATION_SEEN_MS; + private long mEJRewardNotificationSeenMs = QcConstants.DEFAULT_EJ_REWARD_NOTIFICATION_SEEN_MS; + + /** The package verifier app. */ + @Nullable + private String mPackageVerifier; /** An app has reached its quota. The message should contain a {@link Package} object. */ @VisibleForTesting @@ -588,6 +596,16 @@ public final class QuotaController extends StateController { } @Override + public void onSystemServicesReady() { + String[] pkgNames = LocalServices.getService(PackageManagerInternal.class) + .getKnownPackageNames( + PackageManagerInternal.PACKAGE_VERIFIER, UserHandle.USER_SYSTEM); + synchronized (mLock) { + mPackageVerifier = ArrayUtils.firstOrNull(pkgNames); + } + } + + @Override public void maybeStartTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob) { final int userId = jobStatus.getSourceUserId(); final String pkgName = jobStatus.getSourcePackageName(); @@ -875,7 +893,7 @@ public final class QuotaController extends StateController { if (quota.getStandbyBucketLocked() == NEVER_INDEX) { return 0; } - final long limitMs = mEJLimitsMs[quota.getStandbyBucketLocked()]; + final long limitMs = getEJLimitMsLocked(packageName, quota.getStandbyBucketLocked()); long remainingMs = limitMs - quota.getTallyLocked(); // Stale sessions may still be factored into tally. Make sure they're removed. @@ -912,6 +930,14 @@ public final class QuotaController extends StateController { return remainingMs - timer.getCurrentDuration(sElapsedRealtimeClock.millis()); } + private long getEJLimitMsLocked(@NonNull final String packageName, final int standbyBucket) { + final long baseLimitMs = mEJLimitsMs[standbyBucket]; + if (packageName.equals(mPackageVerifier)) { + return baseLimitMs + mEjLimitSpecialAdditionMs; + } + return baseLimitMs; + } + /** * Returns the amount of time, in milliseconds, until the package would have reached its * duration quota, assuming it has a job counting towards its quota the entire time. This takes @@ -1014,7 +1040,7 @@ public final class QuotaController extends StateController { final long nowElapsed = sElapsedRealtimeClock.millis(); ShrinkableDebits quota = getEJQuotaLocked(userId, packageName); - final long limitMs = mEJLimitsMs[quota.getStandbyBucketLocked()]; + final long limitMs = getEJLimitMsLocked(packageName, quota.getStandbyBucketLocked()); final long startWindowElapsed = Math.max(0, nowElapsed - mEJLimitWindowSizeMs); long remainingDeadSpaceMs = remainingExecutionTimeMs; // Total time looked at where a session wouldn't be phasing out. @@ -1606,7 +1632,7 @@ public final class QuotaController extends StateController { inRegularQuotaTimeElapsed = inQuotaTimeElapsed; } if (remainingEJQuota <= 0) { - final long limitMs = mEJLimitsMs[standbyBucket] - mQuotaBufferMs; + final long limitMs = getEJLimitMsLocked(packageName, standbyBucket) - mQuotaBufferMs; long sumMs = 0; final Timer ejTimer = mEJPkgTimers.get(userId, packageName); if (ejTimer != null && ejTimer.isActive()) { @@ -2741,6 +2767,9 @@ public final class QuotaController extends StateController { static final String KEY_EJ_LIMIT_RESTRICTED_MS = QC_CONSTANT_PREFIX + "ej_limit_restricted_ms"; @VisibleForTesting + static final String KEY_EJ_LIMIT_SPECIAL_ADDITION_MS = + QC_CONSTANT_PREFIX + "ej_limit_special_addition_ms"; + @VisibleForTesting static final String KEY_EJ_WINDOW_SIZE_MS = QC_CONSTANT_PREFIX + "ej_window_size_ms"; @VisibleForTesting @@ -2801,6 +2830,7 @@ public final class QuotaController extends StateController { 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_SPECIAL_ADDITION_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_EJ_REWARD_TOP_APP_MS = 10 * SECOND_IN_MILLIS; @@ -3001,6 +3031,11 @@ public final class QuotaController extends StateController { public long EJ_LIMIT_RESTRICTED_MS = DEFAULT_EJ_LIMIT_RESTRICTED_MS; /** + * How much additional EJ quota special, critical apps should get. + */ + public long EJ_LIMIT_SPECIAL_ADDITION_MS = DEFAULT_EJ_LIMIT_SPECIAL_ADDITION_MS; + + /** * The period of time used to calculate expedited job sessions. Apps can only have expedited * job sessions totalling EJ_LIMIT_<bucket>_MS within this period of time (without factoring * in any rewards or free EJs). @@ -3053,6 +3088,7 @@ public final class QuotaController extends StateController { case KEY_EJ_LIMIT_FREQUENT_MS: case KEY_EJ_LIMIT_RARE_MS: case KEY_EJ_LIMIT_RESTRICTED_MS: + case KEY_EJ_LIMIT_SPECIAL_ADDITION_MS: case KEY_EJ_WINDOW_SIZE_MS: updateEJLimitConstantsLocked(); break; @@ -3376,7 +3412,8 @@ public final class QuotaController extends StateController { DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_EJ_LIMIT_ACTIVE_MS, KEY_EJ_LIMIT_WORKING_MS, KEY_EJ_LIMIT_FREQUENT_MS, KEY_EJ_LIMIT_RARE_MS, - KEY_EJ_LIMIT_RESTRICTED_MS, KEY_EJ_WINDOW_SIZE_MS); + KEY_EJ_LIMIT_RESTRICTED_MS, KEY_EJ_LIMIT_SPECIAL_ADDITION_MS, + KEY_EJ_WINDOW_SIZE_MS); EJ_LIMIT_ACTIVE_MS = properties.getLong( KEY_EJ_LIMIT_ACTIVE_MS, DEFAULT_EJ_LIMIT_ACTIVE_MS); EJ_LIMIT_WORKING_MS = properties.getLong( @@ -3387,6 +3424,8 @@ public final class QuotaController extends StateController { KEY_EJ_LIMIT_RARE_MS, DEFAULT_EJ_LIMIT_RARE_MS); EJ_LIMIT_RESTRICTED_MS = properties.getLong( KEY_EJ_LIMIT_RESTRICTED_MS, DEFAULT_EJ_LIMIT_RESTRICTED_MS); + EJ_LIMIT_SPECIAL_ADDITION_MS = properties.getLong( + KEY_EJ_LIMIT_SPECIAL_ADDITION_MS, DEFAULT_EJ_LIMIT_SPECIAL_ADDITION_MS); EJ_WINDOW_SIZE_MS = properties.getLong( KEY_EJ_WINDOW_SIZE_MS, DEFAULT_EJ_WINDOW_SIZE_MS); @@ -3432,6 +3471,13 @@ public final class QuotaController extends StateController { mEJLimitsMs[RESTRICTED_INDEX] = newRestrictedLimitMs; mShouldReevaluateConstraints = true; } + // The addition must be in the range [0 minutes, window size - active limit]. + long newSpecialAdditionMs = Math.max(0, + Math.min(newWindowSizeMs - newActiveLimitMs, EJ_LIMIT_SPECIAL_ADDITION_MS)); + if (mEjLimitSpecialAdditionMs != newSpecialAdditionMs) { + mEjLimitSpecialAdditionMs = newSpecialAdditionMs; + mShouldReevaluateConstraints = true; + } } private void dump(IndentingPrintWriter pw) { @@ -3470,6 +3516,7 @@ public final class QuotaController extends StateController { pw.print(KEY_EJ_LIMIT_FREQUENT_MS, EJ_LIMIT_FREQUENT_MS).println(); pw.print(KEY_EJ_LIMIT_RARE_MS, EJ_LIMIT_RARE_MS).println(); pw.print(KEY_EJ_LIMIT_RESTRICTED_MS, EJ_LIMIT_RESTRICTED_MS).println(); + pw.print(KEY_EJ_LIMIT_SPECIAL_ADDITION_MS, EJ_LIMIT_SPECIAL_ADDITION_MS).println(); pw.print(KEY_EJ_WINDOW_SIZE_MS, EJ_WINDOW_SIZE_MS).println(); pw.print(KEY_EJ_TOP_APP_TIME_CHUNK_SIZE_MS, EJ_TOP_APP_TIME_CHUNK_SIZE_MS).println(); pw.print(KEY_EJ_REWARD_TOP_APP_MS, EJ_REWARD_TOP_APP_MS).println(); @@ -3593,6 +3640,11 @@ public final class QuotaController extends StateController { } @VisibleForTesting + long getEjLimitSpecialAdditionMs() { + return mEjLimitSpecialAdditionMs; + } + + @VisibleForTesting @NonNull long getEJLimitWindowSizeMs() { return mEJLimitWindowSizeMs; diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/TimeController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/TimeController.java index 2b5aab83463b..ede14ec06c71 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/TimeController.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/TimeController.java @@ -157,31 +157,27 @@ public final class TimeController extends StateController { && !job.isConstraintSatisfied(JobStatus.CONSTRAINT_DEADLINE) && job.getLatestRunTimeElapsed() <= mNextJobExpiredElapsedMillis) { if (evaluateDeadlineConstraint(job, nowElapsedMillis)) { - checkExpiredDeadlinesAndResetAlarm(); - checkExpiredDelaysAndResetAlarm(); - } else { - final boolean isAlarmForJob = - job.getLatestRunTimeElapsed() == mNextJobExpiredElapsedMillis; - final boolean wouldBeReady = wouldBeReadyWithConstraintLocked( - job, JobStatus.CONSTRAINT_DEADLINE); - if ((isAlarmForJob && !wouldBeReady) || (!isAlarmForJob && wouldBeReady)) { - checkExpiredDeadlinesAndResetAlarm(); + if (job.isReady()) { + // If the job still isn't ready, there's no point trying to rush the + // Scheduler. + mStateChangedListener.onRunJobNow(job); } + } else if (wouldBeReadyWithConstraintLocked(job, JobStatus.CONSTRAINT_DEADLINE)) { + // This job's deadline is earlier than the current set alarm. Update the alarm. + setDeadlineExpiredAlarmLocked(job.getLatestRunTimeElapsed(), + deriveWorkSource(job.getSourceUid(), job.getSourcePackageName())); } } if (job.hasTimingDelayConstraint() && !job.isConstraintSatisfied(JobStatus.CONSTRAINT_TIMING_DELAY) && job.getEarliestRunTime() <= mNextDelayExpiredElapsedMillis) { - if (evaluateTimingDelayConstraint(job, nowElapsedMillis)) { - checkExpiredDelaysAndResetAlarm(); - } else { - final boolean isAlarmForJob = - job.getEarliestRunTime() == mNextDelayExpiredElapsedMillis; - final boolean wouldBeReady = wouldBeReadyWithConstraintLocked( - job, JobStatus.CONSTRAINT_TIMING_DELAY); - if ((isAlarmForJob && !wouldBeReady) || (!isAlarmForJob && wouldBeReady)) { - checkExpiredDelaysAndResetAlarm(); - } + // Since this is just the delay, we don't need to rush the Scheduler to run the job + // immediately if the constraint is satisfied here. + if (!evaluateTimingDelayConstraint(job, nowElapsedMillis) + && wouldBeReadyWithConstraintLocked(job, JobStatus.CONSTRAINT_TIMING_DELAY)) { + // This job's delay is earlier than the current set alarm. Update the alarm. + setDelayExpiredAlarmLocked(job.getEarliestRunTime(), + deriveWorkSource(job.getSourceUid(), job.getSourcePackageName())); } } } diff --git a/apex/media/framework/java/android/media/MediaTranscodeManager.java b/apex/media/framework/java/android/media/MediaTranscodeManager.java index d449289f156f..3d706e40bc0b 100644 --- a/apex/media/framework/java/android/media/MediaTranscodeManager.java +++ b/apex/media/framework/java/android/media/MediaTranscodeManager.java @@ -109,6 +109,9 @@ public final class MediaTranscodeManager { /** Interval between trying to reconnect to the service. */ private static final int INTERVAL_CONNECT_SERVICE_RETRY_MS = 40; + /** Default bpp(bits-per-pixel) to use for calculating default bitrate. */ + private static final float BPP = 0.25f; + /** * Default transcoding type. * @hide @@ -1002,15 +1005,93 @@ public final class MediaTranscodeManager { if (!shouldTranscode()) { return null; } - // TODO(hkuang): Only modified the video codec type, and use fixed bitrate for now. - // May switch to transcoding profile when it's available. + MediaFormat videoTrackFormat = new MediaFormat(mSrcVideoFormatHint); videoTrackFormat.setString(MediaFormat.KEY_MIME, MediaFormat.MIMETYPE_VIDEO_AVC); - videoTrackFormat.setInteger(MediaFormat.KEY_BIT_RATE, BIT_RATE); + + int width = mSrcVideoFormatHint.getInteger(MediaFormat.KEY_WIDTH); + int height = mSrcVideoFormatHint.getInteger(MediaFormat.KEY_HEIGHT); + if (width <= 0 || height <= 0) { + throw new IllegalArgumentException( + "Source Width and height must be larger than 0"); + } + + // TODO(hkuang): Remove the hardcoded frameRate after b/176940364 is fixed. + float frameRate = (float) 30.0; + /*mSrcVideoFormatHint.getFloat(MediaFormat.KEY_FRAME_RATE, frameRate); + if (frameRate <= 0) { + throw new IllegalArgumentException( + "frameRate must be larger than 0"); + }*/ + + int bitrate = getAVCBitrate(width, height, frameRate); + videoTrackFormat.setInteger(MediaFormat.KEY_BIT_RATE, bitrate); return videoTrackFormat; } /** + * Generate a default bitrate with the fixed bpp(bits-per-pixel) 0.25. + * This maps to: + * 1080P@30fps -> 16Mbps + * 1080P@60fps-> 32Mbps + * 4K@30fps -> 62Mbps + */ + private static int getDefaultBitrate(int width, int height, float frameRate) { + return (int) (width * height * frameRate * BPP); + } + + /** + * Query the bitrate from CamcorderProfile. If there are two profiles that match the + * width/height/framerate, we will use the higher one to get better quality. + * Return default bitrate if could not find any match profile. + */ + private static int getAVCBitrate(int width, int height, float frameRate) { + int bitrate = -1; + int[] cameraIds = {0, 1}; + + // Profiles ordered in decreasing order of preference. + int[] preferQualities = { + CamcorderProfile.QUALITY_2160P, + CamcorderProfile.QUALITY_1080P, + CamcorderProfile.QUALITY_720P, + CamcorderProfile.QUALITY_480P, + CamcorderProfile.QUALITY_LOW, + }; + + for (int cameraId : cameraIds) { + for (int quality : preferQualities) { + // Check if camera id has profile for the quality level. + if (!CamcorderProfile.hasProfile(cameraId, quality)) { + continue; + } + CamcorderProfile profile = CamcorderProfile.get(cameraId, quality); + // Check the width/height/framerate/codec, also consider portrait case. + if (((width == profile.videoFrameWidth + && height == profile.videoFrameHeight) + || (height == profile.videoFrameWidth + && width == profile.videoFrameHeight)) + && (int) frameRate == profile.videoFrameRate + && profile.videoCodec == MediaRecorder.VideoEncoder.H264) { + if (bitrate < profile.videoBitRate) { + bitrate = profile.videoBitRate; + } + break; + } + } + } + + if (bitrate == -1) { + Log.w(TAG, "Failed to find CamcorderProfile for w: " + width + "h: " + height + + " fps: " + + frameRate); + bitrate = getDefaultBitrate(width, height, frameRate); + } + Log.d(TAG, "Using bitrate " + bitrate + " for " + width + " " + height + " " + + frameRate); + return bitrate; + } + + /** * Retrieves the audio track format to be used for transcoding. * * @return the audio track format to be used if transcoding should be performed, and diff --git a/core/api/current.txt b/core/api/current.txt index 29d4915a4953..4ad5e49bf6c0 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -50464,6 +50464,7 @@ package android.view.inputmethod { method public int describeContents(); method public void dump(android.util.Printer, String); method @Nullable public CharSequence getInitialSelectedText(int); + method @Nullable public android.view.inputmethod.SurroundingText getInitialSurroundingText(@IntRange(from=0) int, @IntRange(from=0) int, int); method @Nullable public CharSequence getInitialTextAfterCursor(int, int); method @Nullable public CharSequence getInitialTextBeforeCursor(int, int); method public final void makeCompatible(int); @@ -50627,6 +50628,7 @@ package android.view.inputmethod { method public boolean sendKeyEvent(android.view.KeyEvent); method public boolean setComposingRegion(int, int); method public boolean setComposingText(CharSequence, int); + method public default boolean setImeTemporarilyConsumesInput(boolean); method public boolean setSelection(int, int); field public static final int CURSOR_UPDATE_IMMEDIATE = 1; // 0x1 field public static final int CURSOR_UPDATE_MONITOR = 2; // 0x2 diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt index f22b0f4b22ad..ef6c8b713c2c 100644 --- a/core/api/module-lib-current.txt +++ b/core/api/module-lib-current.txt @@ -109,8 +109,11 @@ package android.media.session { public final class MediaSessionManager { method public void addOnActiveSessionsChangedListener(@NonNull android.media.session.MediaSessionManager.OnActiveSessionsChangedListener, @Nullable android.content.ComponentName, int, @Nullable android.os.Handler); + method public void dispatchMediaKeyEvent(@NonNull android.view.KeyEvent); + method public void dispatchMediaKeyEvent(@NonNull android.view.KeyEvent, boolean); method public void dispatchMediaKeyEventAsSystemService(@NonNull android.view.KeyEvent); method public boolean dispatchMediaKeyEventToSessionAsSystemService(@NonNull android.view.KeyEvent, @NonNull android.media.session.MediaSession.Token); + method public void dispatchVolumeKeyEvent(@NonNull android.view.KeyEvent, int, boolean); method public void dispatchVolumeKeyEventAsSystemService(@NonNull android.view.KeyEvent, int); method public void dispatchVolumeKeyEventToSessionAsSystemService(@NonNull android.view.KeyEvent, @NonNull android.media.session.MediaSession.Token); method @NonNull public java.util.List<android.media.session.MediaController> getActiveSessionsForUser(@Nullable android.content.ComponentName, int); diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 1553214bc2eb..36b565b94db3 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -661,6 +661,10 @@ package android.app { field public static final int FLAG_AUTOGROUP_SUMMARY = 1024; // 0x400 } + public static class Notification.Action implements android.os.Parcelable { + field public static final int SEMANTIC_ACTION_MARK_CONVERSATION_AS_PRIORITY = 11; // 0xb + } + public static final class Notification.TvExtender implements android.app.Notification.Extender { ctor public Notification.TvExtender(); ctor public Notification.TvExtender(android.app.Notification); @@ -1366,6 +1370,8 @@ package android.app.role { method @NonNull @RequiresPermission("com.android.permissioncontroller.permission.MANAGE_ROLES_FROM_CONTROLLER") public java.util.List<java.lang.String> getHeldRolesFromController(@NonNull String); method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public java.util.List<java.lang.String> getRoleHolders(@NonNull String); method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public java.util.List<java.lang.String> getRoleHoldersAsUser(@NonNull String, @NonNull android.os.UserHandle); + method @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public void isApplicationVisibleForRole(@NonNull String, @NonNull String, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>); + method @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public void isRoleVisible(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>); method @RequiresPermission(android.Manifest.permission.OBSERVE_ROLE_HOLDERS) public void removeOnRoleHoldersChangedListenerAsUser(@NonNull android.app.role.OnRoleHoldersChangedListener, @NonNull android.os.UserHandle); method @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public void removeRoleHolderAsUser(@NonNull String, @NonNull String, int, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>); method @RequiresPermission("com.android.permissioncontroller.permission.MANAGE_ROLES_FROM_CONTROLLER") public boolean removeRoleHolderFromController(@NonNull String, @NonNull String); @@ -9852,6 +9858,7 @@ package android.service.voice { field public static final int RECOGNITION_FLAG_ENABLE_AUDIO_NOISE_SUPPRESSION = 8; // 0x8 field public static final int RECOGNITION_MODE_USER_IDENTIFICATION = 2; // 0x2 field public static final int RECOGNITION_MODE_VOICE_TRIGGER = 1; // 0x1 + field public static final int STATE_ERROR = 3; // 0x3 field public static final int STATE_HARDWARE_UNAVAILABLE = -2; // 0xfffffffe field public static final int STATE_KEYPHRASE_ENROLLED = 2; // 0x2 field public static final int STATE_KEYPHRASE_UNENROLLED = 1; // 0x1 diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 1086577b0a39..d226d6252803 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -368,6 +368,7 @@ package android.app { package android.app.admin { public class DevicePolicyManager { + method public void forceUpdateUserSetupComplete(); method public long getLastBugReportRequestTime(); method public long getLastNetworkLogRetrievalTime(); method public long getLastSecurityLogRetrievalTime(); @@ -441,11 +442,6 @@ package android.app.prediction { package android.app.role { - public class RoleControllerManager { - method @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public void isApplicationVisibleForRole(@NonNull String, @NonNull String, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>); - method @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public void isRoleVisible(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>); - } - public final class RoleManager { method @Nullable public String getSmsRoleHolder(int); } diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 8242e4db16c3..297b9ff1af16 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -30,7 +30,6 @@ import android.annotation.IdRes; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; -import android.annotation.Px; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; @@ -83,6 +82,7 @@ import android.util.ArraySet; import android.util.Log; import android.util.Pair; import android.util.SparseArray; +import android.util.TypedValue; import android.util.proto.ProtoOutputStream; import android.view.ContextThemeWrapper; import android.view.Gravity; @@ -218,6 +218,11 @@ public class Notification implements Parcelable private static final int MAX_REPLY_HISTORY = 5; /** + * Maximum aspect ratio of the large icon. 16:9 + */ + private static final float MAX_LARGE_ICON_ASPECT_RATIO = 16f / 9f; + + /** * Maximum number of (generic) action buttons in a notification (contextual action buttons are * handled separately). * @hide @@ -1523,6 +1528,14 @@ public class Notification implements Parcelable */ public static final int SEMANTIC_ACTION_CALL = 10; + /** + * {@code SemanticAction}: Mark the conversation associated with the notification as a + * priority. Note that this is only for use by the notification assistant services. + * @hide + */ + @SystemApi + public static final int SEMANTIC_ACTION_MARK_CONVERSATION_AS_PRIORITY = 11; + private final Bundle mExtras; @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) private Icon mIcon; @@ -2233,7 +2246,8 @@ public class Notification implements Parcelable SEMANTIC_ACTION_UNMUTE, SEMANTIC_ACTION_THUMBS_UP, SEMANTIC_ACTION_THUMBS_DOWN, - SEMANTIC_ACTION_CALL + SEMANTIC_ACTION_CALL, + SEMANTIC_ACTION_MARK_CONVERSATION_AS_PRIORITY }) @Retention(RetentionPolicy.SOURCE) public @interface SemanticAction {} @@ -4228,9 +4242,9 @@ public class Notification implements Parcelable /** * Add a large icon to the notification content view. * - * In the platform template, this image will be shown on the left of the notification view - * in place of the {@link #setSmallIcon(Icon) small icon} (which will be placed in a small - * badge atop the large icon). + * In the platform template, this image will be shown either on the right of the + * notification, with an aspect ratio of up to 16:9, or (when the notification is grouped) + * on the left in place of the {@link #setSmallIcon(Icon) small icon}. */ @NonNull public Builder setLargeIcon(Bitmap b) { @@ -4240,9 +4254,9 @@ public class Notification implements Parcelable /** * Add a large icon to the notification content view. * - * In the platform template, this image will be shown on the left of the notification view - * in place of the {@link #setSmallIcon(Icon) small icon} (which will be placed in a small - * badge atop the large icon). + * In the platform template, this image will be shown either on the right of the + * notification, with an aspect ratio of up to 16:9, or (when the notification is grouped) + * on the left in place of the {@link #setSmallIcon(Icon) small icon}. */ @NonNull public Builder setLargeIcon(Icon icon) { @@ -4911,7 +4925,8 @@ public class Notification implements Parcelable setTextViewColorPrimary(contentView, R.id.title, p); contentView.setViewLayoutWidth(R.id.title, showProgress ? ViewGroup.LayoutParams.WRAP_CONTENT - : ViewGroup.LayoutParams.MATCH_PARENT); + : ViewGroup.LayoutParams.MATCH_PARENT, + TypedValue.COMPLEX_UNIT_PX); } if (p.text != null && p.text.length() != 0) { int textId = showProgress ? com.android.internal.R.id.text_line_1 @@ -5109,8 +5124,7 @@ public class Notification implements Parcelable if (result == null) { result = new TemplateBindResult(); } - final boolean largeIconShown = bindLargeIcon(contentView, p); - calculateLargeIconMarginEnd(largeIconShown, result); + bindLargeIcon(contentView, p, result); if (p.mHeaderless) { // views in the headerless (collapsed) state result.mHeadingExtraMarginSet.applyToView(contentView, @@ -5122,28 +5136,54 @@ public class Notification implements Parcelable } } - private void calculateLargeIconMarginEnd(boolean largeIconShown, + // This code is executed on behalf of other apps' notifications, sometimes even by 3p apps, + // a use case that is not supported by the Compat Framework library. Workarounds to resolve + // the change's state in NotificationManagerService were very complex. These behavior + // changes are entirely visual, and should otherwise be undetectable by apps. + @SuppressWarnings("AndroidFrameworkCompatChange") + private void calculateLargeIconDimens(boolean largeIconShown, @NonNull TemplateBindResult result) { final Resources resources = mContext.getResources(); - final int contentMargin = resources.getDimensionPixelOffset( - R.dimen.notification_content_margin_end); - final int expanderSize = resources.getDimensionPixelSize( - R.dimen.notification_header_expand_icon_size) - contentMargin; - final int extraMarginEndIfVisible = resources.getDimensionPixelSize( - R.dimen.notification_right_icon_size) + contentMargin; - result.setRightIconState(largeIconShown, extraMarginEndIfVisible, expanderSize); + final float density = resources.getDisplayMetrics().density; + final float contentMarginDp = resources.getDimension( + R.dimen.notification_content_margin_end) / density; + final float expanderSizeDp = resources.getDimension( + R.dimen.notification_header_expand_icon_size) / density - contentMarginDp; + final float viewHeightDp = resources.getDimension( + R.dimen.notification_right_icon_size) / density; + float viewWidthDp = viewHeightDp; // icons are 1:1 by default + if (largeIconShown && ( + mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.S + || DevFlags.shouldBackportSNotifRules(mContext.getContentResolver()))) { + Drawable drawable = mN.mLargeIcon.loadDrawable(mContext); + if (drawable != null) { + int iconWidth = drawable.getIntrinsicWidth(); + int iconHeight = drawable.getIntrinsicHeight(); + if (iconWidth > iconHeight && iconHeight > 0) { + final float maxViewWidthDp = viewHeightDp * MAX_LARGE_ICON_ASPECT_RATIO; + viewWidthDp = Math.min(viewHeightDp * iconWidth / iconHeight, + maxViewWidthDp); + } + } + } + final float extraMarginEndDpIfVisible = viewWidthDp + contentMarginDp; + result.setRightIconState(largeIconShown, viewWidthDp, + extraMarginEndDpIfVisible, expanderSizeDp); } /** * Bind the large icon. - * @return if the largeIcon is visible */ - private boolean bindLargeIcon(RemoteViews contentView, StandardTemplateParams p) { + private void bindLargeIcon(RemoteViews contentView, @NonNull StandardTemplateParams p, + @NonNull TemplateBindResult result) { if (mN.mLargeIcon == null && mN.largeIcon != null) { mN.mLargeIcon = Icon.createWithBitmap(mN.largeIcon); } boolean showLargeIcon = mN.mLargeIcon != null && !p.hideLargeIcon; + calculateLargeIconDimens(showLargeIcon, result); if (showLargeIcon) { + contentView.setViewLayoutWidth(R.id.right_icon, + result.mRightIconWidthDp, TypedValue.COMPLEX_UNIT_DIP); contentView.setViewVisibility(R.id.right_icon, View.VISIBLE); contentView.setImageViewIcon(R.id.right_icon, mN.mLargeIcon); processLargeLegacyIcon(mN.mLargeIcon, contentView, p); @@ -5153,7 +5193,6 @@ public class Notification implements Parcelable // visibility) is used by NotificationGroupingUtil to set the visibility. contentView.setImageViewIcon(R.id.right_icon, null); } - return showLargeIcon; } private void bindNotificationHeader(RemoteViews contentView, StandardTemplateParams p) { @@ -5356,8 +5395,9 @@ public class Notification implements Parcelable final boolean snoozeEnabled = mContext.getContentResolver() != null && (Settings.Secure.getInt(mContext.getContentResolver(), Settings.Secure.SHOW_NOTIFICATION_SNOOZE, 0) == 1); - big.setViewLayoutMarginBottomDimen(R.id.notification_action_list_margin_target, - snoozeEnabled ? 0 : R.dimen.notification_content_margin); + int bottomMarginDimen = snoozeEnabled ? 0 : R.dimen.notification_content_margin; + big.setViewLayoutMarginDimen(R.id.notification_action_list_margin_target, + RemoteViews.MARGIN_BOTTOM, bottomMarginDimen); } private static List<Notification.Action> filterOutContextualActions( @@ -5389,7 +5429,8 @@ public class Notification implements Parcelable if (N > 0) { big.setViewVisibility(R.id.actions_container, View.VISIBLE); big.setViewVisibility(R.id.actions, View.VISIBLE); - big.setViewLayoutMarginBottomDimen(R.id.notification_action_list_margin_target, 0); + big.setViewLayoutMarginDimen(R.id.notification_action_list_margin_target, + RemoteViews.MARGIN_BOTTOM, 0); if (N>MAX_ACTION_BUTTONS) N=MAX_ACTION_BUTTONS; for (int i=0; i<N; i++) { Action action = nonContextualActions.get(i); @@ -7788,8 +7829,9 @@ public class Notification implements Parcelable // also update the end margin if there is an image // NOTE: This template doesn't support moving this icon to the left, so we don't // need to fully apply the MarginSet - contentView.setViewLayoutMarginEnd(R.id.notification_messaging, - bindResult.mHeadingExtraMarginSet.getValue()); + contentView.setViewLayoutMargin(R.id.notification_messaging, RemoteViews.MARGIN_END, + bindResult.mHeadingExtraMarginSet.getDpValue(), + TypedValue.COMPLEX_UNIT_DIP); } contentView.setInt(R.id.status_bar_latest_event_content, "setLayoutColor", mBuilder.isColorized(p) @@ -8613,7 +8655,8 @@ public class Notification implements Parcelable if (mBuilder.mN.hasLargeIcon()) { endMargin = R.dimen.notification_media_image_margin_end; } - view.setViewLayoutMarginEndDimen(R.id.notification_main_column, endMargin); + view.setViewLayoutMarginDimen(R.id.notification_main_column, + RemoteViews.MARGIN_END, endMargin); return view; } @@ -8650,8 +8693,8 @@ public class Notification implements Parcelable private void handleImage(RemoteViews contentView) { if (mBuilder.mN.hasLargeIcon()) { - contentView.setViewLayoutMarginEndDimen(R.id.line1, 0); - contentView.setViewLayoutMarginEndDimen(R.id.text, 0); + contentView.setViewLayoutMarginDimen(R.id.line1, RemoteViews.MARGIN_END, 0); + contentView.setViewLayoutMarginDimen(R.id.text, RemoteViews.MARGIN_END, 0); } } @@ -8787,7 +8830,8 @@ public class Notification implements Parcelable // also update the end margin to account for the large icon or expander Resources resources = mBuilder.mContext.getResources(); result.mTitleMarginSet.applyToView(remoteViews, R.id.notification_main_column, - resources.getDimensionPixelOffset(R.dimen.notification_content_margin_end)); + resources.getDimension(R.dimen.notification_content_margin_end) + / resources.getDisplayMetrics().density); } } @@ -11025,6 +11069,7 @@ public class Notification implements Parcelable */ private static class TemplateBindResult { boolean mRightIconVisible; + float mRightIconWidthDp; /** * The margin end that needs to be added to the heading so that it won't overlap @@ -11049,11 +11094,13 @@ public class Notification implements Parcelable */ public final MarginSet mTitleMarginSet = new MarginSet(); - public void setRightIconState(boolean visible, int marginEndIfVisible, int expanderSize) { + public void setRightIconState(boolean visible, float widthDp, + float marginEndDpIfVisible, float expanderSizeDp) { mRightIconVisible = visible; - mHeadingExtraMarginSet.setValues(0, marginEndIfVisible); - mHeadingFullMarginSet.setValues(expanderSize, marginEndIfVisible + expanderSize); - mTitleMarginSet.setValues(0, marginEndIfVisible + expanderSize); + mRightIconWidthDp = widthDp; + mHeadingExtraMarginSet.setValues(0, marginEndDpIfVisible); + mHeadingFullMarginSet.setValues(expanderSizeDp, marginEndDpIfVisible + expanderSizeDp); + mTitleMarginSet.setValues(0, marginEndDpIfVisible + expanderSizeDp); } /** @@ -11062,10 +11109,10 @@ public class Notification implements Parcelable * left_icon and adjust the margins, and to undo that change as well. */ private class MarginSet { - private int mValueIfGone; - private int mValueIfVisible; + private float mValueIfGone; + private float mValueIfVisible; - public void setValues(int valueIfGone, int valueIfVisible) { + public void setValues(float valueIfGone, float valueIfVisible) { mValueIfGone = valueIfGone; mValueIfVisible = valueIfVisible; } @@ -11075,22 +11122,26 @@ public class Notification implements Parcelable } public void applyToView(@NonNull RemoteViews views, @IdRes int viewId, - @Px int extraMargin) { - final int marginEnd = getValue() + extraMargin; + float extraMarginDp) { + final float marginEndDp = getDpValue() + extraMarginDp; if (viewId == R.id.notification_header) { - views.setInt(R.id.notification_header, "setTopLineExtraMarginEnd", marginEnd); + views.setFloat(R.id.notification_header, + "setTopLineExtraMarginEndDp", marginEndDp); } else { - views.setViewLayoutMarginEnd(viewId, marginEnd); + views.setViewLayoutMargin(viewId, RemoteViews.MARGIN_END, + marginEndDp, TypedValue.COMPLEX_UNIT_DIP); } if (mRightIconVisible) { views.setIntTag(viewId, R.id.tag_margin_end_when_icon_visible, - mValueIfVisible + extraMargin); + TypedValue.createComplexDimension( + mValueIfVisible + extraMarginDp, TypedValue.COMPLEX_UNIT_DIP)); views.setIntTag(viewId, R.id.tag_margin_end_when_icon_gone, - mValueIfGone + extraMargin); + TypedValue.createComplexDimension( + mValueIfGone + extraMarginDp, TypedValue.COMPLEX_UNIT_DIP)); } } - public int getValue() { + public float getDpValue() { return mRightIconVisible ? mValueIfVisible : mValueIfGone; } } diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java index ae1c89468b18..2ce0e877aa9f 100644 --- a/core/java/android/app/SystemServiceRegistry.java +++ b/core/java/android/app/SystemServiceRegistry.java @@ -30,7 +30,6 @@ import android.app.contentsuggestions.ContentSuggestionsManager; import android.app.contentsuggestions.IContentSuggestionsManager; import android.app.job.JobSchedulerFrameworkInitializer; import android.app.prediction.AppPredictionManager; -import android.app.role.RoleControllerManager; import android.app.role.RoleManager; import android.app.search.SearchUiManager; import android.app.slice.SliceManager; @@ -1291,14 +1290,6 @@ public final class SystemServiceRegistry { return new RoleManager(ctx.getOuterContext()); }}); - registerService(Context.ROLE_CONTROLLER_SERVICE, RoleControllerManager.class, - new CachedServiceFetcher<RoleControllerManager>() { - @Override - public RoleControllerManager createService(ContextImpl ctx) - throws ServiceNotFoundException { - return new RoleControllerManager(ctx.getOuterContext()); - }}); - registerService(Context.DYNAMIC_SYSTEM_SERVICE, DynamicSystemManager.class, new CachedServiceFetcher<DynamicSystemManager>() { @Override diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 259c1a16d85a..806cb496c4c3 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -11241,6 +11241,7 @@ public class DevicePolicyManager { * {@code android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS} or the caller is * not {@link UserHandle#SYSTEM_USER} */ + @TestApi public void forceUpdateUserSetupComplete() { try { mService.forceUpdateUserSetupComplete(); diff --git a/core/java/android/app/admin/DevicePolicySafetyChecker.java b/core/java/android/app/admin/DevicePolicySafetyChecker.java index 1f8a9335b9ac..b1a80c55cf1e 100644 --- a/core/java/android/app/admin/DevicePolicySafetyChecker.java +++ b/core/java/android/app/admin/DevicePolicySafetyChecker.java @@ -18,6 +18,8 @@ package android.app.admin; import android.annotation.NonNull; import android.app.admin.DevicePolicyManager.DevicePolicyOperation; +import com.android.internal.os.IResultReceiver; + /** * Interface responsible to check if a {@link DevicePolicyManager} API can be safely executed. * @@ -28,9 +30,7 @@ public interface DevicePolicySafetyChecker { /** * Returns whether the given {@code operation} can be safely executed at the moment. */ - default boolean isDevicePolicyOperationSafe(@DevicePolicyOperation int operation) { - return true; - } + boolean isDevicePolicyOperationSafe(@DevicePolicyOperation int operation); /** * Returns a new exception for when the given {@code operation} cannot be safely executed. @@ -39,4 +39,13 @@ public interface DevicePolicySafetyChecker { default UnsafeStateException newUnsafeStateException(@DevicePolicyOperation int operation) { return new UnsafeStateException(operation); } + + /** + * Called when a request was made to factory reset the device, so it can be delayed if it's not + * safe to proceed. + * + * @param callback callback whose {@code send()} method must be called when it's safe to factory + * reset. + */ + void onFactoryReset(IResultReceiver callback); } diff --git a/core/java/android/app/role/RoleControllerManager.java b/core/java/android/app/role/RoleControllerManager.java index 8dde2c55d7d3..ba1f61290631 100644 --- a/core/java/android/app/role/RoleControllerManager.java +++ b/core/java/android/app/role/RoleControllerManager.java @@ -20,8 +20,6 @@ import android.Manifest; import android.annotation.CallbackExecutor; import android.annotation.NonNull; import android.annotation.RequiresPermission; -import android.annotation.SystemService; -import android.annotation.TestApi; import android.app.ActivityThread; import android.content.ComponentName; import android.content.Context; @@ -48,8 +46,6 @@ import java.util.function.Consumer; * * @hide */ -@SystemService(Context.ROLE_CONTROLLER_SERVICE) -@TestApi public class RoleControllerManager { private static final String LOG_TAG = RoleControllerManager.class.getSimpleName(); @@ -199,32 +195,11 @@ public class RoleControllerManager { } /** - * @see RoleControllerService#onIsApplicationQualifiedForRole(String, String) - * - * @deprecated Use {@link #isApplicationVisibleForRole(String, String, Executor, Consumer)} - * instead. - * - * @hide - */ - @RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS) - public void isApplicationQualifiedForRole(@NonNull String roleName, @NonNull String packageName, - @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Boolean> callback) { - AndroidFuture<Bundle> operation = mRemoteService.postAsync(service -> { - AndroidFuture<Bundle> future = new AndroidFuture<>(); - service.isApplicationQualifiedForRole(roleName, packageName, - new RemoteCallback(future::complete)); - return future; - }); - propagateCallback(operation, "isApplicationQualifiedForRole", executor, callback); - } - - /** * @see RoleControllerService#onIsApplicationVisibleForRole(String, String) * * @hide */ @RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS) - @TestApi public void isApplicationVisibleForRole(@NonNull String roleName, @NonNull String packageName, @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Boolean> callback) { AndroidFuture<Bundle> operation = mRemoteService.postAsync(service -> { @@ -242,7 +217,6 @@ public class RoleControllerManager { * @hide */ @RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS) - @TestApi public void isRoleVisible(@NonNull String roleName, @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Boolean> callback) { AndroidFuture<Bundle> operation = mRemoteService.postAsync(service -> { diff --git a/core/java/android/app/role/RoleManager.java b/core/java/android/app/role/RoleManager.java index 8b2e07b09701..9ddd5bef5c6f 100644 --- a/core/java/android/app/role/RoleManager.java +++ b/core/java/android/app/role/RoleManager.java @@ -174,6 +174,9 @@ public final class RoleManager { @NonNull private final Object mListenersLock = new Object(); + @NonNull + private final RoleControllerManager mRoleControllerManager; + /** * @hide */ @@ -181,6 +184,7 @@ public final class RoleManager { mContext = context; mService = IRoleManager.Stub.asInterface(ServiceManager.getServiceOrThrow( Context.ROLE_SERVICE)); + mRoleControllerManager = new RoleControllerManager(context); } /** @@ -676,6 +680,44 @@ public final class RoleManager { } } + /** + * Check whether a role should be visible to user. + * + * @param roleName name of the role to check for + * @param executor the executor to execute callback on + * @param callback the callback to receive whether the role should be visible to user + * + * @hide + */ + @RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS) + @SystemApi + public void isRoleVisible(@NonNull String roleName, + @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Boolean> callback) { + mRoleControllerManager.isRoleVisible(roleName, executor, callback); + } + + /** + * Check whether an application is visible for a role. + * + * While an application can be qualified for a role, it can still stay hidden from user (thus + * not visible). If an application is visible for a role, we may show things related to the role + * for it, e.g. showing an entry pointing to the role settings in its application info page. + * + * @param roleName the name of the role to check for + * @param packageName the package name of the application to check for + * @param executor the executor to execute callback on + * @param callback the callback to receive whether the application is visible for the role + * + * @hide + */ + @RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS) + @SystemApi + public void isApplicationVisibleForRole(@NonNull String roleName, @NonNull String packageName, + @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Boolean> callback) { + mRoleControllerManager.isApplicationVisibleForRole(roleName, packageName, executor, + callback); + } + private static class OnRoleHoldersChangedListenerDelegate extends IOnRoleHoldersChangedListener.Stub { diff --git a/core/java/android/app/time/LocationTimeZoneManager.java b/core/java/android/app/time/LocationTimeZoneManager.java index d909c0cb1eba..71a800f2085e 100644 --- a/core/java/android/app/time/LocationTimeZoneManager.java +++ b/core/java/android/app/time/LocationTimeZoneManager.java @@ -40,17 +40,58 @@ public final class LocationTimeZoneManager { public static final String SHELL_COMMAND_SERVICE_NAME = "location_time_zone_manager"; /** - * Shell command that starts the service (after stop). + * A shell command that starts the service (after stop). */ public static final String SHELL_COMMAND_START = "start"; /** - * Shell command that stops the service. + * A shell command that stops the service. */ public static final String SHELL_COMMAND_STOP = "stop"; /** - * Shell command that sends test commands to a provider + * A shell command that can put providers into different modes. Takes effect next time the + * service is started. + */ + public static final String SHELL_COMMAND_SET_PROVIDER_MODE_OVERRIDE = + "set_provider_mode_override"; + + /** + * The default provider mode. + * For use with {@link #SHELL_COMMAND_SET_PROVIDER_MODE_OVERRIDE}. + */ + public static final String PROVIDER_MODE_OVERRIDE_NONE = "none"; + + /** + * The "simulated" provider mode. + * For use with {@link #SHELL_COMMAND_SET_PROVIDER_MODE_OVERRIDE}. + */ + public static final String PROVIDER_MODE_OVERRIDE_SIMULATED = "simulated"; + + /** + * The "disabled" provider mode (equivalent to there being no provider configured). + * For use with {@link #SHELL_COMMAND_SET_PROVIDER_MODE_OVERRIDE}. + */ + public static final String PROVIDER_MODE_OVERRIDE_DISABLED = "disabled"; + + /** + * A shell command that tells the service to record state information during tests. The next + * argument value is "true" or "false". + */ + public static final String SHELL_COMMAND_RECORD_PROVIDER_STATES = "record_provider_states"; + + /** + * A shell command that tells the service to dump its current state. + */ + public static final String SHELL_COMMAND_DUMP_STATE = "dump_state"; + + /** + * Option for {@link #SHELL_COMMAND_DUMP_STATE} that tells it to dump state as a binary proto. + */ + public static final String DUMP_STATE_OPTION_PROTO = "--proto"; + + /** + * A shell command that sends test commands to a provider */ public static final String SHELL_COMMAND_SEND_PROVIDER_TEST_COMMAND = "send_provider_test_command"; @@ -88,35 +129,6 @@ public final class LocationTimeZoneManager { */ public static final String SIMULATED_PROVIDER_TEST_COMMAND_UNCERTAIN = "uncertain"; - private static final String SYSTEM_PROPERTY_KEY_PROVIDER_MODE_OVERRIDE_PREFIX = - "persist.sys.geotz."; - - /** - * The name of the system property that can be used to set the primary provider into test mode - * (value = {@link #SYSTEM_PROPERTY_VALUE_PROVIDER_MODE_SIMULATED}) or disabled (value = {@link - * #SYSTEM_PROPERTY_VALUE_PROVIDER_MODE_DISABLED}). - */ - public static final String SYSTEM_PROPERTY_KEY_PROVIDER_MODE_OVERRIDE_PRIMARY = - SYSTEM_PROPERTY_KEY_PROVIDER_MODE_OVERRIDE_PREFIX + PRIMARY_PROVIDER_NAME; - - /** - * The name of the system property that can be used to set the secondary provider into test mode - * (value = {@link #SYSTEM_PROPERTY_VALUE_PROVIDER_MODE_SIMULATED}) or disabled (value = {@link - * #SYSTEM_PROPERTY_VALUE_PROVIDER_MODE_DISABLED}). - */ - public static final String SYSTEM_PROPERTY_KEY_PROVIDER_MODE_OVERRIDE_SECONDARY = - SYSTEM_PROPERTY_KEY_PROVIDER_MODE_OVERRIDE_PREFIX + SECONDARY_PROVIDER_NAME; - - /** - * The value of the provider mode system property to put a provider into test mode. - */ - public static final String SYSTEM_PROPERTY_VALUE_PROVIDER_MODE_SIMULATED = "simulated"; - - /** - * The value of the provider mode system property to put a provider into disabled mode. - */ - public static final String SYSTEM_PROPERTY_VALUE_PROVIDER_MODE_DISABLED = "disabled"; - private LocationTimeZoneManager() { // No need to instantiate. } diff --git a/core/java/android/app/timezonedetector/TimeZoneDetector.java b/core/java/android/app/timezonedetector/TimeZoneDetector.java index e0b7ffed1b36..ee718b35d4c5 100644 --- a/core/java/android/app/timezonedetector/TimeZoneDetector.java +++ b/core/java/android/app/timezonedetector/TimeZoneDetector.java @@ -29,11 +29,69 @@ import android.content.Context; @SystemService(Context.TIME_ZONE_DETECTOR_SERVICE) public interface TimeZoneDetector { - /** @hide */ + /** + * The name of the service for shell commands. + * @hide + */ + String SHELL_COMMAND_SERVICE_NAME = "time_zone_detector"; + + /** + * A shell command that prints the current "auto time zone detection" global setting value. + * @hide + */ + String SHELL_COMMAND_IS_AUTO_DETECTION_ENABLED = "is_auto_detection_enabled"; + + /** + * A shell command that sets the current "auto time zone detection" global setting value. + * @hide + */ + String SHELL_COMMAND_SET_AUTO_DETECTION_ENABLED = "set_auto_detection_enabled"; + + /** + * A shell command that prints whether the geolocation-based time zone detection feature is + * supported on the device. + * @hide + */ + String SHELL_COMMAND_IS_GEO_DETECTION_SUPPORTED = "is_geo_detection_supported"; + + /** + * A shell command that prints the current user's "location enabled" setting. + * @hide + */ + String SHELL_COMMAND_IS_LOCATION_ENABLED = "is_location_enabled"; + + /** + * A shell command that prints the current user's "location-based time zone detection enabled" + * setting. + * @hide + */ + String SHELL_COMMAND_IS_GEO_DETECTION_ENABLED = "is_geo_detection_enabled"; + + /** + * A shell command that sets the current user's "location-based time zone detection enabled" + * setting. + * @hide + */ + String SHELL_COMMAND_SET_GEO_DETECTION_ENABLED = "set_geo_detection_enabled"; + + /** + * A shell command that injects a geolocation time zone suggestion (as if from the + * location_time_zone_manager). + * @hide + */ String SHELL_COMMAND_SUGGEST_GEO_LOCATION_TIME_ZONE = "suggest_geo_location_time_zone"; - /** @hide */ + + /** + * A shell command that injects a manual time zone suggestion (as if from the SettingsUI or + * similar). + * @hide + */ String SHELL_COMMAND_SUGGEST_MANUAL_TIME_ZONE = "suggest_manual_time_zone"; - /** @hide */ + + /** + * A shell command that injects a telephony time zone suggestion (as if from the phone app). + * @hide + */ String SHELL_COMMAND_SUGGEST_TELEPHONY_TIME_ZONE = "suggest_telephony_time_zone"; /** diff --git a/core/java/android/apphibernation/OWNERS b/core/java/android/apphibernation/OWNERS new file mode 100644 index 000000000000..587c719f5587 --- /dev/null +++ b/core/java/android/apphibernation/OWNERS @@ -0,0 +1,2 @@ +kevhan@google.com +rajekumar@google.com diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 29ffa0b70313..33f960732be9 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -4821,16 +4821,6 @@ public abstract class Context { public static final String ROLE_SERVICE = "role"; /** - * Official published name of the (internal) role controller service. - * - * @see #getSystemService(String) - * @see android.app.role.RoleControllerService - * - * @hide - */ - public static final String ROLE_CONTROLLER_SERVICE = "role_controller"; - - /** * Use with {@link #getSystemService(String)} to retrieve a * {@link android.hardware.camera2.CameraManager} for interacting with * camera devices. diff --git a/core/java/android/hardware/biometrics/OWNERS b/core/java/android/hardware/biometrics/OWNERS index 33527f824827..2065ffacca7c 100644 --- a/core/java/android/hardware/biometrics/OWNERS +++ b/core/java/android/hardware/biometrics/OWNERS @@ -1,3 +1,8 @@ # Bug component: 879035 +curtislb@google.com +ilyamaty@google.com jaggies@google.com +joshmccloskey@google.com +kchyn@google.com + diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java index a4e573876218..582570e63d54 100644 --- a/core/java/android/hardware/fingerprint/FingerprintManager.java +++ b/core/java/android/hardware/fingerprint/FingerprintManager.java @@ -568,11 +568,13 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing * @param cancel an object that can be used to cancel enrollment * @param userId the user to whom this fingerprint will belong to * @param callback an object to receive enrollment events + * @param shouldLogMetrics a flag that indicates if enrollment failure/success metrics + * should be logged. * @hide */ @RequiresPermission(MANAGE_FINGERPRINT) public void enroll(byte [] hardwareAuthToken, CancellationSignal cancel, int userId, - EnrollmentCallback callback) { + EnrollmentCallback callback, boolean shouldLogMetrics) { if (userId == UserHandle.USER_CURRENT) { userId = getCurrentUserId(); } @@ -593,7 +595,7 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing try { mEnrollmentCallback = callback; mService.enroll(mToken, hardwareAuthToken, userId, mServiceReceiver, - mContext.getOpPackageName()); + mContext.getOpPackageName(), shouldLogMetrics); } catch (RemoteException e) { Slog.w(TAG, "Remote exception in enroll: ", e); // Though this may not be a hardware issue, it will cause apps to give up or try diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl index 64abbea12de0..ac026c796b51 100644 --- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl +++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl @@ -77,7 +77,7 @@ interface IFingerprintService { // Start fingerprint enrollment void enroll(IBinder token, in byte [] hardwareAuthToken, int userId, IFingerprintServiceReceiver receiver, - String opPackageName); + String opPackageName, boolean shouldLogMetrics); // Cancel enrollment in progress void cancelEnrollment(IBinder token); diff --git a/core/java/android/hardware/fingerprint/OWNERS b/core/java/android/hardware/fingerprint/OWNERS index dcead40d482d..e55b8c564ddb 100644 --- a/core/java/android/hardware/fingerprint/OWNERS +++ b/core/java/android/hardware/fingerprint/OWNERS @@ -1,3 +1,8 @@ # Bug component: 114777 +curtislb@google.com +ilyamaty@google.com jaggies@google.com +joshmccloskey@google.com +kchyn@google.com + diff --git a/core/java/android/net/NetworkStatsHistory.java b/core/java/android/net/NetworkStatsHistory.java index bf25602041cf..f41306301d42 100644 --- a/core/java/android/net/NetworkStatsHistory.java +++ b/core/java/android/net/NetworkStatsHistory.java @@ -45,8 +45,8 @@ import com.android.internal.util.IndentingPrintWriter; import libcore.util.EmptyArray; import java.io.CharArrayWriter; -import java.io.DataInputStream; -import java.io.DataOutputStream; +import java.io.DataInput; +import java.io.DataOutput; import java.io.IOException; import java.io.PrintWriter; import java.net.ProtocolException; @@ -162,7 +162,7 @@ public class NetworkStatsHistory implements Parcelable { out.writeLong(totalBytes); } - public NetworkStatsHistory(DataInputStream in) throws IOException { + public NetworkStatsHistory(DataInput in) throws IOException { final int version = in.readInt(); switch (version) { case VERSION_INIT: { @@ -204,7 +204,7 @@ public class NetworkStatsHistory implements Parcelable { } } - public void writeToStream(DataOutputStream out) throws IOException { + public void writeToStream(DataOutput out) throws IOException { out.writeInt(VERSION_ADD_ACTIVE); out.writeLong(bucketDuration); writeVarLongArray(out, bucketStart, bucketCount); @@ -768,7 +768,7 @@ public class NetworkStatsHistory implements Parcelable { */ public static class DataStreamUtils { @Deprecated - public static long[] readFullLongArray(DataInputStream in) throws IOException { + public static long[] readFullLongArray(DataInput in) throws IOException { final int size = in.readInt(); if (size < 0) throw new ProtocolException("negative array size"); final long[] values = new long[size]; @@ -781,7 +781,7 @@ public class NetworkStatsHistory implements Parcelable { /** * Read variable-length {@link Long} using protobuf-style approach. */ - public static long readVarLong(DataInputStream in) throws IOException { + public static long readVarLong(DataInput in) throws IOException { int shift = 0; long result = 0; while (shift < 64) { @@ -797,7 +797,7 @@ public class NetworkStatsHistory implements Parcelable { /** * Write variable-length {@link Long} using protobuf-style approach. */ - public static void writeVarLong(DataOutputStream out, long value) throws IOException { + public static void writeVarLong(DataOutput out, long value) throws IOException { while (true) { if ((value & ~0x7FL) == 0) { out.writeByte((int) value); @@ -809,7 +809,7 @@ public class NetworkStatsHistory implements Parcelable { } } - public static long[] readVarLongArray(DataInputStream in) throws IOException { + public static long[] readVarLongArray(DataInput in) throws IOException { final int size = in.readInt(); if (size == -1) return null; if (size < 0) throw new ProtocolException("negative array size"); @@ -820,7 +820,7 @@ public class NetworkStatsHistory implements Parcelable { return values; } - public static void writeVarLongArray(DataOutputStream out, long[] values, int size) + public static void writeVarLongArray(DataOutput out, long[] values, int size) throws IOException { if (values == null) { out.writeInt(-1); diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java index c014ef682a24..0326b72ece7c 100644 --- a/core/java/android/os/FileUtils.java +++ b/core/java/android/os/FileUtils.java @@ -1463,8 +1463,7 @@ public final class FileUtils { Uri uri = MediaStore.scanFile(resolver, realFile); if (uri != null) { Bundle opts = new Bundle(); - // TODO(b/158465539): Use API constant - opts.putBoolean("android.provider.extra.ACCEPT_ORIGINAL_MEDIA_FORMAT", true); + opts.putBoolean(MediaStore.EXTRA_ACCEPT_ORIGINAL_MEDIA_FORMAT, true); AssetFileDescriptor afd = resolver.openTypedAssetFileDescriptor(uri, "*/*", opts); Log.i(TAG, "Changed to modern format dataSource for: " + realFile); return afd.getFileDescriptor(); diff --git a/core/java/android/service/notification/NotificationListenerFilter.java b/core/java/android/service/notification/NotificationListenerFilter.java index c945c2d64519..6fdfaabb009b 100644 --- a/core/java/android/service/notification/NotificationListenerFilter.java +++ b/core/java/android/service/notification/NotificationListenerFilter.java @@ -17,6 +17,7 @@ package android.service.notification; import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_ALERTING; import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_CONVERSATIONS; +import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_ONGOING; import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_SILENT; import android.os.Parcel; @@ -35,7 +36,8 @@ public class NotificationListenerFilter implements Parcelable { public NotificationListenerFilter() { mAllowedNotificationTypes = FLAG_FILTER_TYPE_CONVERSATIONS | FLAG_FILTER_TYPE_ALERTING - | FLAG_FILTER_TYPE_SILENT; + | FLAG_FILTER_TYPE_SILENT + | FLAG_FILTER_TYPE_ONGOING; mDisallowedPackages = new ArraySet<>(); } diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java index ccde0bcf71c3..f79b59fe5432 100644 --- a/core/java/android/service/notification/NotificationListenerService.java +++ b/core/java/android/service/notification/NotificationListenerService.java @@ -242,6 +242,16 @@ public abstract class NotificationListenerService extends Service { public @interface NotificationCancelReason{}; /** + * @hide + */ + @IntDef(flag = true, prefix = { "FLAG_FILTER_TYPE_" }, value = { + FLAG_FILTER_TYPE_CONVERSATIONS, + FLAG_FILTER_TYPE_ALERTING, + FLAG_FILTER_TYPE_SILENT, + FLAG_FILTER_TYPE_ONGOING + }) + public @interface NotificationFilterTypes {} + /** * A flag value indicating that this notification listener can see conversation type * notifications. * @hide @@ -257,6 +267,12 @@ public abstract class NotificationListenerService extends Service { * @hide */ public static final int FLAG_FILTER_TYPE_SILENT = 4; + /** + * A flag value indicating that this notification listener can see important + * ( > {@link NotificationManager#IMPORTANCE_MIN}) ongoing type notifications. + * @hide + */ + public static final int FLAG_FILTER_TYPE_ONGOING = 8; /** * The full trim of the StatusBarNotification including all its features. diff --git a/core/java/android/service/voice/AlwaysOnHotwordDetector.java b/core/java/android/service/voice/AlwaysOnHotwordDetector.java index 46a5caf176b7..e934fa483ebe 100644 --- a/core/java/android/service/voice/AlwaysOnHotwordDetector.java +++ b/core/java/android/service/voice/AlwaysOnHotwordDetector.java @@ -22,9 +22,9 @@ import static android.Manifest.permission.RECORD_AUDIO; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; -import android.app.ActivityThread; import android.annotation.RequiresPermission; import android.annotation.SystemApi; +import android.app.ActivityThread; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.content.Intent; @@ -41,6 +41,7 @@ import android.media.AudioFormat; import android.media.permission.Identity; import android.os.AsyncTask; import android.os.Binder; +import android.os.Build; import android.os.Handler; import android.os.IBinder; import android.os.Message; @@ -78,6 +79,7 @@ public class AlwaysOnHotwordDetector { * No further interaction should be performed with the detector that returns this availability. */ public static final int STATE_HARDWARE_UNAVAILABLE = -2; + /** * Indicates that recognition for the given keyphrase is not supported. * No further interaction should be performed with the detector that returns this availability. @@ -88,11 +90,13 @@ public class AlwaysOnHotwordDetector { */ @Deprecated public static final int STATE_KEYPHRASE_UNSUPPORTED = -1; + /** * Indicates that the given keyphrase is not enrolled. * The caller may choose to begin an enrollment flow for the keyphrase. */ public static final int STATE_KEYPHRASE_UNENROLLED = 1; + /** * Indicates that the given keyphrase is currently enrolled and it's possible to start * recognition for it. @@ -100,6 +104,14 @@ public class AlwaysOnHotwordDetector { public static final int STATE_KEYPHRASE_ENROLLED = 2; /** + * Indicates that the availability state of the active keyphrase can't be known due to an error. + * + * <p>NOTE: No further interaction should be performed with the detector that returns this + * state, it would be better to create {@link AlwaysOnHotwordDetector} again. + */ + public static final int STATE_ERROR = 3; + + /** * Indicates that the detector isn't ready currently. */ private static final int STATE_NOT_READY = 0; @@ -122,11 +134,13 @@ public class AlwaysOnHotwordDetector { * @hide */ public static final int RECOGNITION_FLAG_NONE = 0; + /** * Recognition flag for {@link #startRecognition(int)} that indicates * whether the trigger audio for hotword needs to be captured. */ public static final int RECOGNITION_FLAG_CAPTURE_TRIGGER_AUDIO = 0x1; + /** * Recognition flag for {@link #startRecognition(int)} that indicates * whether the recognition should keep going on even after the keyphrase triggers. @@ -174,6 +188,7 @@ public class AlwaysOnHotwordDetector { */ public static final int RECOGNITION_MODE_VOICE_TRIGGER = SoundTrigger.RECOGNITION_MODE_VOICE_TRIGGER; + /** * User identification performed with the keyphrase recognition. * Returned by {@link #getSupportedRecognitionModes()} @@ -249,6 +264,7 @@ public class AlwaysOnHotwordDetector { private final Object mLock = new Object(); private final Handler mHandler; private final IBinder mBinder = new Binder(); + private final int mTargetSdkVersion; private int mAvailability = STATE_NOT_READY; @@ -401,8 +417,10 @@ public class AlwaysOnHotwordDetector { * @see AlwaysOnHotwordDetector#STATE_HARDWARE_UNAVAILABLE * @see AlwaysOnHotwordDetector#STATE_KEYPHRASE_UNENROLLED * @see AlwaysOnHotwordDetector#STATE_KEYPHRASE_ENROLLED + * @see AlwaysOnHotwordDetector#STATE_ERROR */ public abstract void onAvailabilityChanged(int status); + /** * Called when the keyphrase is spoken. * This implicitly stops listening for the keyphrase once it's detected. @@ -414,16 +432,19 @@ public class AlwaysOnHotwordDetector { * {@link AlwaysOnHotwordDetector#startRecognition(int)}. */ public abstract void onDetected(@NonNull EventPayload eventPayload); + /** * Called when the detection fails due to an error. */ public abstract void onError(); + /** * Called when the recognition is paused temporarily for some reason. * This is an informational callback, and the clients shouldn't be doing anything here * except showing an indication on their UI if they have to. */ public abstract void onRecognitionPaused(); + /** * Called when the recognition is resumed after it was temporarily paused. * This is an informational callback, and the clients shouldn't be doing anything here @@ -437,11 +458,12 @@ public class AlwaysOnHotwordDetector { * @param locale The java locale for the detector. * @param callback A non-null Callback for receiving the recognition events. * @param modelManagementService A service that allows management of sound models. + * @param targetSdkVersion The target SDK version. * @hide */ public AlwaysOnHotwordDetector(String text, Locale locale, Callback callback, KeyphraseEnrollmentInfo keyphraseEnrollmentInfo, - IVoiceInteractionManagerService modelManagementService) { + IVoiceInteractionManagerService modelManagementService, int targetSdkVersion) { mText = text; mLocale = locale; mKeyphraseEnrollmentInfo = keyphraseEnrollmentInfo; @@ -449,6 +471,7 @@ public class AlwaysOnHotwordDetector { mHandler = new MyHandler(); mInternalCallback = new SoundTriggerListener(mHandler); mModelManagementService = modelManagementService; + mTargetSdkVersion = targetSdkVersion; try { Identity identity = new Identity(); identity.packageName = ActivityThread.currentOpPackageName(); @@ -469,7 +492,7 @@ public class AlwaysOnHotwordDetector { * @throws UnsupportedOperationException if the keyphrase itself isn't supported. * Callers should only call this method after a supported state callback on * {@link Callback#onAvailabilityChanged(int)} to avoid this exception. - * @throws IllegalStateException if the detector is in an invalid state. + * @throws IllegalStateException if the detector is in an invalid or error state. * This may happen if another detector has been instantiated or the * {@link VoiceInteractionService} hosting this detector has been shut down. */ @@ -481,9 +504,9 @@ public class AlwaysOnHotwordDetector { } private int getSupportedRecognitionModesLocked() { - if (mAvailability == STATE_INVALID) { + if (mAvailability == STATE_INVALID || mAvailability == STATE_ERROR) { throw new IllegalStateException( - "getSupportedRecognitionModes called on an invalid detector"); + "getSupportedRecognitionModes called on an invalid detector or error state"); } // This method only makes sense if we can actually support a recognition. @@ -541,7 +564,7 @@ public class AlwaysOnHotwordDetector { * @throws UnsupportedOperationException if the recognition isn't supported. * Callers should only call this method after a supported state callback on * {@link Callback#onAvailabilityChanged(int)} to avoid this exception. - * @throws IllegalStateException if the detector is in an invalid state. + * @throws IllegalStateException if the detector is in an invalid or error state. * This may happen if another detector has been instantiated or the * {@link VoiceInteractionService} hosting this detector has been shut down. */ @@ -549,8 +572,9 @@ public class AlwaysOnHotwordDetector { public boolean startRecognition(@RecognitionFlags int recognitionFlags) { if (DBG) Slog.d(TAG, "startRecognition(" + recognitionFlags + ")"); synchronized (mLock) { - if (mAvailability == STATE_INVALID) { - throw new IllegalStateException("startRecognition called on an invalid detector"); + if (mAvailability == STATE_INVALID || mAvailability == STATE_ERROR) { + throw new IllegalStateException( + "startRecognition called on an invalid detector or error state"); } // Check if we can start/stop a recognition. @@ -572,7 +596,7 @@ public class AlwaysOnHotwordDetector { * @throws UnsupportedOperationException if the recognition isn't supported. * Callers should only call this method after a supported state callback on * {@link Callback#onAvailabilityChanged(int)} to avoid this exception. - * @throws IllegalStateException if the detector is in an invalid state. + * @throws IllegalStateException if the detector is in an invalid or error state. * This may happen if another detector has been instantiated or the * {@link VoiceInteractionService} hosting this detector has been shut down. */ @@ -580,8 +604,9 @@ public class AlwaysOnHotwordDetector { public boolean stopRecognition() { if (DBG) Slog.d(TAG, "stopRecognition()"); synchronized (mLock) { - if (mAvailability == STATE_INVALID) { - throw new IllegalStateException("stopRecognition called on an invalid detector"); + if (mAvailability == STATE_INVALID || mAvailability == STATE_ERROR) { + throw new IllegalStateException( + "stopRecognition called on an invalid detector or error state"); } // Check if we can start/stop a recognition. @@ -610,6 +635,9 @@ public class AlwaysOnHotwordDetector { * - {@link SoundTrigger#STATUS_BAD_VALUE} invalid input parameter * - {@link SoundTrigger#STATUS_INVALID_OPERATION} if the call is out of sequence or * if API is not supported by HAL + * @throws IllegalStateException if the detector is in an invalid or error state. + * This may happen if another detector has been instantiated or the + * {@link VoiceInteractionService} hosting this detector has been shut down. */ @RequiresPermission(allOf = {RECORD_AUDIO, CAPTURE_AUDIO_HOTWORD}) public int setParameter(@ModelParams int modelParam, int value) { @@ -618,8 +646,9 @@ public class AlwaysOnHotwordDetector { } synchronized (mLock) { - if (mAvailability == STATE_INVALID) { - throw new IllegalStateException("setParameter called on an invalid detector"); + if (mAvailability == STATE_INVALID || mAvailability == STATE_ERROR) { + throw new IllegalStateException( + "setParameter called on an invalid detector or error state"); } return setParameterLocked(modelParam, value); @@ -638,6 +667,9 @@ public class AlwaysOnHotwordDetector { * * @param modelParam {@link ModelParams} * @return value of parameter + * @throws IllegalStateException if the detector is in an invalid or error state. + * This may happen if another detector has been instantiated or the + * {@link VoiceInteractionService} hosting this detector has been shut down. */ @RequiresPermission(allOf = {RECORD_AUDIO, CAPTURE_AUDIO_HOTWORD}) public int getParameter(@ModelParams int modelParam) { @@ -646,8 +678,9 @@ public class AlwaysOnHotwordDetector { } synchronized (mLock) { - if (mAvailability == STATE_INVALID) { - throw new IllegalStateException("getParameter called on an invalid detector"); + if (mAvailability == STATE_INVALID || mAvailability == STATE_ERROR) { + throw new IllegalStateException( + "getParameter called on an invalid detector or error state"); } return getParameterLocked(modelParam); @@ -663,6 +696,9 @@ public class AlwaysOnHotwordDetector { * * @param modelParam {@link ModelParams} * @return supported range of parameter, null if not supported + * @throws IllegalStateException if the detector is in an invalid or error state. + * This may happen if another detector has been instantiated or the + * {@link VoiceInteractionService} hosting this detector has been shut down. */ @RequiresPermission(allOf = {RECORD_AUDIO, CAPTURE_AUDIO_HOTWORD}) @Nullable @@ -672,8 +708,9 @@ public class AlwaysOnHotwordDetector { } synchronized (mLock) { - if (mAvailability == STATE_INVALID) { - throw new IllegalStateException("queryParameter called on an invalid detector"); + if (mAvailability == STATE_INVALID || mAvailability == STATE_ERROR) { + throw new IllegalStateException( + "queryParameter called on an invalid detector or error state"); } return queryParameterLocked(modelParam); @@ -735,7 +772,7 @@ public class AlwaysOnHotwordDetector { * @throws UnsupportedOperationException if managing they keyphrase isn't supported. * Callers should only call this method after a supported state callback on * {@link Callback#onAvailabilityChanged(int)} to avoid this exception. - * @throws IllegalStateException if the detector is in an invalid state. + * @throws IllegalStateException if the detector is in an invalid or error state. * This may happen if another detector has been instantiated or the * {@link VoiceInteractionService} hosting this detector has been shut down. */ @@ -748,8 +785,9 @@ public class AlwaysOnHotwordDetector { } private Intent getManageIntentLocked(@KeyphraseEnrollmentInfo.ManageActions int action) { - if (mAvailability == STATE_INVALID) { - throw new IllegalStateException("getManageIntent called on an invalid detector"); + if (mAvailability == STATE_INVALID || mAvailability == STATE_ERROR) { + throw new IllegalStateException( + "getManageIntent called on an invalid detector or error state"); } // This method only makes sense if we can actually support a recognition. @@ -783,8 +821,10 @@ public class AlwaysOnHotwordDetector { void onSoundModelsChanged() { synchronized (mLock) { if (mAvailability == STATE_INVALID - || mAvailability == STATE_HARDWARE_UNAVAILABLE) { - Slog.w(TAG, "Received onSoundModelsChanged for an unsupported keyphrase/config"); + || mAvailability == STATE_HARDWARE_UNAVAILABLE + || mAvailability == STATE_ERROR) { + Slog.w(TAG, "Received onSoundModelsChanged for an unsupported keyphrase/config" + + " or in the error state"); return; } @@ -794,7 +834,16 @@ public class AlwaysOnHotwordDetector { // The availability change callback should ensure that the client starts recognition // again if needed. if (mAvailability == STATE_KEYPHRASE_ENROLLED) { - stopRecognitionLocked(); + try { + stopRecognitionLocked(); + } catch (SecurityException e) { + Slog.w(TAG, "Failed to Stop the recognition", e); + if (mTargetSdkVersion <= Build.VERSION_CODES.R) { + throw e; + } + updateAndNotifyStateChangedLocked(STATE_ERROR); + return; + } } // Execute a refresh availability task - which should then notify of a change. @@ -890,6 +939,15 @@ public class AlwaysOnHotwordDetector { } } + private void updateAndNotifyStateChangedLocked(int availability) { + if (DBG) { + Slog.d(TAG, "Hotword availability changed from " + mAvailability + + " -> " + availability); + } + mAvailability = availability; + notifyStateChangedLocked(); + } + private void notifyStateChangedLocked() { Message message = Message.obtain(mHandler, MSG_AVAILABILITY_CHANGED); message.arg1 = mAvailability; @@ -976,25 +1034,30 @@ public class AlwaysOnHotwordDetector { @Override public Void doInBackground(Void... params) { - int availability = internalGetInitialAvailability(); - - synchronized (mLock) { - if (availability == STATE_NOT_READY) { - internalUpdateEnrolledKeyphraseMetadata(); - if (mKeyphraseMetadata != null) { - availability = STATE_KEYPHRASE_ENROLLED; - } else { - availability = STATE_KEYPHRASE_UNENROLLED; + try { + int availability = internalGetInitialAvailability(); + + synchronized (mLock) { + if (availability == STATE_NOT_READY) { + internalUpdateEnrolledKeyphraseMetadata(); + if (mKeyphraseMetadata != null) { + availability = STATE_KEYPHRASE_ENROLLED; + } else { + availability = STATE_KEYPHRASE_UNENROLLED; + } } + updateAndNotifyStateChangedLocked(availability); } - - if (DBG) { - Slog.d(TAG, "Hotword availability changed from " + mAvailability - + " -> " + availability); + } catch (SecurityException e) { + Slog.w(TAG, "Failed to refresh availability", e); + if (mTargetSdkVersion <= Build.VERSION_CODES.R) { + throw e; + } + synchronized (mLock) { + updateAndNotifyStateChangedLocked(STATE_ERROR); } - mAvailability = availability; - notifyStateChangedLocked(); } + return null; } diff --git a/core/java/android/service/voice/VoiceInteractionService.java b/core/java/android/service/voice/VoiceInteractionService.java index fb03ed45113e..4aff695f1b47 100644 --- a/core/java/android/service/voice/VoiceInteractionService.java +++ b/core/java/android/service/voice/VoiceInteractionService.java @@ -325,7 +325,8 @@ public class VoiceInteractionService extends Service { // Allow only one concurrent recognition via the APIs. safelyShutdownHotwordDetector(); mHotwordDetector = new AlwaysOnHotwordDetector(keyphrase, locale, callback, - mKeyphraseEnrollmentInfo, mSystemService); + mKeyphraseEnrollmentInfo, mSystemService, + getApplicationContext().getApplicationInfo().targetSdkVersion); } return mHotwordDetector; } diff --git a/core/java/android/util/TypedValue.java b/core/java/android/util/TypedValue.java index 7f1ee302903b..19de396c4a4a 100644 --- a/core/java/android/util/TypedValue.java +++ b/core/java/android/util/TypedValue.java @@ -17,8 +17,14 @@ package android.util; import android.annotation.AnyRes; +import android.annotation.FloatRange; +import android.annotation.IntDef; +import android.annotation.IntRange; import android.content.pm.ActivityInfo.Config; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + /** * Container for a dynamically typed data value. Primarily used with * {@link android.content.res.Resources} for holding resource values. @@ -95,6 +101,18 @@ public class TypedValue { * defined below. */ public static final int COMPLEX_UNIT_MASK = 0xf; + /** @hide **/ + @IntDef(prefix = "COMPLEX_UNIT_", value = { + COMPLEX_UNIT_PX, + COMPLEX_UNIT_DIP, + COMPLEX_UNIT_SP, + COMPLEX_UNIT_PT, + COMPLEX_UNIT_IN, + COMPLEX_UNIT_MM, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface ComplexDimensionUnit {} + /** {@link #TYPE_DIMENSION} complex unit: Value is raw pixels. */ public static final int COMPLEX_UNIT_PX = 0; /** {@link #TYPE_DIMENSION} complex unit: Value is Device Independent @@ -381,7 +399,7 @@ public class TypedValue { * @return The complex floating point value multiplied by the appropriate * metrics depending on its unit. */ - public static float applyDimension(int unit, float value, + public static float applyDimension(@ComplexDimensionUnit int unit, float value, DisplayMetrics metrics) { switch (unit) { @@ -417,6 +435,130 @@ public class TypedValue { } /** + * Construct a complex data integer. This validates the radix and the magnitude of the + * mantissa, and sets the {@link TypedValue#COMPLEX_MANTISSA_MASK} and + * {@link TypedValue#COMPLEX_RADIX_MASK} components as provided. The units are not set. + ** + * @param mantissa an integer representing the mantissa. + * @param radix a radix option, e.g. {@link TypedValue#COMPLEX_RADIX_23p0}. + * @return A complex data integer representing the value. + * @hide + */ + private static int createComplex(@IntRange(from = -0x800000, to = 0x7FFFFF) int mantissa, + int radix) { + if (mantissa < -0x800000 || mantissa >= 0x800000) { + throw new IllegalArgumentException("Magnitude of mantissa is too large: " + mantissa); + } + if (radix < TypedValue.COMPLEX_RADIX_23p0 || radix > TypedValue.COMPLEX_RADIX_0p23) { + throw new IllegalArgumentException("Invalid radix: " + radix); + } + return ((mantissa & TypedValue.COMPLEX_MANTISSA_MASK) << TypedValue.COMPLEX_MANTISSA_SHIFT) + | (radix << TypedValue.COMPLEX_RADIX_SHIFT); + } + + /** + * Convert a base value to a complex data integer. This sets the {@link + * TypedValue#COMPLEX_MANTISSA_MASK} and {@link TypedValue#COMPLEX_RADIX_MASK} fields of the + * data to create a floating point representation of the given value. The units are not set. + * + * <p>This is the inverse of {@link TypedValue#complexToFloat(int)}. + * + * @param value An integer value. + * @return A complex data integer representing the value. + * @hide + */ + public static int intToComplex(int value) { + if (value < -0x800000 || value >= 0x800000) { + throw new IllegalArgumentException("Magnitude of the value is too large: " + value); + } + return createComplex(value, TypedValue.COMPLEX_RADIX_23p0); + } + + /** + * Convert a base value to a complex data integer. This sets the {@link + * TypedValue#COMPLEX_MANTISSA_MASK} and {@link TypedValue#COMPLEX_RADIX_MASK} fields of the + * data to create a floating point representation of the given value. The units are not set. + * + * <p>This is the inverse of {@link TypedValue#complexToFloat(int)}. + * + * @param value A floating point value. + * @return A complex data integer representing the value. + * @hide + */ + public static int floatToComplex(@FloatRange(from = -0x800000, to = 0x7FFFFF) float value) { + // validate that the magnitude fits in this representation + if (value < (float) -0x800000 - .5f || value >= (float) 0x800000 - .5f) { + throw new IllegalArgumentException("Magnitude of the value is too large: " + value); + } + try { + // If there's no fraction, use integer representation, as that's clearer + if (value == (float) (int) value) { + return createComplex((int) value, TypedValue.COMPLEX_RADIX_23p0); + } + float absValue = Math.abs(value); + // If the magnitude is 0, we don't need any magnitude digits + if (absValue < 1f) { + return createComplex(Math.round(value * (1 << 23)), TypedValue.COMPLEX_RADIX_0p23); + } + // If the magnitude is less than 2^8, use 8 magnitude digits + if (absValue < (float) (1 << 8)) { + return createComplex(Math.round(value * (1 << 15)), TypedValue.COMPLEX_RADIX_8p15); + } + // If the magnitude is less than 2^16, use 16 magnitude digits + if (absValue < (float) (1 << 16)) { + return createComplex(Math.round(value * (1 << 7)), TypedValue.COMPLEX_RADIX_16p7); + } + // The magnitude requires all 23 digits + return createComplex(Math.round(value), TypedValue.COMPLEX_RADIX_23p0); + } catch (IllegalArgumentException ex) { + // Wrap exception so as to include the value argument in the message. + throw new IllegalArgumentException("Unable to convert value to complex: " + value, ex); + } + } + + /** + * <p>Creates a complex data integer that stores a dimension value and units. + * + * <p>The resulting value can be passed to e.g. + * {@link TypedValue#complexToDimensionPixelOffset(int, DisplayMetrics)} to calculate the pixel + * value for the dimension. + * + * @param value the value of the dimension + * @param units the units of the dimension, e.g. {@link TypedValue#COMPLEX_UNIT_DIP} + * @return A complex data integer representing the value and units of the dimension. + * @hide + */ + public static int createComplexDimension( + @IntRange(from = -0x800000, to = 0x7FFFFF) int value, + @ComplexDimensionUnit int units) { + if (units < TypedValue.COMPLEX_UNIT_PX || units > TypedValue.COMPLEX_UNIT_MM) { + throw new IllegalArgumentException("Must be a valid COMPLEX_UNIT_*: " + units); + } + return intToComplex(value) | units; + } + + /** + * <p>Creates a complex data integer that stores a dimension value and units. + * + * <p>The resulting value can be passed to e.g. + * {@link TypedValue#complexToDimensionPixelOffset(int, DisplayMetrics)} to calculate the pixel + * value for the dimension. + * + * @param value the value of the dimension + * @param units the units of the dimension, e.g. {@link TypedValue#COMPLEX_UNIT_DIP} + * @return A complex data integer representing the value and units of the dimension. + * @hide + */ + public static int createComplexDimension( + @FloatRange(from = -0x800000, to = 0x7FFFFF) float value, + @ComplexDimensionUnit int units) { + if (units < TypedValue.COMPLEX_UNIT_PX || units > TypedValue.COMPLEX_UNIT_MM) { + throw new IllegalArgumentException("Must be a valid COMPLEX_UNIT_*: " + units); + } + return floatToComplex(value) | units; + } + + /** * Converts a complex data value holding a fraction to its final floating * point value. The given <var>data</var> must be structured as a * {@link #TYPE_FRACTION}. diff --git a/core/java/android/view/NotificationHeaderView.java b/core/java/android/view/NotificationHeaderView.java index 673ed0d8b95d..5ce4c50cc85c 100644 --- a/core/java/android/view/NotificationHeaderView.java +++ b/core/java/android/view/NotificationHeaderView.java @@ -156,14 +156,25 @@ public class NotificationHeaderView extends FrameLayout { * Sets the extra margin at the end of the top line of left-aligned text + icons. * This value will have the margin required to accommodate the expand button added to it. * - * @param extraMarginEnd extra margin + * @param extraMarginEnd extra margin in px */ - @RemotableViewMethod public void setTopLineExtraMarginEnd(int extraMarginEnd) { mTopLineView.setHeaderTextMarginEnd(extraMarginEnd + mHeadingEndMargin); } /** + * Sets the extra margin at the end of the top line of left-aligned text + icons. + * This value will have the margin required to accommodate the expand button added to it. + * + * @param extraMarginEndDp extra margin in dp + */ + @RemotableViewMethod + public void setTopLineExtraMarginEndDp(float extraMarginEndDp) { + setTopLineExtraMarginEnd( + (int) (extraMarginEndDp * getResources().getDisplayMetrics().density)); + } + + /** * Get the current margin end value for the header text. * Add this to {@link #getTopLineBaseMarginEnd()} to get the total margin of the top line. * diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index 6d88d637dc24..482f2f07499d 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -386,6 +386,16 @@ public interface WindowManager extends ViewManager { * @hide */ int TRANSIT_KEYGUARD_UNOCCLUDE = 9; + /** + * The first slot for custom transition types. Callers (like Shell) can make use of custom + * transition types for dealing with special cases. These types are effectively ignored by + * Core and will just be passed along as part of TransitionInfo objects. An example is + * split-screen using a custom type for it's snap-to-dismiss action. By using a custom type, + * Shell can properly dispatch the results of that transition to the split-screen + * implementation. + * @hide + */ + int TRANSIT_FIRST_CUSTOM = 10; /** * @hide @@ -401,6 +411,7 @@ public interface WindowManager extends ViewManager { TRANSIT_KEYGUARD_GOING_AWAY, TRANSIT_KEYGUARD_OCCLUDE, TRANSIT_KEYGUARD_UNOCCLUDE, + TRANSIT_FIRST_CUSTOM }) @Retention(RetentionPolicy.SOURCE) @interface TransitionType {} diff --git a/core/java/android/view/inputmethod/BaseInputConnection.java b/core/java/android/view/inputmethod/BaseInputConnection.java index f057c1239e52..415b3a766d16 100644 --- a/core/java/android/view/inputmethod/BaseInputConnection.java +++ b/core/java/android/view/inputmethod/BaseInputConnection.java @@ -163,11 +163,13 @@ public class BaseInputConnection implements InputConnection { } /** - * Default implementation calls {@link #finishComposingText()}. + * Default implementation calls {@link #finishComposingText()} and + * {@code setImeTemporarilyConsumesInput(false)}. */ @CallSuper public void closeConnection() { finishComposingText(); + setImeTemporarilyConsumesInput(false); } /** diff --git a/core/java/android/view/inputmethod/EditorInfo.java b/core/java/android/view/inputmethod/EditorInfo.java index 1cf25a77fa38..2df75f6570db 100644 --- a/core/java/android/view/inputmethod/EditorInfo.java +++ b/core/java/android/view/inputmethod/EditorInfo.java @@ -25,6 +25,7 @@ import static android.view.inputmethod.EditorInfoProto.PRIVATE_IME_OPTIONS; import static android.view.inputmethod.EditorInfoProto.TARGET_INPUT_METHOD_USER_ID; import android.annotation.IntDef; +import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; @@ -36,7 +37,6 @@ import android.os.Parcel; import android.os.Parcelable; import android.os.UserHandle; import android.text.InputType; -import android.text.ParcelableSpan; import android.text.TextUtils; import android.util.Printer; import android.util.proto.ProtoOutputStream; @@ -44,6 +44,7 @@ import android.view.View; import android.view.autofill.AutofillId; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.Preconditions; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -563,8 +564,9 @@ public class EditorInfo implements InputType, Parcelable { @VisibleForTesting static final int MAX_INITIAL_SELECTION_LENGTH = MEMORY_EFFICIENT_TEXT_LENGTH / 2; - @NonNull - private InitialSurroundingText mInitialSurroundingText = new InitialSurroundingText(); + @Nullable + private SurroundingText mInitialSurroundingText = null; + /** * Editors may use this method to provide initial input text to IMEs. As the surrounding text @@ -607,6 +609,12 @@ public class EditorInfo implements InputType, Parcelable { public void setInitialSurroundingSubText(@NonNull CharSequence subText, int subTextStart) { Objects.requireNonNull(subText); + // For privacy protection reason, we don't carry password inputs to IMEs. + if (isPasswordInputType(inputType)) { + mInitialSurroundingText = null; + return; + } + // Swap selection start and end if necessary. final int subTextSelStart = initialSelStart > initialSelEnd ? initialSelEnd - subTextStart : initialSelStart - subTextStart; @@ -616,23 +624,17 @@ public class EditorInfo implements InputType, Parcelable { final int subTextLength = subText.length(); // Unknown or invalid selection. if (subTextStart < 0 || subTextSelStart < 0 || subTextSelEnd > subTextLength) { - mInitialSurroundingText = new InitialSurroundingText(); - return; - } - - // For privacy protection reason, we don't carry password inputs to IMEs. - if (isPasswordInputType(inputType)) { - mInitialSurroundingText = new InitialSurroundingText(); + mInitialSurroundingText = null; return; } if (subTextLength <= MEMORY_EFFICIENT_TEXT_LENGTH) { - mInitialSurroundingText = new InitialSurroundingText(subText, subTextSelStart, - subTextSelEnd); + mInitialSurroundingText = new SurroundingText(subText, subTextSelStart, + subTextSelEnd, subTextStart); return; } - trimLongSurroundingText(subText, subTextSelStart, subTextSelEnd); + trimLongSurroundingText(subText, subTextSelStart, subTextSelEnd, subTextStart); } /** @@ -651,8 +653,10 @@ public class EditorInfo implements InputType, Parcelable { * @param subText The long text that needs to be trimmed. * @param selStart The text offset of the start of the selection. * @param selEnd The text offset of the end of the selection + * @param subTextStart The position that the input text got trimmed. */ - private void trimLongSurroundingText(CharSequence subText, int selStart, int selEnd) { + private void trimLongSurroundingText(CharSequence subText, int selStart, int selEnd, + int subTextStart) { final int sourceSelLength = selEnd - selStart; // When the selected text is too long, drop it. final int newSelLength = (sourceSelLength > MAX_INITIAL_SELECTION_LENGTH) @@ -702,10 +706,13 @@ public class EditorInfo implements InputType, Parcelable { // obj. newBeforeCursorHead = 0; final int newSelHead = newBeforeCursorHead + newBeforeCursorLength; - mInitialSurroundingText = new InitialSurroundingText( - newInitialSurroundingText, newSelHead, newSelHead + newSelLength); + final int newOffset = subTextStart + selStart - newSelHead; + mInitialSurroundingText = new SurroundingText( + newInitialSurroundingText, newSelHead, newSelHead + newSelLength, + newOffset); } + /** * Get <var>length</var> characters of text before the current cursor position. May be * {@code null} when the protocol is not supported. @@ -720,7 +727,17 @@ public class EditorInfo implements InputType, Parcelable { */ @Nullable public CharSequence getInitialTextBeforeCursor(int length, int flags) { - return mInitialSurroundingText.getInitialTextBeforeCursor(length, flags); + if (mInitialSurroundingText == null) { + return null; + } + + int selStart = Math.min(mInitialSurroundingText.getSelectionStart(), + mInitialSurroundingText.getSelectionEnd()); + int n = Math.min(length, selStart); + return ((flags & InputConnection.GET_TEXT_WITH_STYLES) != 0) + ? mInitialSurroundingText.getText().subSequence(selStart - n, selStart) + : TextUtils.substring(mInitialSurroundingText.getText(), selStart - n, + selStart); } /** @@ -735,6 +752,10 @@ public class EditorInfo implements InputType, Parcelable { */ @Nullable public CharSequence getInitialSelectedText(int flags) { + if (mInitialSurroundingText == null) { + return null; + } + // Swap selection start and end if necessary. final int correctedTextSelStart = initialSelStart > initialSelEnd ? initialSelEnd : initialSelStart; @@ -742,11 +763,21 @@ public class EditorInfo implements InputType, Parcelable { ? initialSelStart : initialSelEnd; final int sourceSelLength = correctedTextSelEnd - correctedTextSelStart; - if (initialSelStart < 0 || initialSelEnd < 0 - || mInitialSurroundingText.getSelectionLength() != sourceSelLength) { + int selStart = mInitialSurroundingText.getSelectionStart(); + int selEnd = mInitialSurroundingText.getSelectionEnd(); + if (selStart > selEnd) { + int tmp = selStart; + selStart = selEnd; + selEnd = tmp; + } + final int selLength = selEnd - selStart; + if (initialSelStart < 0 || initialSelEnd < 0 || selLength != sourceSelLength) { return null; } - return mInitialSurroundingText.getInitialSelectedText(flags); + + return ((flags & InputConnection.GET_TEXT_WITH_STYLES) != 0) + ? mInitialSurroundingText.getText().subSequence(selStart, selEnd) + : TextUtils.substring(mInitialSurroundingText.getText(), selStart, selEnd); } /** @@ -763,7 +794,79 @@ public class EditorInfo implements InputType, Parcelable { */ @Nullable public CharSequence getInitialTextAfterCursor(int length, int flags) { - return mInitialSurroundingText.getInitialTextAfterCursor(length, flags); + if (mInitialSurroundingText == null) { + return null; + } + + int surroundingTextLength = mInitialSurroundingText.getText().length(); + int selEnd = Math.max(mInitialSurroundingText.getSelectionStart(), + mInitialSurroundingText.getSelectionEnd()); + int n = Math.min(length, surroundingTextLength - selEnd); + return ((flags & InputConnection.GET_TEXT_WITH_STYLES) != 0) + ? mInitialSurroundingText.getText().subSequence(selEnd, selEnd + n) + : TextUtils.substring(mInitialSurroundingText.getText(), selEnd, selEnd + n); + } + + /** + * Gets the surrounding text around the current cursor, with <var>beforeLength</var> characters + * of text before the cursor (start of the selection), <var>afterLength</var> characters of text + * after the cursor (end of the selection), and all of the selected text. + * + * <p>The initial surrounding text for return could be trimmed if oversize. Fundamental trimming + * rules are:</p> + * <ul> + * <li>The text before the cursor is the most important information to IMEs.</li> + * <li>The text after the cursor is the second important information to IMEs.</li> + * <li>The selected text is the least important information but it shall NEVER be truncated. + * When it is too long, just drop it.</li> + * </ul> + * + * <p>For example, the subText can be viewed as TextBeforeCursor + Selection + TextAfterCursor. + * The result could be:</p> + * <ol> + * <li>(maybeTrimmedAtHead)TextBeforeCursor + Selection + * + TextAfterCursor(maybeTrimmedAtTail)</li> + * <li>(maybeTrimmedAtHead)TextBeforeCursor + TextAfterCursor(maybeTrimmedAtTail)</li> + * </ol> + * + * @param beforeLength The expected length of the text before the cursor. + * @param afterLength The expected length of the text after the cursor. + * @param flags Supplies additional options controlling how the text is returned. May be either + * {@code 0} or {@link InputConnection#GET_TEXT_WITH_STYLES}. + * @return an {@link android.view.inputmethod.SurroundingText} object describing the surrounding + * text and state of selection, or {@code null} if the editor or system could not support this + * protocol. + * @throws IllegalArgumentException if {@code beforeLength} or {@code afterLength} is negative. + */ + @Nullable + public SurroundingText getInitialSurroundingText( + @IntRange(from = 0) int beforeLength, @IntRange(from = 0) int afterLength, + @InputConnection.GetTextType int flags) { + Preconditions.checkArgumentNonnegative(beforeLength); + Preconditions.checkArgumentNonnegative(afterLength); + + if (mInitialSurroundingText == null) { + return null; + } + + int length = mInitialSurroundingText.getText().length(); + int selStart = mInitialSurroundingText.getSelectionStart(); + int selEnd = mInitialSurroundingText.getSelectionEnd(); + if (selStart > selEnd) { + int tmp = selStart; + selStart = selEnd; + selEnd = tmp; + } + + int before = Math.min(beforeLength, selStart); + int after = Math.min(selEnd + afterLength, length); + int offset = selStart - before; + CharSequence newText = ((flags & InputConnection.GET_TEXT_WITH_STYLES) != 0) + ? mInitialSurroundingText.getText().subSequence(offset, after) + : TextUtils.substring(mInitialSurroundingText.getText(), offset, after); + int newSelEnd = Math.min(selEnd - offset, length); + return new SurroundingText(newText, before, newSelEnd, + mInitialSurroundingText.getOffset() + offset); } private static boolean isCutOnSurrogate(CharSequence sourceText, int cutPosition, @@ -893,7 +996,10 @@ public class EditorInfo implements InputType, Parcelable { dest.writeInt(fieldId); dest.writeString(fieldName); dest.writeBundle(extras); - mInitialSurroundingText.writeToParcel(dest, flags); + dest.writeBoolean(mInitialSurroundingText != null); + if (mInitialSurroundingText != null) { + mInitialSurroundingText.writeToParcel(dest, flags); + } if (hintLocales != null) { hintLocales.writeToParcel(dest, flags); } else { @@ -925,9 +1031,11 @@ public class EditorInfo implements InputType, Parcelable { res.fieldId = source.readInt(); res.fieldName = source.readString(); res.extras = source.readBundle(); - InitialSurroundingText initialSurroundingText = - InitialSurroundingText.CREATOR.createFromParcel(source); - res.mInitialSurroundingText = initialSurroundingText; + boolean hasInitialSurroundingText = source.readBoolean(); + if (hasInitialSurroundingText) { + res.mInitialSurroundingText = + SurroundingText.CREATOR.createFromParcel(source); + } LocaleList hintLocales = LocaleList.CREATOR.createFromParcel(source); res.hintLocales = hintLocales.isEmpty() ? null : hintLocales; res.contentMimeTypes = source.readStringArray(); @@ -943,120 +1051,4 @@ public class EditorInfo implements InputType, Parcelable { public int describeContents() { return 0; } - - static final class InitialSurroundingText implements Parcelable { - @Nullable final CharSequence mSurroundingText; - final int mSelectionHead; - final int mSelectionEnd; - - InitialSurroundingText() { - mSurroundingText = null; - mSelectionHead = 0; - mSelectionEnd = 0; - } - - InitialSurroundingText(@Nullable CharSequence surroundingText, int selectionHead, - int selectionEnd) { - // Copy the original text (without NoCopySpan) in case the original text is updated - // later. - mSurroundingText = copyWithParcelableSpans(surroundingText); - mSelectionHead = selectionHead; - mSelectionEnd = selectionEnd; - } - - @Nullable - private CharSequence getInitialTextBeforeCursor(int n, int flags) { - if (mSurroundingText == null) { - return null; - } - - final int length = Math.min(n, mSelectionHead); - return ((flags & InputConnection.GET_TEXT_WITH_STYLES) != 0) - ? mSurroundingText.subSequence(mSelectionHead - length, mSelectionHead) - : TextUtils.substring(mSurroundingText, mSelectionHead - length, - mSelectionHead); - } - - @Nullable - private CharSequence getInitialSelectedText(int flags) { - if (mSurroundingText == null) { - return null; - } - - return ((flags & InputConnection.GET_TEXT_WITH_STYLES) != 0) - ? mSurroundingText.subSequence(mSelectionHead, mSelectionEnd) - : TextUtils.substring(mSurroundingText, mSelectionHead, mSelectionEnd); - } - - @Nullable - private CharSequence getInitialTextAfterCursor(int n, int flags) { - if (mSurroundingText == null) { - return null; - } - - final int length = Math.min(n, mSurroundingText.length() - mSelectionEnd); - return ((flags & InputConnection.GET_TEXT_WITH_STYLES) != 0) - ? mSurroundingText.subSequence(mSelectionEnd, mSelectionEnd + length) - : TextUtils.substring(mSurroundingText, mSelectionEnd, mSelectionEnd + length); - } - - private int getSelectionLength() { - return mSelectionEnd - mSelectionHead; - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - TextUtils.writeToParcel(mSurroundingText, dest, flags); - dest.writeInt(mSelectionHead); - dest.writeInt(mSelectionEnd); - } - - public static final @android.annotation.NonNull Parcelable.Creator<InitialSurroundingText> - CREATOR = new Parcelable.Creator<InitialSurroundingText>() { - @Override - public InitialSurroundingText createFromParcel(Parcel source) { - final CharSequence initialText = - TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source); - final int selectionHead = source.readInt(); - final int selectionEnd = source.readInt(); - - return new InitialSurroundingText(initialText, selectionHead, selectionEnd); - } - - @Override - public InitialSurroundingText[] newArray(int size) { - return new InitialSurroundingText[size]; - } - }; - - /** - * Create a copy of the given {@link CharSequence} object, with completely copy - * {@link ParcelableSpan} instances. - * - * @param source the original {@link CharSequence} to be copied. - * @return the copied {@link CharSequence}. {@code null} if {@code source} is {@code null}. - */ - @Nullable - private static CharSequence copyWithParcelableSpans(@Nullable CharSequence source) { - if (source == null) { - return null; - } - Parcel parcel = null; - try { - parcel = Parcel.obtain(); - TextUtils.writeToParcel(source, parcel, /* parcelableFlags= */ 0); - parcel.setDataPosition(0); - return TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel); - } finally { - if (parcel != null) { - parcel.recycle(); - } - } - } - } -} +}
\ No newline at end of file diff --git a/core/java/android/view/inputmethod/InputConnection.java b/core/java/android/view/inputmethod/InputConnection.java index 8c81143e4cdc..a76d46d1c00f 100644 --- a/core/java/android/view/inputmethod/InputConnection.java +++ b/core/java/android/view/inputmethod/InputConnection.java @@ -1002,4 +1002,22 @@ public interface InputConnection { */ boolean commitContent(@NonNull InputContentInfo inputContentInfo, int flags, @Nullable Bundle opts); + + /** + * Called by the input method to indicate that it temporarily consumes all input for itself, + * or no longer does so. + * + * <p>Editors should reflect that they are temporarily not receiving input by hiding the + * cursor if {@code imeTemporarilyConsumesInput} is {@code true}, and resume showing the + * cursor if it is {@code false}. + * + * @param imeTemporarilyConsumesInput {@code true} when the IME is temporarily consuming input + * and the cursor should be hidden, {@code false} when input to the editor resumes and the + * cursor should be shown again. + * @return {@code true} on success, {@code false} if the input connection is no longer valid, or + * the protocol is not supported. + */ + default boolean setImeTemporarilyConsumesInput(boolean imeTemporarilyConsumesInput) { + return false; + } } diff --git a/core/java/android/view/inputmethod/InputConnectionWrapper.java b/core/java/android/view/inputmethod/InputConnectionWrapper.java index ca853485d4f3..b29149fc1fa0 100644 --- a/core/java/android/view/inputmethod/InputConnectionWrapper.java +++ b/core/java/android/view/inputmethod/InputConnectionWrapper.java @@ -335,4 +335,13 @@ public class InputConnectionWrapper implements InputConnection { public boolean commitContent(InputContentInfo inputContentInfo, int flags, Bundle opts) { return mTarget.commitContent(inputContentInfo, flags, opts); } + + /** + * {@inheritDoc} + * @throws NullPointerException if the target is {@code null}. + */ + @Override + public boolean setImeTemporarilyConsumesInput(boolean imeTemporarilyConsumesInput) { + return mTarget.setImeTemporarilyConsumesInput(imeTemporarilyConsumesInput); + } } diff --git a/core/java/android/view/inputmethod/SurroundingText.java b/core/java/android/view/inputmethod/SurroundingText.java index 94e05a830b85..c85a18a1df79 100644 --- a/core/java/android/view/inputmethod/SurroundingText.java +++ b/core/java/android/view/inputmethod/SurroundingText.java @@ -18,6 +18,7 @@ package android.view.inputmethod; import android.annotation.IntRange; import android.annotation.NonNull; +import android.annotation.Nullable; import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; @@ -74,7 +75,7 @@ public final class SurroundingText implements Parcelable { public SurroundingText(@NonNull final CharSequence text, @IntRange(from = 0) int selectionStart, @IntRange(from = 0) int selectionEnd, @IntRange(from = -1) int offset) { - mText = text; + mText = copyWithParcelableSpans(text); mSelectionStart = selectionStart; mSelectionEnd = selectionEnd; mOffset = offset; @@ -155,4 +156,29 @@ public final class SurroundingText implements Parcelable { return new SurroundingText[size]; } }; + + /** + * Create a copy of the given {@link CharSequence} object, with completely copy + * {@link ParcelableSpan} instances. + * + * @param source the original {@link CharSequence} to be copied. + * @return the copied {@link CharSequence}. {@code null} if {@code source} is {@code null}. + */ + @Nullable + private static CharSequence copyWithParcelableSpans(@Nullable CharSequence source) { + if (source == null) { + return null; + } + Parcel parcel = null; + try { + parcel = Parcel.obtain(); + TextUtils.writeToParcel(source, parcel, /* parcelableFlags= */ 0); + parcel.setDataPosition(0); + return TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel); + } finally { + if (parcel != null) { + parcel.recycle(); + } + } + } } diff --git a/core/java/android/webkit/TEST_MAPPING b/core/java/android/webkit/TEST_MAPPING new file mode 100644 index 000000000000..bd25200ffc38 --- /dev/null +++ b/core/java/android/webkit/TEST_MAPPING @@ -0,0 +1,36 @@ +{ + "presubmit": [ + { + "name": "CtsWebkitTestCases", + "options": [ + { + "exclude-annotation": "androidx.test.filters.FlakyTest" + } + ] + }, + { + "name": "CtsHostsideWebViewTests", + "options": [ + { + "exclude-annotation": "androidx.test.filters.FlakyTest" + } + ] + }, + { + "name": "GtsWebViewTestCases", + "options": [ + { + "exclude-annotation": "android.test.FlakyTest" + } + ] + }, + { + "name": "GtsWebViewHostTestCases", + "options": [ + { + "exclude-annotation": "android.test.FlakyTest" + } + ] + } + ] +} diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java index 4ba1ca8989b7..4a84851eb075 100644 --- a/core/java/android/widget/RemoteViews.java +++ b/core/java/android/widget/RemoteViews.java @@ -18,6 +18,7 @@ package android.widget; import android.annotation.ColorInt; import android.annotation.DimenRes; +import android.annotation.IdRes; import android.annotation.IntDef; import android.annotation.LayoutRes; import android.annotation.NonNull; @@ -63,12 +64,15 @@ import android.util.ArrayMap; import android.util.IntArray; import android.util.Log; import android.util.Pair; +import android.util.TypedValue; +import android.util.TypedValue.ComplexDimensionUnit; import android.view.ContextThemeWrapper; import android.view.LayoutInflater; import android.view.LayoutInflater.Filter; import android.view.RemotableViewMethod; import android.view.View; import android.view.ViewGroup; +import android.view.ViewGroup.MarginLayoutParams; import android.view.ViewStub; import android.widget.AdapterView.OnItemClickListener; @@ -173,6 +177,48 @@ public class RemoteViews implements Parcelable, Filter { private static final int SET_INT_TAG_TAG = 22; /** @hide **/ + @IntDef(prefix = "MARGIN_", value = { + MARGIN_LEFT, + MARGIN_TOP, + MARGIN_RIGHT, + MARGIN_BOTTOM, + MARGIN_START, + MARGIN_END + }) + @Retention(RetentionPolicy.SOURCE) + public @interface MarginType {} + /** + * The value will apply to the marginLeft. + * @hide + */ + public static final int MARGIN_LEFT = 0; + /** + * The value will apply to the marginTop. + * @hide + */ + public static final int MARGIN_TOP = 1; + /** + * The value will apply to the marginRight. + * @hide + */ + public static final int MARGIN_RIGHT = 2; + /** + * The value will apply to the marginBottom. + * @hide + */ + public static final int MARGIN_BOTTOM = 3; + /** + * The value will apply to the marginStart. + * @hide + */ + public static final int MARGIN_START = 4; + /** + * The value will apply to the marginEnd. + * @hide + */ + public static final int MARGIN_END = 5; + + /** @hide **/ @IntDef(flag = true, value = { FLAG_REAPPLY_DISALLOWED, FLAG_WIDGET_IS_COLLECTION_CHILD, @@ -1730,8 +1776,16 @@ public class RemoteViews implements Parcelable, Filter { final ViewGroup targetVg = (ViewGroup) target.mRoot; - // Clear all children when nested views omitted - target.mChildren = null; + if (mViewIdToKeep == REMOVE_ALL_VIEWS_ID) { + // Clear all children when there's no excepted view + target.mChildren = null; + } else { + // Remove just the children which don't match the excepted view + target.mChildren.removeIf(childTree -> childTree.mRoot.getId() != mViewIdToKeep); + if (target.mChildren.isEmpty()) { + target.mChildren = null; + } + } return new RuntimeAction() { @Override public void apply(View root, ViewGroup rootParent, OnClickHandler handler) @@ -1922,13 +1976,13 @@ public class RemoteViews implements Parcelable, Filter { * Helper action to set text size on a TextView in any supported units. */ private class TextViewSizeAction extends Action { - public TextViewSizeAction(int viewId, int units, float size) { + TextViewSizeAction(@IdRes int viewId, @ComplexDimensionUnit int units, float size) { this.viewId = viewId; this.units = units; this.size = size; } - public TextViewSizeAction(Parcel parcel) { + TextViewSizeAction(Parcel parcel) { viewId = parcel.readInt(); units = parcel.readInt(); size = parcel.readFloat(); @@ -2004,36 +2058,56 @@ public class RemoteViews implements Parcelable, Filter { */ private static class LayoutParamAction extends Action { - /** Set marginEnd */ - public static final int LAYOUT_MARGIN_END_DIMEN = 1; - /** Set width */ - public static final int LAYOUT_WIDTH = 2; - public static final int LAYOUT_MARGIN_BOTTOM_DIMEN = 3; - public static final int LAYOUT_MARGIN_END = 4; + static final int LAYOUT_MARGIN_LEFT = MARGIN_LEFT; + static final int LAYOUT_MARGIN_TOP = MARGIN_TOP; + static final int LAYOUT_MARGIN_RIGHT = MARGIN_RIGHT; + static final int LAYOUT_MARGIN_BOTTOM = MARGIN_BOTTOM; + static final int LAYOUT_MARGIN_START = MARGIN_START; + static final int LAYOUT_MARGIN_END = MARGIN_END; + static final int LAYOUT_WIDTH = 8; + static final int LAYOUT_HEIGHT = 9; final int mProperty; + final boolean mIsDimen; final int mValue; /** * @param viewId ID of the view alter * @param property which layout parameter to alter * @param value new value of the layout parameter + * @param units the units of the given value + */ + LayoutParamAction(@IdRes int viewId, int property, float value, + @ComplexDimensionUnit int units) { + this.viewId = viewId; + this.mProperty = property; + this.mIsDimen = false; + this.mValue = TypedValue.createComplexDimension(value, units); + } + + /** + * @param viewId ID of the view alter + * @param property which layout parameter to alter + * @param dimen new dimension with the value of the layout parameter */ - public LayoutParamAction(int viewId, int property, int value) { + LayoutParamAction(@IdRes int viewId, int property, @DimenRes int dimen) { this.viewId = viewId; this.mProperty = property; - this.mValue = value; + this.mIsDimen = true; + this.mValue = dimen; } public LayoutParamAction(Parcel parcel) { viewId = parcel.readInt(); mProperty = parcel.readInt(); + mIsDimen = parcel.readBoolean(); mValue = parcel.readInt(); } public void writeToParcel(Parcel dest, int flags) { dest.writeInt(viewId); dest.writeInt(mProperty); + dest.writeBoolean(mIsDimen); dest.writeInt(mValue); } @@ -2047,26 +2121,49 @@ public class RemoteViews implements Parcelable, Filter { if (layoutParams == null) { return; } - int value = mValue; switch (mProperty) { - case LAYOUT_MARGIN_END_DIMEN: - value = resolveDimenPixelOffset(target, mValue); - // fall-through - case LAYOUT_MARGIN_END: - if (layoutParams instanceof ViewGroup.MarginLayoutParams) { - ((ViewGroup.MarginLayoutParams) layoutParams).setMarginEnd(value); + case LAYOUT_MARGIN_LEFT: + if (layoutParams instanceof MarginLayoutParams) { + ((MarginLayoutParams) layoutParams).leftMargin = getPixelOffset(target); target.setLayoutParams(layoutParams); } break; - case LAYOUT_MARGIN_BOTTOM_DIMEN: - if (layoutParams instanceof ViewGroup.MarginLayoutParams) { - int resolved = resolveDimenPixelOffset(target, mValue); - ((ViewGroup.MarginLayoutParams) layoutParams).bottomMargin = resolved; + case LAYOUT_MARGIN_TOP: + if (layoutParams instanceof MarginLayoutParams) { + ((MarginLayoutParams) layoutParams).topMargin = getPixelOffset(target); + target.setLayoutParams(layoutParams); + } + break; + case LAYOUT_MARGIN_RIGHT: + if (layoutParams instanceof MarginLayoutParams) { + ((MarginLayoutParams) layoutParams).rightMargin = getPixelOffset(target); + target.setLayoutParams(layoutParams); + } + break; + case LAYOUT_MARGIN_BOTTOM: + if (layoutParams instanceof MarginLayoutParams) { + ((MarginLayoutParams) layoutParams).bottomMargin = getPixelOffset(target); + target.setLayoutParams(layoutParams); + } + break; + case LAYOUT_MARGIN_START: + if (layoutParams instanceof MarginLayoutParams) { + ((MarginLayoutParams) layoutParams).setMarginStart(getPixelOffset(target)); + target.setLayoutParams(layoutParams); + } + break; + case LAYOUT_MARGIN_END: + if (layoutParams instanceof MarginLayoutParams) { + ((MarginLayoutParams) layoutParams).setMarginEnd(getPixelOffset(target)); target.setLayoutParams(layoutParams); } break; case LAYOUT_WIDTH: - layoutParams.width = mValue; + layoutParams.width = getPixelSize(target); + target.setLayoutParams(layoutParams); + break; + case LAYOUT_HEIGHT: + layoutParams.height = getPixelSize(target); target.setLayoutParams(layoutParams); break; default: @@ -2074,11 +2171,26 @@ public class RemoteViews implements Parcelable, Filter { } } - private static int resolveDimenPixelOffset(View target, int value) { - if (value == 0) { - return 0; + private int getPixelOffset(View target) { + if (mIsDimen) { + if (mValue == 0) { + return 0; + } + return target.getResources().getDimensionPixelOffset(mValue); + } + return TypedValue.complexToDimensionPixelOffset(mValue, + target.getResources().getDisplayMetrics()); + } + + private int getPixelSize(View target) { + if (mIsDimen) { + if (mValue == 0) { + return 0; + } + return target.getResources().getDimensionPixelSize(mValue); } - return target.getContext().getResources().getDimensionPixelOffset(value); + return TypedValue.complexToDimensionPixelSize(mValue, + target.getResources().getDisplayMetrics()); } @Override @@ -2512,6 +2624,7 @@ public class RemoteViews implements Parcelable, Filter { * @param nestedView {@link RemoteViews} that describes the child. */ public void addView(int viewId, RemoteViews nestedView) { + // Clear all children when nested views omitted addAction(nestedView == null ? new ViewGroupActionRemove(viewId) : new ViewGroupActionAdd(viewId, nestedView)); @@ -3044,57 +3157,94 @@ public class RemoteViews implements Parcelable, Filter { } /** - * @hide - * Equivalent to calling {@link android.view.ViewGroup.MarginLayoutParams#setMarginEnd(int)}. + * Equivalent to calling {@link MarginLayoutParams#setMarginEnd}. * Only works if the {@link View#getLayoutParams()} supports margins. - * Hidden for now since we don't want to support this for all different layout margins yet. * * @param viewId The id of the view to change - * @param endMarginDimen a dimen resource to read the margin from or 0 to clear the margin. + * @param type The margin being set e.g. {@link #MARGIN_END} + * @param dimen a dimension resource to apply to the margin, or 0 to clear the margin. + * @hide */ - public void setViewLayoutMarginEndDimen(int viewId, @DimenRes int endMarginDimen) { - addAction(new LayoutParamAction(viewId, LayoutParamAction.LAYOUT_MARGIN_END_DIMEN, - endMarginDimen)); + public void setViewLayoutMarginDimen(@IdRes int viewId, @MarginType int type, + @DimenRes int dimen) { + addAction(new LayoutParamAction(viewId, type, dimen)); } /** - * Equivalent to calling {@link android.view.ViewGroup.MarginLayoutParams#setMarginEnd(int)}. + * Equivalent to calling {@link MarginLayoutParams#setMarginEnd}. * Only works if the {@link View#getLayoutParams()} supports margins. - * Hidden for now since we don't want to support this for all different layout margins yet. + * + * <p>NOTE: It is recommended to use {@link TypedValue#COMPLEX_UNIT_PX} only for 0. + * Setting margins in pixels will behave poorly when the RemoteViews object is used on a + * display with a different density. * * @param viewId The id of the view to change - * @param endMargin a value in pixels for the end margin. + * @param type The margin being set e.g. {@link #MARGIN_END} + * @param value a value for the margin the given units. + * @param units The unit type of the value e.g. {@link TypedValue#COMPLEX_UNIT_DIP} * @hide */ - public void setViewLayoutMarginEnd(int viewId, @DimenRes int endMargin) { - addAction(new LayoutParamAction(viewId, LayoutParamAction.LAYOUT_MARGIN_END, - endMargin)); + public void setViewLayoutMargin(@IdRes int viewId, @MarginType int type, float value, + @ComplexDimensionUnit int units) { + addAction(new LayoutParamAction(viewId, type, value, units)); } /** - * Equivalent to setting {@link android.view.ViewGroup.MarginLayoutParams#bottomMargin}. + * Equivalent to setting {@link android.view.ViewGroup.LayoutParams#width} except that you may + * provide the value in any dimension units. * - * @param bottomMarginDimen a dimen resource to read the margin from or 0 to clear the margin. + * <p>NOTE: It is recommended to use {@link TypedValue#COMPLEX_UNIT_PX} only for 0, + * {@link ViewGroup.LayoutParams#WRAP_CONTENT}, or {@link ViewGroup.LayoutParams#MATCH_PARENT}. + * Setting actual sizes in pixels will behave poorly when the RemoteViews object is used on a + * display with a different density. + * + * @param width Width of the view in the given units + * @param units The unit type of the value e.g. {@link TypedValue#COMPLEX_UNIT_DIP} * @hide */ - public void setViewLayoutMarginBottomDimen(int viewId, @DimenRes int bottomMarginDimen) { - addAction(new LayoutParamAction(viewId, LayoutParamAction.LAYOUT_MARGIN_BOTTOM_DIMEN, - bottomMarginDimen)); + public void setViewLayoutWidth(@IdRes int viewId, float width, + @ComplexDimensionUnit int units) { + addAction(new LayoutParamAction(viewId, LayoutParamAction.LAYOUT_WIDTH, width, units)); } /** - * Equivalent to setting {@link android.view.ViewGroup.LayoutParams#width}. + * Equivalent to setting {@link android.view.ViewGroup.LayoutParams#width} with + * the result of {@link Resources#getDimensionPixelSize(int)}. * - * @param layoutWidth one of 0, MATCH_PARENT or WRAP_CONTENT. Other sizes are not allowed - * because they behave poorly when the density changes. + * @param widthDimen the dimension resource for the view's width * @hide */ - public void setViewLayoutWidth(int viewId, int layoutWidth) { - if (layoutWidth != 0 && layoutWidth != ViewGroup.LayoutParams.MATCH_PARENT - && layoutWidth != ViewGroup.LayoutParams.WRAP_CONTENT) { - throw new IllegalArgumentException("Only supports 0, WRAP_CONTENT and MATCH_PARENT"); - } - mActions.add(new LayoutParamAction(viewId, LayoutParamAction.LAYOUT_WIDTH, layoutWidth)); + public void setViewLayoutWidthDimen(@IdRes int viewId, @DimenRes int widthDimen) { + addAction(new LayoutParamAction(viewId, LayoutParamAction.LAYOUT_WIDTH, widthDimen)); + } + + /** + * Equivalent to setting {@link android.view.ViewGroup.LayoutParams#height} except that you may + * provide the value in any dimension units. + * + * <p>NOTE: It is recommended to use {@link TypedValue#COMPLEX_UNIT_PX} only for 0, + * {@link ViewGroup.LayoutParams#WRAP_CONTENT}, or {@link ViewGroup.LayoutParams#MATCH_PARENT}. + * Setting actual sizes in pixels will behave poorly when the RemoteViews object is used on a + * display with a different density. + * + * @param height height of the view in the given units + * @param units The unit type of the value e.g. {@link TypedValue#COMPLEX_UNIT_DIP} + * @hide + */ + public void setViewLayoutHeight(@IdRes int viewId, float height, + @ComplexDimensionUnit int units) { + addAction(new LayoutParamAction(viewId, LayoutParamAction.LAYOUT_HEIGHT, height, units)); + } + + /** + * Equivalent to setting {@link android.view.ViewGroup.LayoutParams#height} with + * the result of {@link Resources#getDimensionPixelSize(int)}. + * + * @param heightDimen a dimen resource to read the height from. + * @hide + */ + public void setViewLayoutHeightDimen(@IdRes int viewId, @DimenRes int heightDimen) { + addAction(new LayoutParamAction(viewId, LayoutParamAction.LAYOUT_HEIGHT, heightDimen)); } /** diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 4edfc5f309b2..1599f2bdef2e 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -494,6 +494,13 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener private TextUtils.TruncateAt mEllipsize; + // A flag to indicate the cursor was hidden by IME. + private boolean mImeTemporarilyConsumesInput; + + // Whether cursor is visible without regard to {@link mImeTemporarilyConsumesInput}. + // {code true} is the default value. + private boolean mCursorVisibleFromAttr = true; + static class Drawables { static final int LEFT = 0; static final int TOP = 1; @@ -10496,7 +10503,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener /** * Set whether the cursor is visible. The default is true. Note that this property only - * makes sense for editable TextView. + * makes sense for editable TextView. If IME is temporarily consuming the input, the cursor will + * be always invisible, visibility will be updated as the last state when IME does not consume + * the input anymore. * * @see #isCursorVisible() * @@ -10504,6 +10513,25 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener */ @android.view.RemotableViewMethod public void setCursorVisible(boolean visible) { + mCursorVisibleFromAttr = visible; + updateCursorVisibleInternal(); + } + + /** + * Sets the IME is temporarily consuming the input and make the cursor invisible if + * {@code imeTemporarilyConsumesInput} is {@code true}. Otherwise, make the cursor visible. + * + * @param imeTemporarilyConsumesInput {@code true} if IME is temporarily consuming the input + * + * @hide + */ + public void setImeTemporarilyConsumesInput(boolean imeTemporarilyConsumesInput) { + mImeTemporarilyConsumesInput = imeTemporarilyConsumesInput; + updateCursorVisibleInternal(); + } + + private void updateCursorVisibleInternal() { + boolean visible = mCursorVisibleFromAttr && !mImeTemporarilyConsumesInput; if (visible && mEditor == null) return; // visible is the default value with no edit data createEditorIfNeeded(); if (mEditor.mCursorVisible != visible) { @@ -10518,7 +10546,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } /** - * @return whether or not the cursor is visible (assuming this TextView is editable) + * @return whether or not the cursor is visible (assuming this TextView is editable). This + * method may return {@code false} when the IME is temporarily consuming the input even if the + * {@code mEditor.mCursorVisible} attribute is {@code true} or {@code #setCursorVisible(true)} + * is called. * * @see #setCursorVisible(boolean) * diff --git a/core/java/android/window/ITransitionPlayer.aidl b/core/java/android/window/ITransitionPlayer.aidl index a8a29b26a148..55d47cb7ed8b 100644 --- a/core/java/android/window/ITransitionPlayer.aidl +++ b/core/java/android/window/ITransitionPlayer.aidl @@ -16,6 +16,7 @@ package android.window; +import android.app.ActivityManager; import android.view.SurfaceControl; import android.window.TransitionInfo; import android.window.WindowContainerTransaction; @@ -58,6 +59,9 @@ oneway interface ITransitionPlayer { * @param type The {@link WindowManager#TransitionType} of the transition to start. * @param transitionToken An identifying token for the transition that needs to be started. * Pass this to {@link IWindowOrganizerController#startTransition}. + * @param triggerTask If non-null, the task containing the activity whose lifecycle change + * (start or finish) has caused this transition to occur. */ - void requestStartTransition(int type, in IBinder transitionToken); + void requestStartTransition(int type, in IBinder transitionToken, + in ActivityManager.RunningTaskInfo triggerTask); } diff --git a/core/java/android/window/TransitionInfo.java b/core/java/android/window/TransitionInfo.java index da291cf0fd2c..d1d49b6d4722 100644 --- a/core/java/android/window/TransitionInfo.java +++ b/core/java/android/window/TransitionInfo.java @@ -26,6 +26,7 @@ import static android.view.WindowManager.TRANSIT_TO_FRONT; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.app.ActivityManager; import android.graphics.Point; import android.graphics.Rect; import android.os.Parcel; @@ -153,7 +154,8 @@ public final class TransitionInfo implements Parcelable { /** * @return a surfacecontrol that can serve as a parent surfacecontrol for all the changing * participants to animate within. This will generally be placed at the highest-z-order - * shared ancestor of all participants. + * shared ancestor of all participants. While this is non-null, it's possible for the rootleash + * to be invalid if the transition is a no-op. */ @NonNull public SurfaceControl getRootLeash() { @@ -181,7 +183,7 @@ public final class TransitionInfo implements Parcelable { @Nullable public Change getChange(@NonNull WindowContainerToken token) { for (int i = mChanges.size() - 1; i >= 0; --i) { - if (mChanges.get(i).mContainer == token) { + if (token.equals(mChanges.get(i).mContainer)) { return mChanges.get(i); } } @@ -254,6 +256,7 @@ public final class TransitionInfo implements Parcelable { private final Rect mStartAbsBounds = new Rect(); private final Rect mEndAbsBounds = new Rect(); private final Point mEndRelOffset = new Point(); + private ActivityManager.RunningTaskInfo mTaskInfo = null; public Change(@Nullable WindowContainerToken container, @NonNull SurfaceControl leash) { mContainer = container; @@ -270,6 +273,7 @@ public final class TransitionInfo implements Parcelable { mStartAbsBounds.readFromParcel(in); mEndAbsBounds.readFromParcel(in); mEndRelOffset.readFromParcel(in); + mTaskInfo = in.readTypedObject(ActivityManager.RunningTaskInfo.CREATOR); } /** Sets the parent of this change's container. The parent must be a participant or null. */ @@ -302,6 +306,14 @@ public final class TransitionInfo implements Parcelable { mEndRelOffset.set(left, top); } + /** + * Sets the taskinfo of this container if this is a task. WARNING: this takes the + * reference, so don't modify it afterwards. + */ + public void setTaskInfo(ActivityManager.RunningTaskInfo taskInfo) { + mTaskInfo = taskInfo; + } + /** @return the container that is changing. May be null if non-remotable (eg. activity) */ @Nullable public WindowContainerToken getContainer() { @@ -359,6 +371,12 @@ public final class TransitionInfo implements Parcelable { return mLeash; } + /** @return the task info or null if this isn't a task */ + @NonNull + public ActivityManager.RunningTaskInfo getTaskInfo() { + return mTaskInfo; + } + @Override /** @hide */ public void writeToParcel(@NonNull Parcel dest, int flags) { @@ -370,6 +388,7 @@ public final class TransitionInfo implements Parcelable { mStartAbsBounds.writeToParcel(dest, flags); mEndAbsBounds.writeToParcel(dest, flags); mEndRelOffset.writeToParcel(dest, flags); + dest.writeTypedObject(mTaskInfo, flags); } @NonNull diff --git a/core/java/com/android/internal/inputmethod/CallbackUtils.java b/core/java/com/android/internal/inputmethod/CallbackUtils.java index 248feb8bcbd7..e9e39db90437 100644 --- a/core/java/com/android/internal/inputmethod/CallbackUtils.java +++ b/core/java/com/android/internal/inputmethod/CallbackUtils.java @@ -200,4 +200,29 @@ public final class CallbackUtils { callback.onResult(result); } catch (RemoteException ignored) { } } + + /** + * A utility method using given {@link IVoidResultCallback} to callback the result. + * + * @param callback {@link IVoidResultCallback} to be called back. + * @param resultSupplier the supplier from which the result is provided. + */ + public static void onResult(@NonNull IVoidResultCallback callback, + @NonNull Supplier<Void> resultSupplier) { + Throwable exception = null; + + try { + resultSupplier.get(); + } catch (Throwable throwable) { + exception = throwable; + } + + try { + if (exception != null) { + callback.onError(ThrowableHolder.of(exception)); + return; + } + callback.onResult(); + } catch (RemoteException ignored) { } + } } diff --git a/core/java/com/android/internal/inputmethod/Completable.java b/core/java/com/android/internal/inputmethod/Completable.java index 1913fcdc9ba9..b82ba8132889 100644 --- a/core/java/com/android/internal/inputmethod/Completable.java +++ b/core/java/com/android/internal/inputmethod/Completable.java @@ -286,6 +286,42 @@ public final class Completable { } /** + * Completable object of {@link java.lang.Void}. + */ + public static final class Void extends ValueBase { + /** + * Notify when this completable object callback. + */ + @AnyThread + @Override + protected void onComplete() { + synchronized (mStateLock) { + switch (mState) { + case CompletionState.NOT_COMPLETED: + mState = CompletionState.COMPLETED_WITH_VALUE; + break; + default: + throw new UnsupportedOperationException( + "onComplete() is not allowed on state=" + stateToString(mState)); + } + } + super.onComplete(); + } + + /** + * @throws RuntimeException when called while {@link #onError} happened. + * @throws UnsupportedOperationException when called while {@link #hasValue()} returns + * {@code false}. + */ + @AnyThread + public void getValue() { + synchronized (mStateLock) { + enforceGetValueLocked(); + } + } + } + + /** * Base class of completable object types. * * @param <T> type associated with this completable object. @@ -396,6 +432,13 @@ public final class Completable { } /** + * @return an instance of {@link Completable.Void}. + */ + public static Completable.Void createVoid() { + return new Completable.Void(); + } + + /** * Completable object of {@link java.lang.Boolean}. */ public static final class Boolean extends Values<java.lang.Boolean> { } @@ -465,6 +508,17 @@ public final class Completable { } /** + * Await the result by the {@link Completable.Void}. + * + * Check the result once {@link ValueBase#onComplete()} + */ + @AnyThread + public static void getResult(@NonNull Completable.Void value) { + value.await(); + value.getValue(); + } + + /** * Await the result by the {@link Completable.Int}, and log it if there is no result after * given timeout. * diff --git a/core/java/com/android/internal/inputmethod/IVoidResultCallback.aidl b/core/java/com/android/internal/inputmethod/IVoidResultCallback.aidl new file mode 100644 index 000000000000..0b25a2b886c9 --- /dev/null +++ b/core/java/com/android/internal/inputmethod/IVoidResultCallback.aidl @@ -0,0 +1,24 @@ +/* + * 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.internal.inputmethod; + +import com.android.internal.inputmethod.ThrowableHolder; + +oneway interface IVoidResultCallback { + void onResult(); + void onError(in ThrowableHolder exception); +}
\ No newline at end of file diff --git a/core/java/com/android/internal/inputmethod/ResultCallbacks.java b/core/java/com/android/internal/inputmethod/ResultCallbacks.java index 6ce851b59ccd..2a48c1f60aa9 100644 --- a/core/java/com/android/internal/inputmethod/ResultCallbacks.java +++ b/core/java/com/android/internal/inputmethod/ResultCallbacks.java @@ -352,4 +352,39 @@ public final class ResultCallbacks { } }; } + + /** + * Creates {@link IVoidResultCallback.Stub} that is to set {@link Completable.Void} when + * receiving the result. + * + * @param value {@link Completable.Void} to be set when receiving the result. + * @return {@link IVoidResultCallback.Stub} that can be passed as a binder IPC parameter. + */ + @AnyThread + public static IVoidResultCallback.Stub of(@NonNull Completable.Void value) { + final AtomicReference<WeakReference<Completable.Void>> atomicRef = + new AtomicReference<>(new WeakReference<>(value)); + + return new IVoidResultCallback.Stub() { + @BinderThread + @Override + public void onResult() { + final Completable.Void value = unwrap(atomicRef); + if (value == null) { + return; + } + value.onComplete(); + } + + @BinderThread + @Override + public void onError(ThrowableHolder throwableHolder) { + final Completable.Void value = unwrap(atomicRef); + if (value == null) { + return; + } + value.onError(throwableHolder); + } + }; + } } diff --git a/core/java/com/android/internal/jank/InteractionJankMonitor.java b/core/java/com/android/internal/jank/InteractionJankMonitor.java index 771a72c98a7a..1e2ce288974c 100644 --- a/core/java/com/android/internal/jank/InteractionJankMonitor.java +++ b/core/java/com/android/internal/jank/InteractionJankMonitor.java @@ -463,7 +463,7 @@ public class InteractionJankMonitor { } public String getName() { - return "Cuj<" + getNameOfCuj(mCujType) + ">"; + return "J<" + getNameOfCuj(mCujType) + ">"; } } } diff --git a/core/java/com/android/internal/os/KernelCpuUidTimeReader.java b/core/java/com/android/internal/os/KernelCpuUidTimeReader.java index 2dd51b4459e7..f7fad2c5bbaa 100644 --- a/core/java/com/android/internal/os/KernelCpuUidTimeReader.java +++ b/core/java/com/android/internal/os/KernelCpuUidTimeReader.java @@ -143,10 +143,6 @@ public abstract class KernelCpuUidTimeReader<T> { */ public void removeUid(int uid) { mLastTimes.delete(uid); - - if (mBpfTimesAvailable) { - mBpfReader.removeUidsInRange(uid, uid); - } } /** diff --git a/core/java/com/android/internal/util/LatencyTracker.java b/core/java/com/android/internal/util/LatencyTracker.java index 254c2997fa65..1e9801f5ef30 100644 --- a/core/java/com/android/internal/util/LatencyTracker.java +++ b/core/java/com/android/internal/util/LatencyTracker.java @@ -91,18 +91,6 @@ public class LatencyTracker { */ public static final int ACTION_START_RECENTS_ANIMATION = 8; - private static final String[] NAMES = new String[]{ - "expand panel", - "toggle recents", - "fingerprint wake-and-unlock", - "check credential", - "check credential unlocked", - "turn on screen", - "rotate the screen", - "face wake-and-unlock", - "start recents-animation", - }; - private static final int[] STATSD_ACTION = new int[]{ FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_EXPAND_PANEL, FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_TOGGLE_RECENTS, @@ -185,6 +173,10 @@ public class LatencyTracker { } } + private String getTraceNameOfAcion(int action) { + return "L<" + getNameOfAction(action) + ">"; + } + public static boolean isEnabled(Context ctx) { return getInstance(ctx).isEnabled(); } @@ -202,7 +194,7 @@ public class LatencyTracker { if (!isEnabled()) { return; } - Trace.asyncTraceBegin(Trace.TRACE_TAG_APP, NAMES[action], 0); + Trace.asyncTraceBegin(Trace.TRACE_TAG_APP, getTraceNameOfAcion(action), 0); mStartRtc.put(action, SystemClock.elapsedRealtime()); } @@ -221,7 +213,7 @@ public class LatencyTracker { return; } mStartRtc.delete(action); - Trace.asyncTraceEnd(Trace.TRACE_TAG_APP, NAMES[action], 0); + Trace.asyncTraceEnd(Trace.TRACE_TAG_APP, getTraceNameOfAcion(action), 0); logAction(action, (int) (endRtc - startRtc)); } diff --git a/core/java/com/android/internal/view/IInputConnectionWrapper.java b/core/java/com/android/internal/view/IInputConnectionWrapper.java index 3a7e66ce76f0..e7b7bf4a5b52 100644 --- a/core/java/com/android/internal/view/IInputConnectionWrapper.java +++ b/core/java/com/android/internal/view/IInputConnectionWrapper.java @@ -79,6 +79,7 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub { private static final int DO_CLOSE_CONNECTION = 150; private static final int DO_COMMIT_CONTENT = 160; private static final int DO_GET_SURROUNDING_TEXT = 41; + private static final int DO_SET_IME_TEMPORARILY_CONSUMES_INPUT = 170; @GuardedBy("mLock") @@ -266,6 +267,16 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub { dispatchMessage(mH.obtainMessage(DO_COMMIT_CONTENT, flags, 0 /* unused */, args)); } + /** + * Dispatches the request for setting ime temporarily consumes input. + * + * <p>See {@link InputConnection#setImeTemporarilyConsumesInput(boolean)}. + */ + public void setImeTemporarilyConsumesInput(boolean imeTemporarilyConsumesInput) { + dispatchMessage(obtainMessageB(DO_SET_IME_TEMPORARILY_CONSUMES_INPUT, + imeTemporarilyConsumesInput)); + } + void dispatchMessage(Message msg) { // If we are calling this from the main thread, then we can call // right through. Otherwise, we need to send the message to the @@ -811,6 +822,22 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub { } return; } + case DO_SET_IME_TEMPORARILY_CONSUMES_INPUT: { + Trace.traceBegin(Trace.TRACE_TAG_INPUT, + "InputConnection#setImeTemporarilyConsumesInput"); + try { + InputConnection ic = getInputConnection(); + if (ic == null || !isActive()) { + Log.w(TAG, + "setImeTemporarilyConsumesInput on inactive InputConnection"); + return; + } + ic.setImeTemporarilyConsumesInput(msg.arg1 == 1); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_INPUT); + } + return; + } } Log.w(TAG, "Unhandled message code: " + msg.what); } @@ -837,4 +864,8 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub { args.arg2 = arg2; return mH.obtainMessage(what, 0, 0, args); } + + Message obtainMessageB(int what, boolean arg1) { + return mH.obtainMessage(what, arg1 ? 1 : 0, 0); + } } diff --git a/core/java/com/android/internal/view/IInputContext.aidl b/core/java/com/android/internal/view/IInputContext.aidl index 53cbf961fff4..586404c53f18 100644 --- a/core/java/com/android/internal/view/IInputContext.aidl +++ b/core/java/com/android/internal/view/IInputContext.aidl @@ -85,4 +85,6 @@ import com.android.internal.inputmethod.ISurroundingTextResultCallback; void getSurroundingText(int beforeLength, int afterLength, int flags, ISurroundingTextResultCallback callback); + + void setImeTemporarilyConsumesInput(boolean imeTemporarilyConsumesInput); } diff --git a/core/java/com/android/internal/view/InputConnectionWrapper.java b/core/java/com/android/internal/view/InputConnectionWrapper.java index af9c0124078a..84c92ca83f36 100644 --- a/core/java/com/android/internal/view/InputConnectionWrapper.java +++ b/core/java/com/android/internal/view/InputConnectionWrapper.java @@ -524,6 +524,19 @@ public class InputConnectionWrapper implements InputConnection { value, TAG, "commitContent()", mCancellationGroup, MAX_WAIT_TIME_MILLIS) != 0; } + /** + * See {@link InputConnection#setImeTemporarilyConsumesInput(boolean)}. + */ + @AnyThread + public boolean setImeTemporarilyConsumesInput(boolean imeTemporarilyConsumesInput) { + try { + mIInputContext.setImeTemporarilyConsumesInput(imeTemporarilyConsumesInput); + return true; + } catch (RemoteException e) { + return false; + } + } + @AnyThread private boolean isMethodMissing(@MissingMethodFlags final int methodFlag) { return (mMissingMethods & methodFlag) == methodFlag; diff --git a/core/java/com/android/internal/widget/EditableInputConnection.java b/core/java/com/android/internal/widget/EditableInputConnection.java index 767ad42efbf3..4ccf9ce91f27 100644 --- a/core/java/com/android/internal/widget/EditableInputConnection.java +++ b/core/java/com/android/internal/widget/EditableInputConnection.java @@ -245,6 +245,15 @@ public class EditableInputConnection extends BaseInputConnection } @Override + public boolean setImeTemporarilyConsumesInput(boolean imeTemporarilyConsumesInput) { + if (mTextView == null) { + return super.setImeTemporarilyConsumesInput(imeTemporarilyConsumesInput); + } + mTextView.setImeTemporarilyConsumesInput(imeTemporarilyConsumesInput); + return true; + } + + @Override public void dumpDebug(ProtoOutputStream proto, long fieldId) { final long token = proto.start(fieldId); CharSequence editableText = mTextView.getText(); diff --git a/core/tests/coretests/src/android/util/TypedValueTest.kt b/core/tests/coretests/src/android/util/TypedValueTest.kt new file mode 100644 index 000000000000..7a05d970de33 --- /dev/null +++ b/core/tests/coretests/src/android/util/TypedValueTest.kt @@ -0,0 +1,155 @@ +/* + * 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 android.util + +import androidx.test.filters.LargeTest +import androidx.test.filters.SmallTest +import androidx.test.runner.AndroidJUnit4 +import org.junit.Assert.assertEquals +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mockito.mock +import kotlin.math.abs +import kotlin.math.min +import kotlin.math.roundToInt + +@RunWith(AndroidJUnit4::class) +class TypedValueTest { + @LargeTest + @Test + fun testFloatToComplex() { + fun assertRoundTripEquals(value: Float, expectedRadix: Int? = null) { + val complex = TypedValue.floatToComplex(value) + // Ensure values are accurate within .5% of the original value and within .5 + val delta = min(abs(value) / 512f, .5f) + assertEquals(value, TypedValue.complexToFloat(complex), delta) + // If expectedRadix is provided, validate it + if (expectedRadix != null) { + val actualRadix = ((complex shr TypedValue.COMPLEX_RADIX_SHIFT) + and TypedValue.COMPLEX_RADIX_MASK) + assertEquals("Incorrect radix for $value:", expectedRadix, actualRadix) + } + } + + assertRoundTripEquals(0f, TypedValue.COMPLEX_RADIX_23p0) + + assertRoundTripEquals(0.5f, TypedValue.COMPLEX_RADIX_0p23) + assertRoundTripEquals(0.05f, TypedValue.COMPLEX_RADIX_0p23) + assertRoundTripEquals(0.005f, TypedValue.COMPLEX_RADIX_0p23) + assertRoundTripEquals(0.0005f, TypedValue.COMPLEX_RADIX_0p23) + assertRoundTripEquals(0.00005f, TypedValue.COMPLEX_RADIX_0p23) + + assertRoundTripEquals(1.5f, TypedValue.COMPLEX_RADIX_8p15) + assertRoundTripEquals(10.5f, TypedValue.COMPLEX_RADIX_8p15) + assertRoundTripEquals(100.5f, TypedValue.COMPLEX_RADIX_8p15) + assertRoundTripEquals(255.5f, TypedValue.COMPLEX_RADIX_8p15) // 2^8 - .5 + + assertRoundTripEquals(256.5f, TypedValue.COMPLEX_RADIX_16p7) // 2^8 + .5 + assertRoundTripEquals(1000.5f, TypedValue.COMPLEX_RADIX_16p7) + assertRoundTripEquals(10000.5f, TypedValue.COMPLEX_RADIX_16p7) + assertRoundTripEquals(65535.5f, TypedValue.COMPLEX_RADIX_16p7) // 2^16 - .5 + + assertRoundTripEquals(65536.5f, TypedValue.COMPLEX_RADIX_23p0) // 2^16 + .5 + assertRoundTripEquals(100000.5f, TypedValue.COMPLEX_RADIX_23p0) + assertRoundTripEquals(1000000.5f, TypedValue.COMPLEX_RADIX_23p0) + assertRoundTripEquals(8388607.2f, TypedValue.COMPLEX_RADIX_23p0) // 2^23 -.8 + + assertRoundTripEquals(-0.5f, TypedValue.COMPLEX_RADIX_0p23) + assertRoundTripEquals(-0.05f, TypedValue.COMPLEX_RADIX_0p23) + assertRoundTripEquals(-0.005f, TypedValue.COMPLEX_RADIX_0p23) + assertRoundTripEquals(-0.0005f, TypedValue.COMPLEX_RADIX_0p23) + assertRoundTripEquals(-0.00005f, TypedValue.COMPLEX_RADIX_0p23) + + assertRoundTripEquals(-1.5f, TypedValue.COMPLEX_RADIX_8p15) + assertRoundTripEquals(-10.5f, TypedValue.COMPLEX_RADIX_8p15) + assertRoundTripEquals(-100.5f, TypedValue.COMPLEX_RADIX_8p15) + assertRoundTripEquals(-255.5f, TypedValue.COMPLEX_RADIX_8p15) // -2^8 + .5 + + // NOTE: -256.5f fits in COMPLEX_RADIX_8p15 but is stored with COMPLEX_RADIX_16p7 for + // simplicity of the algorithm. However, it's better not to enforce that with a test. + assertRoundTripEquals(-257.5f, TypedValue.COMPLEX_RADIX_16p7) // -2^8 - 1.5 + assertRoundTripEquals(-1000.5f, TypedValue.COMPLEX_RADIX_16p7) + assertRoundTripEquals(-10000.5f, TypedValue.COMPLEX_RADIX_16p7) + assertRoundTripEquals(-65535.5f, TypedValue.COMPLEX_RADIX_16p7) // -2^16 + .5 + + // NOTE: -65536.5f fits in COMPLEX_RADIX_16p7 but is stored with COMPLEX_RADIX_23p0 for + // simplicity of the algorithm. However, it's better not to enforce that with a test. + assertRoundTripEquals(-65537.5f, TypedValue.COMPLEX_RADIX_23p0) // -2^16 - 1.5 + assertRoundTripEquals(-100000.5f, TypedValue.COMPLEX_RADIX_23p0) + assertRoundTripEquals(-1000000.5f, TypedValue.COMPLEX_RADIX_23p0) + assertRoundTripEquals(-8388607.5f, TypedValue.COMPLEX_RADIX_23p0) // 2^23 -.5 + + // Test for every integer value in the range... + for (i: Int in -(1 shl 23) until (1 shl 23)) { + // ... that true integers are stored as the precise integer + assertRoundTripEquals(i.toFloat(), TypedValue.COMPLEX_RADIX_23p0) + // ... that values round up when just below an integer + assertRoundTripEquals(i - .1f) + // ... that values round down when just above an integer + assertRoundTripEquals(i + .1f) + } + } + + @SmallTest + @Test(expected = IllegalArgumentException::class) + fun testFloatToComplex_failsIfValueTooLarge() { + TypedValue.floatToComplex(8388607.5f) // 2^23 - .5 + } + + @SmallTest + @Test(expected = IllegalArgumentException::class) + fun testFloatToComplex_failsIfValueTooSmall() { + TypedValue.floatToComplex(8388608.5f) // -2^23 - .5 + } + + @LargeTest + @Test + fun testIntToComplex() { + // Validates every single valid value + for (value: Int in -(1 shl 23) until (1 shl 23)) { + assertEquals(value.toFloat(), TypedValue.complexToFloat(TypedValue.intToComplex(value))) + } + } + + @SmallTest + @Test(expected = IllegalArgumentException::class) + fun testIntToComplex_failsIfValueTooLarge() { + TypedValue.intToComplex(0x800000) + } + + @SmallTest + @Test(expected = IllegalArgumentException::class) + fun testIntToComplex_failsIfValueTooSmall() { + TypedValue.intToComplex(-0x800001) + } + + @SmallTest + @Test + fun testCreateComplexDimension_appliesUnits() { + val metrics: DisplayMetrics = mock(DisplayMetrics::class.java) + metrics.density = 3.25f + + val height = 52 * metrics.density + val widthFloat = height * 16 / 9 + val widthDimen = TypedValue.createComplexDimension( + widthFloat / metrics.density, + TypedValue.COMPLEX_UNIT_DIP + ) + val widthPx = TypedValue.complexToDimensionPixelSize(widthDimen, metrics) + assertEquals(widthFloat.roundToInt(), widthPx) + } +}
\ No newline at end of file diff --git a/core/tests/coretests/src/android/view/inputmethod/EditorInfoTest.java b/core/tests/coretests/src/android/view/inputmethod/EditorInfoTest.java index 92fb52837c36..c636912ee98d 100644 --- a/core/tests/coretests/src/android/view/inputmethod/EditorInfoTest.java +++ b/core/tests/coretests/src/android/view/inputmethod/EditorInfoTest.java @@ -109,11 +109,15 @@ public class EditorInfoTest { editorInfo.initialSelEnd = editorInfo.initialSelStart + selectionLength; final int expectedTextBeforeCursorLength = 0; final int expectedTextAfterCursorLength = testText.length(); + final SurroundingText expectedSurroundingText = + new SurroundingText(testText, editorInfo.initialSelStart, + editorInfo.initialSelEnd, 0); + editorInfo.setInitialSurroundingText(testText); assertExpectedTextLength(editorInfo, expectedTextBeforeCursorLength, selectionLength, - expectedTextAfterCursorLength); + expectedTextAfterCursorLength, expectedSurroundingText); } @Test @@ -125,11 +129,14 @@ public class EditorInfoTest { editorInfo.initialSelEnd = testText.length(); final int expectedTextBeforeCursorLength = testText.length(); final int expectedTextAfterCursorLength = 0; + final SurroundingText expectedSurroundingText = + new SurroundingText(testText, editorInfo.initialSelStart, + editorInfo.initialSelEnd, 0); editorInfo.setInitialSurroundingText(testText); assertExpectedTextLength(editorInfo, expectedTextBeforeCursorLength, selectionLength, - expectedTextAfterCursorLength); + expectedTextAfterCursorLength, expectedSurroundingText); } @Test @@ -141,11 +148,14 @@ public class EditorInfoTest { editorInfo.initialSelEnd = editorInfo.initialSelStart + selectionLength; final int expectedTextBeforeCursorLength = editorInfo.initialSelStart; final int expectedTextAfterCursorLength = testText.length() - editorInfo.initialSelEnd; + final SurroundingText expectedSurroundingText = + new SurroundingText(testText, editorInfo.initialSelStart, + editorInfo.initialSelEnd, 0); editorInfo.setInitialSurroundingText(testText); assertExpectedTextLength(editorInfo, expectedTextBeforeCursorLength, selectionLength, - expectedTextAfterCursorLength); + expectedTextAfterCursorLength, expectedSurroundingText); } @Test @@ -158,11 +168,14 @@ public class EditorInfoTest { final int expectedTextBeforeCursorLength = testText.length() / 2; final int expectedTextAfterCursorLength = testText.length() - testText.length() / 2 - selectionLength; - + final SurroundingText expectedSurroundingText = + new SurroundingText(testText, + editorInfo.initialSelEnd, + editorInfo.initialSelStart , 0); editorInfo.setInitialSurroundingText(testText); assertExpectedTextLength(editorInfo, expectedTextBeforeCursorLength, selectionLength, - expectedTextAfterCursorLength); + expectedTextAfterCursorLength, expectedSurroundingText); } @Test @@ -174,9 +187,10 @@ public class EditorInfoTest { editorInfo.setInitialSurroundingText(testText); assertExpectedTextLength(editorInfo, - /* expectBeforeCursorLength= */null, - /* expectSelectionLength= */null, - /* expectAfterCursorLength= */null); + /* expectBeforeCursorLength= */ null, + /* expectSelectionLength= */ null, + /* expectAfterCursorLength= */ null, + /* expectSurroundingText= */ null); } @Test @@ -190,11 +204,23 @@ public class EditorInfoTest { (int) (0.8 * (EditorInfo.MEMORY_EFFICIENT_TEXT_LENGTH - selectionLength))); final int expectedTextAfterCursorLength = EditorInfo.MEMORY_EFFICIENT_TEXT_LENGTH - expectedTextBeforeCursorLength - selectionLength; + final int offset = editorInfo.initialSelStart - expectedTextBeforeCursorLength; + final CharSequence beforeCursor = testText.subSequence( + offset, offset + expectedTextBeforeCursorLength); + final CharSequence afterCursor = testText.subSequence(editorInfo.initialSelEnd, + editorInfo.initialSelEnd + expectedTextAfterCursorLength); + final CharSequence selectedText = testText.subSequence(editorInfo.initialSelStart, + editorInfo.initialSelEnd); + + final SurroundingText expectedSurroundingText = + new SurroundingText(TextUtils.concat(beforeCursor, selectedText, afterCursor), + expectedTextBeforeCursorLength, + expectedTextBeforeCursorLength + selectionLength, offset); editorInfo.setInitialSurroundingText(testText); assertExpectedTextLength(editorInfo, expectedTextBeforeCursorLength, selectionLength, - expectedTextAfterCursorLength); + expectedTextAfterCursorLength, expectedSurroundingText); } @Test @@ -207,11 +233,22 @@ public class EditorInfoTest { final int expectedTextBeforeCursorLength = Math.min(editorInfo.initialSelStart, (int) (0.8 * EditorInfo.MEMORY_EFFICIENT_TEXT_LENGTH)); final int expectedTextAfterCursorLength = testText.length() - editorInfo.initialSelEnd; + final CharSequence before = testText.subSequence( + editorInfo.initialSelStart - expectedTextBeforeCursorLength, + expectedTextBeforeCursorLength); + final CharSequence after = testText.subSequence(editorInfo.initialSelEnd, + editorInfo.initialSelEnd + expectedTextAfterCursorLength); + final SurroundingText expectedSurroundingText = + new SurroundingText(TextUtils.concat(before, after), + expectedTextBeforeCursorLength, + expectedTextBeforeCursorLength, + editorInfo.initialSelStart - expectedTextBeforeCursorLength); editorInfo.setInitialSurroundingText(testText); assertExpectedTextLength(editorInfo, expectedTextBeforeCursorLength, - /* expectSelectionLength= */null, expectedTextAfterCursorLength); + /* expectSelectionLength= */null, expectedTextAfterCursorLength, + expectedSurroundingText); } @Test @@ -269,6 +306,19 @@ public class EditorInfoTest { InputConnection.GET_TEXT_WITH_STYLES), targetEditorInfo.getInitialTextAfterCursor(LONG_EXP_TEXT_LENGTH, InputConnection.GET_TEXT_WITH_STYLES))); + + final SurroundingText sourceSurroundingText = sourceEditorInfo.getInitialSurroundingText( + LONG_EXP_TEXT_LENGTH, LONG_EXP_TEXT_LENGTH, InputConnection.GET_TEXT_WITH_STYLES); + final SurroundingText targetSurroundingText = targetEditorInfo.getInitialSurroundingText( + LONG_EXP_TEXT_LENGTH, LONG_EXP_TEXT_LENGTH, InputConnection.GET_TEXT_WITH_STYLES); + + assertTrue(TextUtils.equals(sourceSurroundingText.getText(), + targetSurroundingText.getText())); + assertEquals(sourceSurroundingText.getSelectionStart(), + targetSurroundingText.getSelectionStart()); + assertEquals(sourceSurroundingText.getSelectionEnd(), + targetSurroundingText.getSelectionEnd()); + assertEquals(sourceSurroundingText.getOffset(), targetSurroundingText.getOffset()); } @Test @@ -338,7 +388,8 @@ public class EditorInfoTest { private static void assertExpectedTextLength(EditorInfo editorInfo, @Nullable Integer expectBeforeCursorLength, @Nullable Integer expectSelectionLength, - @Nullable Integer expectAfterCursorLength) { + @Nullable Integer expectAfterCursorLength, + @Nullable SurroundingText expectSurroundingText) { final CharSequence textBeforeCursor = editorInfo.getInitialTextBeforeCursor(LONG_EXP_TEXT_LENGTH, InputConnection.GET_TEXT_WITH_STYLES); @@ -347,6 +398,10 @@ public class EditorInfoTest { final CharSequence textAfterCursor = editorInfo.getInitialTextAfterCursor(LONG_EXP_TEXT_LENGTH, InputConnection.GET_TEXT_WITH_STYLES); + final SurroundingText surroundingText = editorInfo.getInitialSurroundingText( + LONG_EXP_TEXT_LENGTH, + LONG_EXP_TEXT_LENGTH, + InputConnection.GET_TEXT_WITH_STYLES); if (expectBeforeCursorLength == null) { assertNull(textBeforeCursor); @@ -365,6 +420,18 @@ public class EditorInfoTest { } else { assertEquals(expectAfterCursorLength.intValue(), textAfterCursor.length()); } + + if (expectSurroundingText == null) { + assertNull(surroundingText); + } else { + assertTrue(TextUtils.equals( + expectSurroundingText.getText(), surroundingText.getText())); + assertEquals(expectSurroundingText.getSelectionStart(), + surroundingText.getSelectionStart()); + assertEquals(expectSurroundingText.getSelectionEnd(), + surroundingText.getSelectionEnd()); + assertEquals(expectSurroundingText.getOffset(), surroundingText.getOffset()); + } } private static CharSequence createTestText(int surroundingLength) { diff --git a/core/tests/coretests/src/android/widget/TextViewTest.java b/core/tests/coretests/src/android/widget/TextViewTest.java index 66fdfff990f8..a4284a04310e 100644 --- a/core/tests/coretests/src/android/widget/TextViewTest.java +++ b/core/tests/coretests/src/android/widget/TextViewTest.java @@ -276,6 +276,34 @@ public class TextViewTest { 0, mTextView.getImeOptions() & EditorInfo.IME_FLAG_NO_FULLSCREEN); } + @Test + @UiThreadTest + public void setSetImeTemporarilyConsumesInput_recoveryToVisible() { + mTextView = new TextView(mActivity); + mTextView.setCursorVisible(true); + assertTrue(mTextView.isCursorVisible()); + + mTextView.setImeTemporarilyConsumesInput(true); + assertFalse(mTextView.isCursorVisible()); + + mTextView.setImeTemporarilyConsumesInput(false); + assertTrue(mTextView.isCursorVisible()); + } + + @Test + @UiThreadTest + public void setSetImeTemporarilyConsumesInput_recoveryToInvisible() { + mTextView = new TextView(mActivity); + mTextView.setCursorVisible(false); + assertFalse(mTextView.isCursorVisible()); + + mTextView.setImeTemporarilyConsumesInput(true); + assertFalse(mTextView.isCursorVisible()); + + mTextView.setImeTemporarilyConsumesInput(false); + assertFalse(mTextView.isCursorVisible()); + } + private String createLongText() { int size = 600 * 1000; final StringBuilder builder = new StringBuilder(size); diff --git a/graphics/java/android/graphics/RecordingCanvas.java b/graphics/java/android/graphics/RecordingCanvas.java index 9f46ceb61332..49888fd5f977 100644 --- a/graphics/java/android/graphics/RecordingCanvas.java +++ b/graphics/java/android/graphics/RecordingCanvas.java @@ -204,6 +204,26 @@ public final class RecordingCanvas extends BaseRecordingCanvas { } /** + * Draws a ripple + * + * @param cx + * @param cy + * @param radius + * @param paint + * @param progress + * @param shader + * + * @hide + */ + public void drawRipple(CanvasProperty<Float> cx, CanvasProperty<Float> cy, + CanvasProperty<Float> radius, CanvasProperty<Paint> paint, + CanvasProperty<Float> progress, RuntimeShader shader) { + nDrawRipple(mNativeCanvasWrapper, cx.getNativeContainer(), cy.getNativeContainer(), + radius.getNativeContainer(), paint.getNativeContainer(), + progress.getNativeContainer(), shader.getNativeShaderFactory()); + } + + /** * Draws a round rect * * @param left @@ -260,6 +280,9 @@ public final class RecordingCanvas extends BaseRecordingCanvas { private static native void nDrawCircle(long renderer, long propCx, long propCy, long propRadius, long propPaint); @CriticalNative + private static native void nDrawRipple(long renderer, long propCx, long propCy, long propRadius, + long propPaint, long propProgress, long runtimeEffect); + @CriticalNative private static native void nDrawRoundRect(long renderer, long propLeft, long propTop, long propRight, long propBottom, long propRx, long propRy, long propPaint); @CriticalNative diff --git a/graphics/java/android/graphics/RuntimeShader.java b/graphics/java/android/graphics/RuntimeShader.java index fb0983a83f9d..7f2e503ac8fd 100644 --- a/graphics/java/android/graphics/RuntimeShader.java +++ b/graphics/java/android/graphics/RuntimeShader.java @@ -115,6 +115,10 @@ public class RuntimeShader extends Shader { nativeShaders, colorSpace().getNativeInstance(), mIsOpaque); } + public long getNativeShaderFactory() { + return mNativeInstanceRuntimeShaderFactory; + } + private static native long nativeCreate(long shaderFactory, long matrix, byte[] inputs, long[] shaderInputs, long colorSpaceHandle, boolean isOpaque); diff --git a/libs/WindowManager/Shell/res/layout/tv_pip_controls.xml b/libs/WindowManager/Shell/res/layout/tv_pip_controls.xml deleted file mode 100644 index 9157f63ce1b3..000000000000 --- a/libs/WindowManager/Shell/res/layout/tv_pip_controls.xml +++ /dev/null @@ -1,33 +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. ---> -<!-- Layout for {@link com.android.wm.shell.pip.tv.PipControlsView}. --> -<merge xmlns:android="http://schemas.android.com/apk/res/android"> - <com.android.wm.shell.pip.tv.PipControlButtonView - android:id="@+id/full_button" - android:layout_width="@dimen/picture_in_picture_button_width" - android:layout_height="wrap_content" - android:src="@drawable/pip_ic_fullscreen_white" - android:text="@string/pip_fullscreen" /> - - <com.android.wm.shell.pip.tv.PipControlButtonView - android:id="@+id/close_button" - android:layout_width="@dimen/picture_in_picture_button_width" - android:layout_height="wrap_content" - android:layout_marginStart="@dimen/picture_in_picture_button_start_margin" - android:src="@drawable/pip_ic_close_white" - android:text="@string/pip_close" /> -</merge> diff --git a/libs/WindowManager/Shell/res/layout/tv_pip_menu.xml b/libs/WindowManager/Shell/res/layout/tv_pip_menu.xml index 0d684e8b0ab5..49e2379589a4 100644 --- a/libs/WindowManager/Shell/res/layout/tv_pip_menu.xml +++ b/libs/WindowManager/Shell/res/layout/tv_pip_menu.xml @@ -14,19 +14,39 @@ See the License for the specific language governing permissions and limitations under the License. --> -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+id/tv_pip_menu" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:orientation="horizontal" - android:paddingTop="350dp" - android:background="#CC000000" - android:gravity="top|center_horizontal" - android:clipChildren="false"> - - <com.android.wm.shell.pip.tv.PipControlsView - android:id="@+id/pip_controls" +<!-- Layout for TvPipMenuView --> +<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/tv_pip_menu" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="#CC000000"> + + <LinearLayout + android:id="@+id/tv_pip_menu_action_buttons" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:alpha="0" /> -</LinearLayout> + android:layout_gravity="center_horizontal" + android:layout_marginTop="350dp" + android:orientation="horizontal" + android:alpha="0"> + + <com.android.wm.shell.pip.tv.TvPipMenuActionButton + android:id="@+id/tv_pip_menu_fullscreen_button" + android:layout_width="@dimen/picture_in_picture_button_width" + android:layout_height="wrap_content" + android:src="@drawable/pip_ic_fullscreen_white" + android:text="@string/pip_fullscreen" /> + + <com.android.wm.shell.pip.tv.TvPipMenuActionButton + android:id="@+id/tv_pip_menu_close_button" + android:layout_width="@dimen/picture_in_picture_button_width" + android:layout_height="wrap_content" + android:layout_marginStart="@dimen/picture_in_picture_button_start_margin" + android:src="@drawable/pip_ic_close_white" + android:text="@string/pip_close" /> + + <!-- More TvPipMenuActionButtons may be added here at runtime. --> + + </LinearLayout> + +</FrameLayout> diff --git a/libs/WindowManager/Shell/res/layout/tv_pip_control_button.xml b/libs/WindowManager/Shell/res/layout/tv_pip_menu_action_button.xml index 727ac3412a25..5925008e0d08 100644 --- a/libs/WindowManager/Shell/res/layout/tv_pip_control_button.xml +++ b/libs/WindowManager/Shell/res/layout/tv_pip_menu_action_button.xml @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. --> -<!-- Layout for {@link com.android.wm.shell.pip.tv.PipControlButtonView}. --> +<!-- Layout for TvPipMenuActionButton --> <merge xmlns:android="http://schemas.android.com/apk/res/android"> <ImageView android:id="@+id/button" diff --git a/libs/WindowManager/Shell/res/layout/tv_pip_custom_control.xml b/libs/WindowManager/Shell/res/layout/tv_pip_menu_additional_action_button.xml index 452f2cd5ccb6..bf4eb2691ff0 100644 --- a/libs/WindowManager/Shell/res/layout/tv_pip_custom_control.xml +++ b/libs/WindowManager/Shell/res/layout/tv_pip_menu_additional_action_button.xml @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. --> -<com.android.wm.shell.pip.tv.PipControlButtonView +<com.android.wm.shell.pip.tv.TvPipMenuActionButton xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="@dimen/picture_in_picture_button_width" android:layout_height="wrap_content" diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/FullscreenTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/FullscreenTaskListener.java index 63d31182a748..8817f8aaec7c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/FullscreenTaskListener.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/FullscreenTaskListener.java @@ -55,19 +55,16 @@ public class FullscreenTaskListener implements ShellTaskOrganizer.TaskListener { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Fullscreen Task Appeared: #%d", taskInfo.taskId); mLeashByTaskId.put(taskInfo.taskId, leash); + if (Transitions.ENABLE_SHELL_TRANSITIONS) return; final Point positionInParent = taskInfo.positionInParent; mSyncQueue.runInSync(t -> { // Reset several properties back to fullscreen (PiP, for example, leaves all these // properties in a bad state). t.setWindowCrop(leash, null); t.setPosition(leash, positionInParent.x, positionInParent.y); - // TODO(shell-transitions): Eventually set everything in transition so there's no - // SF Transaction here. - if (!Transitions.ENABLE_SHELL_TRANSITIONS) { - t.setAlpha(leash, 1f); - t.setMatrix(leash, 1, 0, 0, 1); - t.show(leash); - } + t.setAlpha(leash, 1f); + t.setMatrix(leash, 1, 0, 0, 1); + t.show(leash); }); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/Transitions.java index 54863d23643a..5213f6c80989 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/Transitions.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/Transitions.java @@ -16,6 +16,7 @@ package com.android.wm.shell; +import static android.view.WindowManager.TRANSIT_CHANGE; import static android.view.WindowManager.TRANSIT_CLOSE; import static android.view.WindowManager.TRANSIT_OPEN; import static android.view.WindowManager.TRANSIT_TO_BACK; @@ -25,15 +26,17 @@ import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPI import android.animation.Animator; import android.animation.ValueAnimator; import android.annotation.NonNull; +import android.annotation.Nullable; +import android.app.ActivityManager; import android.os.IBinder; import android.os.RemoteException; import android.os.SystemProperties; import android.util.ArrayMap; -import android.util.Slog; import android.view.SurfaceControl; import android.view.WindowManager; import android.window.ITransitionPlayer; import android.window.TransitionInfo; +import android.window.WindowContainerTransaction; import android.window.WindowOrganizer; import androidx.annotation.BinderThread; @@ -59,8 +62,16 @@ public class Transitions { private final ShellExecutor mAnimExecutor; private final TransitionPlayerImpl mPlayerImpl; + /** List of possible handlers. Ordered by specificity (eg. tapped back to front). */ + private final ArrayList<TransitionHandler> mHandlers = new ArrayList<>(); + + private static final class ActiveTransition { + ArrayList<Animator> mAnimations = null; + TransitionHandler mFirstHandler = null; + } + /** Keeps track of currently tracked transitions and all the animations associated with each */ - private final ArrayMap<IBinder, ArrayList<Animator>> mActiveTransitions = new ArrayMap<>(); + private final ArrayMap<IBinder, ActiveTransition> mActiveTransitions = new ArrayMap<>(); public Transitions(@NonNull WindowOrganizer organizer, @NonNull TransactionPool pool, @NonNull ShellExecutor mainExecutor, @NonNull ShellExecutor animExecutor) { @@ -75,6 +86,22 @@ public class Transitions { taskOrganizer.registerTransitionPlayer(mPlayerImpl); } + /** + * Adds a handler candidate. + * @see TransitionHandler + */ + public void addHandler(@NonNull TransitionHandler handler) { + mHandlers.add(handler); + } + + public ShellExecutor getMainExecutor() { + return mMainExecutor; + } + + public ShellExecutor getAnimExecutor() { + return mAnimExecutor; + } + // TODO(shell-transitions): real animations private void startExampleAnimation(@NonNull IBinder transition, @NonNull SurfaceControl leash, boolean show) { @@ -93,7 +120,7 @@ public class Transitions { transaction.apply(); mTransactionPool.release(transaction); mMainExecutor.execute(() -> { - mActiveTransitions.get(transition).remove(va); + mActiveTransitions.get(transition).mAnimations.remove(va); onFinish(transition); }); }; @@ -114,30 +141,23 @@ public class Transitions { @Override public void onAnimationRepeat(Animator animation) { } }); - mActiveTransitions.get(transition).add(va); + mActiveTransitions.get(transition).mAnimations.add(va); mAnimExecutor.execute(va::start); } - private static boolean isOpeningType(@WindowManager.TransitionType int type) { + /** @return true if the transition was triggered by opening something vs closing something */ + public static boolean isOpeningType(@WindowManager.TransitionType int type) { return type == TRANSIT_OPEN || type == TRANSIT_TO_FRONT || type == WindowManager.TRANSIT_KEYGUARD_GOING_AWAY; } - private void onTransitionReady(@NonNull IBinder transitionToken, @NonNull TransitionInfo info, + /** + * Reparents all participants into a shared parent and orders them based on: the global transit + * type, their transit mode, and their destination z-order. + */ + private static void setupStartState(@NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t) { - ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "onTransitionReady %s: %s", - transitionToken, info); - // start task - if (!mActiveTransitions.containsKey(transitionToken)) { - Slog.e(TAG, "Got transitionReady for non-active transition " + transitionToken - + " expecting one of " + mActiveTransitions.keySet()); - } - if (mActiveTransitions.get(transitionToken) != null) { - throw new IllegalStateException("Got a duplicate onTransitionReady call for " - + transitionToken); - } - mActiveTransitions.put(transitionToken, new ArrayList<>()); boolean isOpening = isOpeningType(info.getType()); if (info.getRootLeash().isValid()) { t.show(info.getRootLeash()); @@ -148,24 +168,26 @@ public class Transitions { final SurfaceControl leash = change.getLeash(); final int mode = info.getChanges().get(i).getMode(); - // Don't animate anything with an animating parent + // Don't move anything with an animating parent if (change.getParent() != null) { - if (mode == TRANSIT_OPEN || mode == TRANSIT_TO_FRONT) { + if (mode == TRANSIT_OPEN || mode == TRANSIT_TO_FRONT || mode == TRANSIT_CHANGE) { t.show(leash); t.setMatrix(leash, 1, 0, 0, 1); + t.setAlpha(leash, 1.f); + t.setPosition(leash, change.getEndRelOffset().x, change.getEndRelOffset().y); } continue; } t.reparent(leash, info.getRootLeash()); - t.setPosition(leash, change.getEndAbsBounds().left - info.getRootOffset().x, - change.getEndAbsBounds().top - info.getRootOffset().y); + t.setPosition(leash, change.getStartAbsBounds().left - info.getRootOffset().x, + change.getStartAbsBounds().top - info.getRootOffset().y); // Put all the OPEN/SHOW on top if (mode == TRANSIT_OPEN || mode == TRANSIT_TO_FRONT) { t.show(leash); t.setMatrix(leash, 1, 0, 0, 1); if (isOpening) { - // put on top and fade in + // put on top with 0 alpha t.setLayer(leash, info.getChanges().size() - i); if ((change.getFlags() & FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT) != 0) { // This received a transferred starting window, so make it immediately @@ -173,47 +195,155 @@ public class Transitions { t.setAlpha(leash, 1.f); } else { t.setAlpha(leash, 0.f); - startExampleAnimation(transitionToken, leash, true /* show */); } } else { - // put on bottom and leave it visible without fade + // put on bottom and leave it visible t.setLayer(leash, -i); t.setAlpha(leash, 1.f); } } else if (mode == TRANSIT_CLOSE || mode == TRANSIT_TO_BACK) { if (isOpening) { - // put on bottom and leave visible without fade + // put on bottom and leave visible t.setLayer(leash, -i); } else { - // put on top and fade out + // put on top t.setLayer(leash, info.getChanges().size() - i); - startExampleAnimation(transitionToken, leash, false /* show */); } - } else { + } else { // CHANGE t.setLayer(leash, info.getChanges().size() - i); } } + } + + private void onTransitionReady(@NonNull IBinder transitionToken, @NonNull TransitionInfo info, + @NonNull SurfaceControl.Transaction t) { + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "onTransitionReady %s: %s", + transitionToken, info); + final ActiveTransition active = mActiveTransitions.get(transitionToken); + if (active == null) { + throw new IllegalStateException("Got transitionReady for non-active transition " + + transitionToken + ". expecting one of " + mActiveTransitions.keySet()); + } + if (active.mAnimations != null) { + throw new IllegalStateException("Got a duplicate onTransitionReady call for " + + transitionToken); + } + if (!info.getRootLeash().isValid()) { + // Invalid root-leash implies that the transition is empty/no-op, so just do + // housekeeping and return. + t.apply(); + onFinish(transitionToken); + return; + } + + setupStartState(info, t); + + final Runnable finishRunnable = () -> onFinish(transitionToken); + // If a handler chose to uniquely run this animation, try delegating to it. + if (active.mFirstHandler != null && active.mFirstHandler.startAnimation( + transitionToken, info, t, finishRunnable)) { + return; + } + // Otherwise give every other handler a chance (in order) + for (int i = mHandlers.size() - 1; i >= 0; --i) { + if (mHandlers.get(i) == active.mFirstHandler) continue; + if (mHandlers.get(i).startAnimation(transitionToken, info, t, finishRunnable)) { + return; + } + } + + // No handler chose to perform this animation, so fall-back to the + // default animation handling. + final boolean isOpening = isOpeningType(info.getType()); + active.mAnimations = new ArrayList<>(); // Play fade animations + for (int i = info.getChanges().size() - 1; i >= 0; --i) { + final TransitionInfo.Change change = info.getChanges().get(i); + + // Don't animate anything with an animating parent + if (change.getParent() != null) continue; + + final int mode = info.getChanges().get(i).getMode(); + if (isOpening && (mode == TRANSIT_OPEN || mode == TRANSIT_TO_FRONT)) { + if ((change.getFlags() & FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT) != 0) { + // This received a transferred starting window, so don't animate + continue; + } + // fade in + startExampleAnimation(transitionToken, change.getLeash(), true /* show */); + } else if (!isOpening && (mode == TRANSIT_CLOSE || mode == TRANSIT_TO_BACK)) { + // fade out + startExampleAnimation(transitionToken, change.getLeash(), false /* show */); + } + } t.apply(); onFinish(transitionToken); } private void onFinish(IBinder transition) { - if (!mActiveTransitions.get(transition).isEmpty()) return; + final ActiveTransition active = mActiveTransitions.get(transition); + if (active.mAnimations != null && !active.mAnimations.isEmpty()) return; ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Transition animations finished, notifying core %s", transition); mActiveTransitions.remove(transition); mOrganizer.finishTransition(transition, null, null); } - private void requestStartTransition(int type, @NonNull IBinder transitionToken) { + private void requestStartTransition(int type, @NonNull IBinder transitionToken, + @Nullable ActivityManager.RunningTaskInfo triggerTask) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Transition requested: type=%d %s", type, transitionToken); if (mActiveTransitions.containsKey(transitionToken)) { throw new RuntimeException("Transition already started " + transitionToken); } - IBinder transition = mOrganizer.startTransition(type, transitionToken, null /* wct */); - mActiveTransitions.put(transition, null); + final ActiveTransition active = new ActiveTransition(); + WindowContainerTransaction wct = null; + for (int i = mHandlers.size() - 1; i >= 0; --i) { + wct = mHandlers.get(i).handleRequest(type, transitionToken, triggerTask); + if (wct != null) { + active.mFirstHandler = mHandlers.get(i); + break; + } + } + IBinder transition = mOrganizer.startTransition(type, transitionToken, wct); + mActiveTransitions.put(transition, active); + } + + /** Start a new transition directly. */ + public IBinder startTransition(@WindowManager.TransitionType int type, + @NonNull WindowContainerTransaction wct, @Nullable TransitionHandler handler) { + final ActiveTransition active = new ActiveTransition(); + active.mFirstHandler = handler; + IBinder transition = mOrganizer.startTransition(type, null /* token */, wct); + mActiveTransitions.put(transition, active); + return transition; + } + + /** + * Interface for something which can handle a subset of transitions. + */ + public interface TransitionHandler { + /** + * Starts a transition animation. This is always called if handleRequest returned non-null + * for a particular transition. Otherwise, it is only called if no other handler before + * it handled the transition. + * + * @return true if transition was handled, false if not (falls-back to default). + */ + boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info, + @NonNull SurfaceControl.Transaction t, @NonNull Runnable finishCallback); + + /** + * Potentially handles a startTransition request. + * @param type The transition type + * @param triggerTask The task which triggered this transition request. + * @return WCT to apply with transition-start or null if this handler isn't handling + * the request. + */ + @Nullable + WindowContainerTransaction handleRequest(@WindowManager.TransitionType int type, + @NonNull IBinder transition, + @Nullable ActivityManager.RunningTaskInfo triggerTask); } @BinderThread @@ -227,9 +357,10 @@ public class Transitions { } @Override - public void requestStartTransition(int i, IBinder iBinder) throws RemoteException { + public void requestStartTransition(int i, IBinder iBinder, + ActivityManager.RunningTaskInfo runningTaskInfo) throws RemoteException { mMainExecutor.execute(() -> { - Transitions.this.requestStartTransition(i, iBinder); + Transitions.this.requestStartTransition(i, iBinder, runningTaskInfo); }); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java index 7f3389599a51..86da2b502870 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java @@ -471,13 +471,12 @@ public class BubbleExpandedView extends LinearLayout { mIsOverflow = overflow; Intent target = new Intent(mContext, BubbleOverflowActivity.class); + target.addFlags(FLAG_ACTIVITY_NEW_DOCUMENT | FLAG_ACTIVITY_MULTIPLE_TASK); Bundle extras = new Bundle(); extras.putBinder(EXTRA_BUBBLE_CONTROLLER, ObjectWrapper.wrap(mController)); target.putExtras(extras); - // TODO(b/175352055) Please replace FLAG_MUTABLE_UNAUDITED below - // with either FLAG_IMMUTABLE (recommended) or FLAG_MUTABLE. mPendingIntent = PendingIntent.getActivity(mContext, 0 /* requestCode */, - target, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE_UNAUDITED); + target, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE); mSettingsIcon.setVisibility(GONE); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenController.java index 6b79a3661bc0..789bd1a90731 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenController.java @@ -44,6 +44,7 @@ import android.window.WindowContainerTransaction; import com.android.internal.policy.DividerSnapAlgorithm; import com.android.wm.shell.R; import com.android.wm.shell.ShellTaskOrganizer; +import com.android.wm.shell.Transitions; import com.android.wm.shell.common.DisplayChangeController; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.DisplayImeController; @@ -113,7 +114,7 @@ public class LegacySplitScreenController implements LegacySplitScreen, DisplayController displayController, SystemWindows systemWindows, DisplayImeController imeController, Handler handler, TransactionPool transactionPool, ShellTaskOrganizer shellTaskOrganizer, SyncTransactionQueue syncQueue, - TaskStackListenerImpl taskStackListener) { + TaskStackListenerImpl taskStackListener, Transitions transitions) { mContext = context; mDisplayController = displayController; mSystemWindows = systemWindows; @@ -123,7 +124,8 @@ public class LegacySplitScreenController implements LegacySplitScreen, mTransactionPool = transactionPool; mWindowManagerProxy = new WindowManagerProxy(syncQueue, shellTaskOrganizer); mTaskOrganizer = shellTaskOrganizer; - mSplits = new LegacySplitScreenTaskListener(this, shellTaskOrganizer, syncQueue); + mSplits = new LegacySplitScreenTaskListener(this, shellTaskOrganizer, transitions, + syncQueue); mImePositionProcessor = new DividerImeController(mSplits, mTransactionPool, mHandler, shellTaskOrganizer); mRotationController = @@ -553,8 +555,36 @@ public class LegacySplitScreenController implements LegacySplitScreen, mHomeStackResizable = mWindowManagerProxy.applyEnterSplit(mSplits, mSplitLayout); } + void prepareEnterSplitTransition(WindowContainerTransaction outWct) { + // Set resizable directly here because buildEnterSplit already resizes home stack. + mHomeStackResizable = mWindowManagerProxy.buildEnterSplit(outWct, mSplits, mSplitLayout); + } + + void finishEnterSplitTransition(boolean minimized) { + update(mDisplayController.getDisplayContext( + mContext.getDisplayId()).getResources().getConfiguration()); + if (minimized) { + ensureMinimizedSplit(); + } else { + ensureNormalSplit(); + } + } + void startDismissSplit(boolean toPrimaryTask) { + startDismissSplit(toPrimaryTask, false /* snapped */); + } + + void startDismissSplit(boolean toPrimaryTask, boolean snapped) { + if (Transitions.ENABLE_SHELL_TRANSITIONS) { + mSplits.getSplitTransitions().dismissSplit( + mSplits, mSplitLayout, !toPrimaryTask, snapped); + } else { mWindowManagerProxy.applyDismissSplit(mSplits, mSplitLayout, !toPrimaryTask); + onDismissSplit(); + } + } + + void onDismissSplit() { updateVisibility(false /* visible */); mMinimized = false; // Resets divider bar position to undefined, so new divider bar will apply default position diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenTaskListener.java index 02c82dee8ca4..8c624cdb1063 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenTaskListener.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenTaskListener.java @@ -32,6 +32,7 @@ import android.util.Log; import android.util.SparseArray; import android.view.SurfaceControl; import android.view.SurfaceSession; +import android.window.TaskOrganizer; import androidx.annotation.NonNull; @@ -63,11 +64,17 @@ class LegacySplitScreenTaskListener implements ShellTaskOrganizer.TaskListener { final SurfaceSession mSurfaceSession = new SurfaceSession(); + private final SplitScreenTransitions mSplitTransitions; + LegacySplitScreenTaskListener(LegacySplitScreenController splitScreenController, ShellTaskOrganizer shellTaskOrganizer, + Transitions transitions, SyncTransactionQueue syncQueue) { mSplitScreenController = splitScreenController; mTaskOrganizer = shellTaskOrganizer; + mSplitTransitions = new SplitScreenTransitions(splitScreenController.mTransactionPool, + transitions, mSplitScreenController, this); + transitions.addHandler(mSplitTransitions); mSyncQueue = syncQueue; } @@ -98,6 +105,14 @@ class LegacySplitScreenTaskListener implements ShellTaskOrganizer.TaskListener { mSplitScreenController.mTransactionPool.release(t); } + TaskOrganizer getTaskOrganizer() { + return mTaskOrganizer; + } + + SplitScreenTransitions getSplitTransitions() { + return mSplitTransitions; + } + @Override public void onTaskAppeared(RunningTaskInfo taskInfo, SurfaceControl leash) { synchronized (this) { @@ -195,10 +210,12 @@ class LegacySplitScreenTaskListener implements ShellTaskOrganizer.TaskListener { private void handleChildTaskAppeared(RunningTaskInfo taskInfo, SurfaceControl leash) { mLeashByTaskId.put(taskInfo.taskId, leash); + if (Transitions.ENABLE_SHELL_TRANSITIONS) return; updateChildTaskSurface(taskInfo, leash, true /* firstAppeared */); } private void handleChildTaskChanged(RunningTaskInfo taskInfo) { + if (Transitions.ENABLE_SHELL_TRANSITIONS) return; final SurfaceControl leash = mLeashByTaskId.get(taskInfo.taskId); updateChildTaskSurface(taskInfo, leash, false /* firstAppeared */); } @@ -241,14 +258,15 @@ class LegacySplitScreenTaskListener implements ShellTaskOrganizer.TaskListener { } else if (info.token.asBinder() == mSecondary.token.asBinder()) { mSecondary = info; } + if (DEBUG) { + Log.d(TAG, "onTaskInfoChanged " + mPrimary + " " + mSecondary); + } + if (Transitions.ENABLE_SHELL_TRANSITIONS) return; final boolean primaryIsEmpty = mPrimary.topActivityType == ACTIVITY_TYPE_UNDEFINED; final boolean secondaryIsEmpty = mSecondary.topActivityType == ACTIVITY_TYPE_UNDEFINED; final boolean secondaryImpliesMinimize = mSecondary.topActivityType == ACTIVITY_TYPE_HOME || (mSecondary.topActivityType == ACTIVITY_TYPE_RECENTS && mSplitScreenController.isHomeStackResizable()); - if (DEBUG) { - Log.d(TAG, "onTaskInfoChanged " + mPrimary + " " + mSecondary); - } if (primaryIsEmpty == primaryWasEmpty && secondaryWasEmpty == secondaryIsEmpty && secondaryImpliedMinimize == secondaryImpliesMinimize) { // No relevant changes diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/SplitScreenTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/SplitScreenTransitions.java new file mode 100644 index 000000000000..93520c0b59de --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/SplitScreenTransitions.java @@ -0,0 +1,342 @@ +/* + * 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.wm.shell.legacysplitscreen; + +import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; +import static android.view.WindowManager.TRANSIT_CHANGE; +import static android.view.WindowManager.TRANSIT_CLOSE; +import static android.view.WindowManager.TRANSIT_FIRST_CUSTOM; +import static android.view.WindowManager.TRANSIT_OPEN; +import static android.view.WindowManager.TRANSIT_TO_BACK; +import static android.view.WindowManager.TRANSIT_TO_FRONT; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ValueAnimator; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.app.ActivityManager; +import android.app.WindowConfiguration; +import android.graphics.Rect; +import android.os.IBinder; +import android.view.SurfaceControl; +import android.view.WindowManager; +import android.window.TransitionInfo; +import android.window.WindowContainerTransaction; + +import com.android.wm.shell.Transitions; +import com.android.wm.shell.common.TransactionPool; +import com.android.wm.shell.common.annotations.ExternalThread; + +import java.util.ArrayList; + +/** Plays transition animations for split-screen */ +public class SplitScreenTransitions implements Transitions.TransitionHandler { + private static final String TAG = "SplitScreenTransitions"; + + public static final int TRANSIT_SPLIT_DISMISS_SNAP = TRANSIT_FIRST_CUSTOM + 10; + + private final TransactionPool mTransactionPool; + private final Transitions mTransitions; + private final LegacySplitScreenController mSplitScreen; + private final LegacySplitScreenTaskListener mListener; + + private IBinder mPendingDismiss = null; + private boolean mDismissFromSnap = false; + private IBinder mPendingEnter = null; + private IBinder mAnimatingTransition = null; + + /** Keeps track of currently running animations */ + private final ArrayList<Animator> mAnimations = new ArrayList<>(); + + private Runnable mFinishCallback = null; + private SurfaceControl.Transaction mFinishTransaction; + + SplitScreenTransitions(@NonNull TransactionPool pool, @NonNull Transitions transitions, + @NonNull LegacySplitScreenController splitScreen, + @NonNull LegacySplitScreenTaskListener listener) { + mTransactionPool = pool; + mTransitions = transitions; + mSplitScreen = splitScreen; + mListener = listener; + } + + @Override + public WindowContainerTransaction handleRequest(@WindowManager.TransitionType int type, + @NonNull IBinder transition, @Nullable ActivityManager.RunningTaskInfo triggerTask) { + WindowContainerTransaction out = null; + if (mSplitScreen.isDividerVisible()) { + // try to handle everything while in split-screen + out = new WindowContainerTransaction(); + if (triggerTask != null) { + final boolean shouldDismiss = + // if we close the primary-docked task, then leave split-screen since there + // is nothing behind it. + ((type == TRANSIT_CLOSE || type == TRANSIT_TO_BACK) + && triggerTask.parentTaskId == mListener.mPrimary.taskId) + // if a non-resizable is launched, we also need to leave split-screen. + || ((type == TRANSIT_OPEN || type == TRANSIT_TO_FRONT) + && !triggerTask.isResizeable); + // In both cases, dismiss the primary + if (shouldDismiss) { + WindowManagerProxy.buildDismissSplit(out, mListener, + mSplitScreen.getSplitLayout(), true /* dismiss */); + if (type == TRANSIT_OPEN || type == TRANSIT_TO_FRONT) { + out.reorder(triggerTask.token, true /* onTop */); + } + mPendingDismiss = transition; + } + } + } else if (triggerTask != null) { + // Not in split mode, so look for an open with a trigger task. + if ((type == TRANSIT_OPEN || type == TRANSIT_TO_FRONT) + && triggerTask.configuration.windowConfiguration.getWindowingMode() + == WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) { + out = new WindowContainerTransaction(); + mSplitScreen.prepareEnterSplitTransition(out); + mPendingEnter = transition; + } + } + return out; + } + + // TODO(shell-transitions): real animations + private void startExampleAnimation(@NonNull SurfaceControl leash, boolean show) { + final float end = show ? 1.f : 0.f; + final float start = 1.f - end; + final SurfaceControl.Transaction transaction = mTransactionPool.acquire(); + final ValueAnimator va = ValueAnimator.ofFloat(start, end); + va.setDuration(500); + va.addUpdateListener(animation -> { + float fraction = animation.getAnimatedFraction(); + transaction.setAlpha(leash, start * (1.f - fraction) + end * fraction); + transaction.apply(); + }); + final Runnable finisher = () -> { + transaction.setAlpha(leash, end); + transaction.apply(); + mTransactionPool.release(transaction); + mTransitions.getMainExecutor().execute(() -> { + mAnimations.remove(va); + onFinish(); + }); + }; + va.addListener(new Animator.AnimatorListener() { + @Override + public void onAnimationStart(Animator animation) { } + + @Override + public void onAnimationEnd(Animator animation) { + finisher.run(); + } + + @Override + public void onAnimationCancel(Animator animation) { + finisher.run(); + } + + @Override + public void onAnimationRepeat(Animator animation) { } + }); + mAnimations.add(va); + mTransitions.getAnimExecutor().execute(va::start); + } + + // TODO(shell-transitions): real animations + private void startExampleResizeAnimation(@NonNull SurfaceControl leash, + @NonNull Rect startBounds, @NonNull Rect endBounds) { + final SurfaceControl.Transaction transaction = mTransactionPool.acquire(); + final ValueAnimator va = ValueAnimator.ofFloat(0.f, 1.f); + va.setDuration(500); + va.addUpdateListener(animation -> { + float fraction = animation.getAnimatedFraction(); + transaction.setWindowCrop(leash, + (int) (startBounds.width() * (1.f - fraction) + endBounds.width() * fraction), + (int) (startBounds.height() * (1.f - fraction) + + endBounds.height() * fraction)); + transaction.setPosition(leash, + startBounds.left * (1.f - fraction) + endBounds.left * fraction, + startBounds.top * (1.f - fraction) + endBounds.top * fraction); + transaction.apply(); + }); + final Runnable finisher = () -> { + transaction.setWindowCrop(leash, 0, 0); + transaction.setPosition(leash, endBounds.left, endBounds.top); + transaction.apply(); + mTransactionPool.release(transaction); + mTransitions.getMainExecutor().execute(() -> { + mAnimations.remove(va); + onFinish(); + }); + }; + va.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + finisher.run(); + } + + @Override + public void onAnimationCancel(Animator animation) { + finisher.run(); + } + }); + mAnimations.add(va); + mTransitions.getAnimExecutor().execute(va::start); + } + + @Override + public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info, + @NonNull SurfaceControl.Transaction t, @NonNull Runnable finishCallback) { + if (transition != mPendingDismiss && transition != mPendingEnter) { + // If we're not in split-mode, just abort + if (!mSplitScreen.isDividerVisible()) return false; + // Check to see if HOME is involved + for (int i = info.getChanges().size() - 1; i >= 0; --i) { + final TransitionInfo.Change change = info.getChanges().get(i); + if (change.getTaskInfo() == null + || change.getTaskInfo().getActivityType() != ACTIVITY_TYPE_HOME) continue; + if (change.getMode() == TRANSIT_OPEN || change.getMode() == TRANSIT_TO_FRONT) { + mSplitScreen.ensureMinimizedSplit(); + } else if (change.getMode() == TRANSIT_CLOSE + || change.getMode() == TRANSIT_TO_BACK) { + mSplitScreen.ensureNormalSplit(); + } + } + // Use normal animations. + return false; + } + + mFinishCallback = finishCallback; + mFinishTransaction = mTransactionPool.acquire(); + mAnimatingTransition = transition; + + // Play fade animations + for (int i = info.getChanges().size() - 1; i >= 0; --i) { + final TransitionInfo.Change change = info.getChanges().get(i); + final SurfaceControl leash = change.getLeash(); + final int mode = info.getChanges().get(i).getMode(); + + if (mode == TRANSIT_CHANGE) { + if (change.getParent() != null) { + // This is probably reparented, so we want the parent to be immediately visible + final TransitionInfo.Change parentChange = info.getChange(change.getParent()); + t.show(parentChange.getLeash()); + t.setAlpha(parentChange.getLeash(), 1.f); + // and then animate this layer outside the parent (since, for example, this is + // the home task animating from fullscreen to part-screen). + t.reparent(leash, info.getRootLeash()); + t.setLayer(leash, info.getChanges().size() - i); + // build the finish reparent/reposition + mFinishTransaction.reparent(leash, parentChange.getLeash()); + mFinishTransaction.setPosition(leash, + change.getEndRelOffset().x, change.getEndRelOffset().y); + } + // TODO(shell-transitions): screenshot here + final Rect startBounds = new Rect(change.getStartAbsBounds()); + final boolean isHome = change.getTaskInfo() != null + && change.getTaskInfo().getActivityType() == ACTIVITY_TYPE_HOME; + if (mPendingDismiss == transition && mDismissFromSnap && !isHome) { + // Home is special since it doesn't move during fling. Everything else, though, + // when dismissing from snap, the top/left is at 0,0. + startBounds.offsetTo(0, 0); + } + final Rect endBounds = new Rect(change.getEndAbsBounds()); + startBounds.offset(-info.getRootOffset().x, -info.getRootOffset().y); + endBounds.offset(-info.getRootOffset().x, -info.getRootOffset().y); + startExampleResizeAnimation(leash, startBounds, endBounds); + } + if (change.getParent() != null) { + continue; + } + + if (transition == mPendingEnter + && mListener.mPrimary.token.equals(change.getContainer()) + || mListener.mSecondary.token.equals(change.getContainer())) { + t.setWindowCrop(leash, change.getStartAbsBounds().width(), + change.getStartAbsBounds().height()); + if (mListener.mPrimary.token.equals(change.getContainer())) { + // Move layer to top since we want it above the oversized home task during + // animation even though home task is on top in hierarchy. + t.setLayer(leash, info.getChanges().size() + 1); + } + } + boolean isOpening = Transitions.isOpeningType(info.getType()); + if (isOpening && (mode == TRANSIT_OPEN || mode == TRANSIT_TO_FRONT)) { + // fade in + startExampleAnimation(leash, true /* show */); + } else if (!isOpening && (mode == TRANSIT_CLOSE || mode == TRANSIT_TO_BACK)) { + // fade out + if (transition == mPendingDismiss && mDismissFromSnap) { + // Dismissing via snap-to-top/bottom means that the dismissed task is already + // not-visible (usually cropped to oblivion) so immediately set its alpha to 0 + // and don't animate it so it doesn't pop-in when reparented. + t.setAlpha(leash, 0.f); + } else { + startExampleAnimation(leash, false /* show */); + } + } + } + if (transition == mPendingEnter) { + // If entering, check if we should enter into minimized or normal split + boolean homeIsVisible = false; + for (int i = info.getChanges().size() - 1; i >= 0; --i) { + final TransitionInfo.Change change = info.getChanges().get(i); + if (change.getTaskInfo() == null + || change.getTaskInfo().getActivityType() != ACTIVITY_TYPE_HOME) { + continue; + } + homeIsVisible = change.getMode() == TRANSIT_OPEN + || change.getMode() == TRANSIT_TO_FRONT + || change.getMode() == TRANSIT_CHANGE; + break; + } + mSplitScreen.finishEnterSplitTransition(homeIsVisible); + } + t.apply(); + onFinish(); + return true; + } + + @ExternalThread + void dismissSplit(LegacySplitScreenTaskListener tiles, LegacySplitDisplayLayout layout, + boolean dismissOrMaximize, boolean snapped) { + final WindowContainerTransaction wct = new WindowContainerTransaction(); + WindowManagerProxy.buildDismissSplit(wct, tiles, layout, dismissOrMaximize); + mTransitions.getMainExecutor().execute(() -> { + mDismissFromSnap = snapped; + mPendingDismiss = mTransitions.startTransition(TRANSIT_SPLIT_DISMISS_SNAP, wct, this); + }); + } + + private void onFinish() { + if (!mAnimations.isEmpty()) return; + mFinishTransaction.apply(); + mTransactionPool.release(mFinishTransaction); + mFinishTransaction = null; + mFinishCallback.run(); + mFinishCallback = null; + if (mAnimatingTransition == mPendingEnter) { + mPendingEnter = null; + } + if (mAnimatingTransition == mPendingDismiss) { + mSplitScreen.onDismissSplit(); + mPendingDismiss = null; + } + mDismissFromSnap = false; + mAnimatingTransition = null; + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/WindowManagerProxy.java b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/WindowManagerProxy.java index 68da35d9c4b1..90a8de02deb8 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/WindowManagerProxy.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/WindowManagerProxy.java @@ -39,6 +39,7 @@ import android.window.WindowContainerTransaction; import android.window.WindowOrganizer; import com.android.internal.annotations.GuardedBy; +import com.android.wm.shell.Transitions; import com.android.wm.shell.common.SyncTransactionQueue; import java.util.ArrayList; @@ -90,7 +91,11 @@ class WindowManagerProxy { void dismissOrMaximizeDocked(final LegacySplitScreenTaskListener tiles, LegacySplitDisplayLayout layout, final boolean dismissOrMaximize) { - mExecutor.execute(() -> applyDismissSplit(tiles, layout, dismissOrMaximize)); + if (Transitions.ENABLE_SHELL_TRANSITIONS) { + tiles.mSplitScreenController.startDismissSplit(!dismissOrMaximize, true /* snapped */); + } else { + mExecutor.execute(() -> applyDismissSplit(tiles, layout, dismissOrMaximize)); + } } public void setResizing(final boolean resizing) { @@ -181,6 +186,18 @@ class WindowManagerProxy { return isHomeResizable; } + /** @see #buildEnterSplit */ + boolean applyEnterSplit(LegacySplitScreenTaskListener tiles, LegacySplitDisplayLayout layout) { + // Set launchtile first so that any stack created after + // getAllRootTaskInfos and before reparent (even if unlikely) are placed + // correctly. + mTaskOrganizer.setLaunchRoot(DEFAULT_DISPLAY, tiles.mSecondary.token); + WindowContainerTransaction wct = new WindowContainerTransaction(); + final boolean isHomeResizable = buildEnterSplit(wct, tiles, layout); + applySyncTransaction(wct); + return isHomeResizable; + } + /** * Finishes entering split-screen by reparenting all FULLSCREEN tasks into the secondary split. * This assumes there is already something in the primary split since that is usually what @@ -189,14 +206,10 @@ class WindowManagerProxy { * * @return whether the home stack is resizable */ - boolean applyEnterSplit(LegacySplitScreenTaskListener tiles, LegacySplitDisplayLayout layout) { - // Set launchtile first so that any stack created after - // getAllRootTaskInfos and before reparent (even if unlikely) are placed - // correctly. - mTaskOrganizer.setLaunchRoot(DEFAULT_DISPLAY, tiles.mSecondary.token); + boolean buildEnterSplit(WindowContainerTransaction outWct, LegacySplitScreenTaskListener tiles, + LegacySplitDisplayLayout layout) { List<ActivityManager.RunningTaskInfo> rootTasks = mTaskOrganizer.getRootTasks(DEFAULT_DISPLAY, null /* activityTypes */); - WindowContainerTransaction wct = new WindowContainerTransaction(); if (rootTasks.isEmpty()) { return false; } @@ -215,48 +228,60 @@ class WindowManagerProxy { // Since this iterates from bottom to top, update topHomeTask for every fullscreen task // so it will be left with the status of the top one. topHomeTask = isHomeOrRecentTask(rootTask) ? rootTask : null; - wct.reparent(rootTask.token, tiles.mSecondary.token, true /* onTop */); + outWct.reparent(rootTask.token, tiles.mSecondary.token, true /* onTop */); } // Move the secondary split-forward. - wct.reorder(tiles.mSecondary.token, true /* onTop */); - boolean isHomeResizable = applyHomeTasksMinimized(layout, null /* parent */, wct); - if (topHomeTask != null) { + outWct.reorder(tiles.mSecondary.token, true /* onTop */); + boolean isHomeResizable = applyHomeTasksMinimized(layout, null /* parent */, + outWct); + if (topHomeTask != null && !Transitions.ENABLE_SHELL_TRANSITIONS) { // Translate/update-crop of secondary out-of-band with sync transaction -- Until BALST // is enabled, this temporarily syncs the home surface position with offset until // sync transaction finishes. - wct.setBoundsChangeTransaction(topHomeTask.token, tiles.mHomeBounds); + outWct.setBoundsChangeTransaction(topHomeTask.token, tiles.mHomeBounds); } - applySyncTransaction(wct); return isHomeResizable; } - boolean isHomeOrRecentTask(ActivityManager.RunningTaskInfo ti) { + static boolean isHomeOrRecentTask(ActivityManager.RunningTaskInfo ti) { final int atype = ti.getActivityType(); return atype == ACTIVITY_TYPE_HOME || atype == ACTIVITY_TYPE_RECENTS; } + /** @see #buildDismissSplit */ + void applyDismissSplit(LegacySplitScreenTaskListener tiles, LegacySplitDisplayLayout layout, + boolean dismissOrMaximize) { + // Set launch root first so that any task created after getChildContainers and + // before reparent (pretty unlikely) are put into fullscreen. + mTaskOrganizer.setLaunchRoot(Display.DEFAULT_DISPLAY, null); + // TODO(task-org): Once task-org is more complete, consider using Appeared/Vanished + // plus specific APIs to clean this up. + final WindowContainerTransaction wct = new WindowContainerTransaction(); + buildDismissSplit(wct, tiles, layout, dismissOrMaximize); + applySyncTransaction(wct); + } + /** * Reparents all tile members back to their display and resets home task override bounds. * @param dismissOrMaximize When {@code true} this resolves the split by closing the primary * split (thus resulting in the top of the secondary split becoming * fullscreen. {@code false} resolves the other way. */ - void applyDismissSplit(LegacySplitScreenTaskListener tiles, LegacySplitDisplayLayout layout, + static void buildDismissSplit(WindowContainerTransaction outWct, + LegacySplitScreenTaskListener tiles, LegacySplitDisplayLayout layout, boolean dismissOrMaximize) { - // Set launch root first so that any task created after getChildContainers and - // before reparent (pretty unlikely) are put into fullscreen. - mTaskOrganizer.setLaunchRoot(Display.DEFAULT_DISPLAY, null); // TODO(task-org): Once task-org is more complete, consider using Appeared/Vanished // plus specific APIs to clean this up. + final TaskOrganizer taskOrg = tiles.getTaskOrganizer(); List<ActivityManager.RunningTaskInfo> primaryChildren = - mTaskOrganizer.getChildTasks(tiles.mPrimary.token, null /* activityTypes */); + taskOrg.getChildTasks(tiles.mPrimary.token, null /* activityTypes */); List<ActivityManager.RunningTaskInfo> secondaryChildren = - mTaskOrganizer.getChildTasks(tiles.mSecondary.token, null /* activityTypes */); + taskOrg.getChildTasks(tiles.mSecondary.token, null /* activityTypes */); // In some cases (eg. non-resizable is launched), system-server will leave split-screen. // as a result, the above will not capture any tasks; yet, we need to clean-up the // home task bounds. List<ActivityManager.RunningTaskInfo> freeHomeAndRecents = - mTaskOrganizer.getRootTasks(DEFAULT_DISPLAY, HOME_AND_RECENTS); + taskOrg.getRootTasks(DEFAULT_DISPLAY, HOME_AND_RECENTS); // Filter out the root split tasks freeHomeAndRecents.removeIf(p -> p.token.equals(tiles.mSecondary.token) || p.token.equals(tiles.mPrimary.token)); @@ -265,11 +290,10 @@ class WindowManagerProxy { && freeHomeAndRecents.isEmpty()) { return; } - WindowContainerTransaction wct = new WindowContainerTransaction(); if (dismissOrMaximize) { // Dismissing, so move all primary split tasks first for (int i = primaryChildren.size() - 1; i >= 0; --i) { - wct.reparent(primaryChildren.get(i).token, null /* parent */, + outWct.reparent(primaryChildren.get(i).token, null /* parent */, true /* onTop */); } boolean homeOnTop = false; @@ -277,16 +301,16 @@ class WindowManagerProxy { // order within the secondary split. for (int i = secondaryChildren.size() - 1; i >= 0; --i) { final ActivityManager.RunningTaskInfo ti = secondaryChildren.get(i); - wct.reparent(ti.token, null /* parent */, true /* onTop */); + outWct.reparent(ti.token, null /* parent */, true /* onTop */); if (isHomeOrRecentTask(ti)) { - wct.setBounds(ti.token, null); - wct.setWindowingMode(ti.token, WINDOWING_MODE_UNDEFINED); + outWct.setBounds(ti.token, null); + outWct.setWindowingMode(ti.token, WINDOWING_MODE_UNDEFINED); if (i == 0) { homeOnTop = true; } } } - if (homeOnTop) { + if (homeOnTop && !Transitions.ENABLE_SHELL_TRANSITIONS) { // Translate/update-crop of secondary out-of-band with sync transaction -- instead // play this in sync with new home-app frame because until BALST is enabled this // shows up on screen before the syncTransaction returns. @@ -304,7 +328,7 @@ class WindowManagerProxy { layout.mDisplayLayout.height()); crop.offset(-posX, -posY); sft.setWindowCrop(tiles.mSecondarySurface, crop); - wct.setBoundsChangeTransaction(tiles.mSecondary.token, sft); + outWct.setBoundsChangeTransaction(tiles.mSecondary.token, sft); } } else { // Maximize, so move non-home secondary split first @@ -312,7 +336,7 @@ class WindowManagerProxy { if (isHomeOrRecentTask(secondaryChildren.get(i))) { continue; } - wct.reparent(secondaryChildren.get(i).token, null /* parent */, + outWct.reparent(secondaryChildren.get(i).token, null /* parent */, true /* onTop */); } // Find and place home tasks in-between. This simulates the fact that there was @@ -320,24 +344,23 @@ class WindowManagerProxy { for (int i = secondaryChildren.size() - 1; i >= 0; --i) { final ActivityManager.RunningTaskInfo ti = secondaryChildren.get(i); if (isHomeOrRecentTask(ti)) { - wct.reparent(ti.token, null /* parent */, true /* onTop */); + outWct.reparent(ti.token, null /* parent */, true /* onTop */); // reset bounds and mode too - wct.setBounds(ti.token, null); - wct.setWindowingMode(ti.token, WINDOWING_MODE_UNDEFINED); + outWct.setBounds(ti.token, null); + outWct.setWindowingMode(ti.token, WINDOWING_MODE_UNDEFINED); } } for (int i = primaryChildren.size() - 1; i >= 0; --i) { - wct.reparent(primaryChildren.get(i).token, null /* parent */, + outWct.reparent(primaryChildren.get(i).token, null /* parent */, true /* onTop */); } } for (int i = freeHomeAndRecents.size() - 1; i >= 0; --i) { - wct.setBounds(freeHomeAndRecents.get(i).token, null); - wct.setWindowingMode(freeHomeAndRecents.get(i).token, WINDOWING_MODE_UNDEFINED); + outWct.setBounds(freeHomeAndRecents.get(i).token, null); + outWct.setWindowingMode(freeHomeAndRecents.get(i).token, WINDOWING_MODE_UNDEFINED); } // Reset focusable to true - wct.setFocusable(tiles.mPrimary.token, true /* focusable */); - applySyncTransaction(wct); + outWct.setFocusable(tiles.mPrimary.token, true /* focusable */); } /** diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipController.java index 0955056900f1..ffa6c9921d97 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipController.java @@ -60,7 +60,8 @@ import java.util.Objects; /** * Manages the picture-in-picture (PIP) UI and states. */ -public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallback { +public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallback, + TvPipMenuController.Delegate { private static final String TAG = "TvPipController"; static final boolean DEBUG = false; @@ -198,7 +199,7 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac mPipBoundsAlgorithm = pipBoundsAlgorithm; mPipMediaController = pipMediaController; mTvPipMenuController = tvPipMenuController; - mTvPipMenuController.attachPipController(this); + mTvPipMenuController.setDelegate(this); // Ensure that we have the display info in case we get calls to update the bounds // before the listener calls back final DisplayInfo displayInfo = new DisplayInfo(); @@ -289,6 +290,7 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac /** * Closes PIP (PIPed activity and PIP system UI). */ + @Override public void closePip() { if (DEBUG) Log.d(TAG, "closePip(), current state=" + getStateDescription()); @@ -318,9 +320,15 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac mHandler.removeCallbacks(mClosePipRunnable); } + @Override + public void movePipToNormalPosition() { + resizePinnedStack(PipController.STATE_PIP); + } + /** * Moves the PIPed activity to the fullscreen and closes PIP system UI. */ + @Override public void movePipToFullscreen() { if (DEBUG) Log.d(TAG, "movePipToFullscreen(), current state=" + getStateDescription()); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipControlsView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipControlsView.java deleted file mode 100644 index 95d9b77c513e..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipControlsView.java +++ /dev/null @@ -1,61 +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.wm.shell.pip.tv; - -import android.content.Context; -import android.util.AttributeSet; -import android.view.Gravity; -import android.view.LayoutInflater; -import android.widget.LinearLayout; - -import com.android.wm.shell.R; - - -/** - * A view containing PIP controls including fullscreen, close, and media controls. - */ -public class PipControlsView extends LinearLayout { - - public PipControlsView(Context context) { - this(context, null); - } - - public PipControlsView(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public PipControlsView(Context context, AttributeSet attrs, int defStyleAttr) { - this(context, attrs, defStyleAttr, 0); - } - - public PipControlsView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { - super(context, attrs, defStyleAttr, defStyleRes); - LayoutInflater layoutInflater = (LayoutInflater) getContext().getSystemService( - Context.LAYOUT_INFLATER_SERVICE); - layoutInflater.inflate(R.layout.tv_pip_controls, this); - setOrientation(LinearLayout.HORIZONTAL); - setGravity(Gravity.TOP | Gravity.CENTER_HORIZONTAL); - } - - PipControlButtonView getFullscreenButton() { - return findViewById(R.id.full_button); - } - - PipControlButtonView getCloseButton() { - return findViewById(R.id.close_button); - } -} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipControlsViewController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipControlsViewController.java deleted file mode 100644 index 5265e7705ed9..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipControlsViewController.java +++ /dev/null @@ -1,158 +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.wm.shell.pip.tv; - -import android.app.PendingIntent; -import android.app.RemoteAction; -import android.content.Context; -import android.graphics.Color; -import android.os.Handler; -import android.os.Looper; -import android.util.Log; -import android.view.LayoutInflater; -import android.view.View; - -import com.android.wm.shell.R; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - - -/** - * Controller for {@link PipControlsView}. - */ -public class PipControlsViewController { - private static final String TAG = PipControlsViewController.class.getSimpleName(); - - private static final float DISABLED_ACTION_ALPHA = 0.54f; - - private final PipController mPipController; - - private final Context mContext; - private final Handler mUiThreadHandler; - private final PipControlsView mView; - private final List<PipControlButtonView> mAdditionalButtons = new ArrayList<>(); - - private final List<RemoteAction> mCustomActions = new ArrayList<>(); - private final List<RemoteAction> mMediaActions = new ArrayList<>(); - - public PipControlsViewController(PipControlsView view, PipController pipController) { - mContext = view.getContext(); - mUiThreadHandler = new Handler(Looper.getMainLooper()); - mPipController = pipController; - mView = view; - - mView.getFullscreenButton().setOnClickListener(v -> mPipController.movePipToFullscreen()); - mView.getCloseButton().setOnClickListener(v -> mPipController.closePip()); - - mPipController.getPipMediaController().addActionListener(this::onMediaActionsChanged); - } - - PipControlsView getView() { - return mView; - } - - /** - * Updates the set of activity-defined actions. - */ - void setCustomActions(List<? extends RemoteAction> actions) { - if (mCustomActions.isEmpty() && actions.isEmpty()) { - // Nothing changed - return early. - return; - } - mCustomActions.clear(); - mCustomActions.addAll(actions); - updateAdditionalActions(); - } - - private void onMediaActionsChanged(List<RemoteAction> actions) { - if (mMediaActions.isEmpty() && actions.isEmpty()) { - // Nothing changed - return early. - return; - } - mMediaActions.clear(); - mMediaActions.addAll(actions); - - // Update the view only if there are no custom actions (media actions are only shown when - // there no custom actions). - if (mCustomActions.isEmpty()) { - updateAdditionalActions(); - } - } - - private void updateAdditionalActions() { - final List<RemoteAction> actionsToDisplay; - if (!mCustomActions.isEmpty()) { - // If there are custom actions: show them. - actionsToDisplay = mCustomActions; - } else if (!mMediaActions.isEmpty()) { - // If there are no custom actions, but there media actions: show them. - actionsToDisplay = mMediaActions; - } else { - // If there no custom actions and no media actions: clean up all the additional buttons. - actionsToDisplay = Collections.emptyList(); - } - - // Make sure we exactly as many additional buttons as we have actions to display. - final int actionsNumber = actionsToDisplay.size(); - int buttonsNumber = mAdditionalButtons.size(); - if (actionsNumber > buttonsNumber) { - final LayoutInflater layoutInflater = LayoutInflater.from(mContext); - // Add buttons until we have enough to display all of the actions. - while (actionsNumber > buttonsNumber) { - final PipControlButtonView button = (PipControlButtonView) layoutInflater.inflate( - R.layout.tv_pip_custom_control, mView, false); - mView.addView(button); - mAdditionalButtons.add(button); - - buttonsNumber++; - } - } else if (actionsNumber < buttonsNumber) { - // Hide buttons until we as many as the actions. - while (actionsNumber < buttonsNumber) { - final View button = mAdditionalButtons.get(buttonsNumber - 1); - button.setVisibility(View.GONE); - button.setOnClickListener(null); - - buttonsNumber--; - } - } - - // "Assign" actions to the buttons. - for (int index = 0; index < actionsNumber; index++) { - final RemoteAction action = actionsToDisplay.get(index); - final PipControlButtonView button = mAdditionalButtons.get(index); - button.setVisibility(View.VISIBLE); // Ensure the button is visible. - button.setText(action.getContentDescription()); - button.setEnabled(action.isEnabled()); - button.setAlpha(action.isEnabled() ? 1f : DISABLED_ACTION_ALPHA); - button.setOnClickListener(v -> { - try { - action.getActionIntent().send(); - } catch (PendingIntent.CanceledException e) { - Log.w(TAG, "Failed to send action", e); - } - }); - - action.getIcon().loadDrawableAsync(mContext, drawable -> { - drawable.setTint(Color.WHITE); - button.setImageDrawable(drawable); - }, mUiThreadHandler); - } - } -} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipMenuView.java deleted file mode 100644 index 83cb7ce8065b..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipMenuView.java +++ /dev/null @@ -1,126 +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.wm.shell.pip.tv; - -import static android.view.KeyEvent.ACTION_UP; -import static android.view.KeyEvent.KEYCODE_BACK; - -import android.animation.Animator; -import android.animation.AnimatorInflater; -import android.annotation.Nullable; -import android.app.RemoteAction; -import android.content.Context; -import android.content.pm.ParceledListSlice; -import android.util.Log; -import android.view.KeyEvent; -import android.view.SurfaceControl; -import android.view.ViewRootImpl; -import android.view.WindowManagerGlobal; -import android.widget.FrameLayout; - -import com.android.wm.shell.R; - -import java.util.Collections; - -/** - * The Menu View that shows controls of the PiP. Always fullscreen. - */ -public class PipMenuView extends FrameLayout { - private static final String TAG = "PipMenuView"; - private static final boolean DEBUG = PipController.DEBUG; - - private final Animator mFadeInAnimation; - private final Animator mFadeOutAnimation; - private final PipControlsViewController mPipControlsViewController; - @Nullable - private OnBackPressListener mOnBackPressListener; - - public PipMenuView(Context context, PipController pipController) { - super(context, null, 0); - inflate(context, R.layout.tv_pip_menu, this); - - mPipControlsViewController = new PipControlsViewController( - findViewById(R.id.pip_controls), pipController); - mFadeInAnimation = AnimatorInflater.loadAnimator( - mContext, R.anim.tv_pip_menu_fade_in_animation); - mFadeInAnimation.setTarget(mPipControlsViewController.getView()); - mFadeOutAnimation = AnimatorInflater.loadAnimator( - mContext, R.anim.tv_pip_menu_fade_out_animation); - mFadeOutAnimation.setTarget(mPipControlsViewController.getView()); - } - - @Nullable - SurfaceControl getWindowSurfaceControl() { - final ViewRootImpl root = getViewRootImpl(); - if (root == null) { - return null; - } - final SurfaceControl out = root.getSurfaceControl(); - if (out != null && out.isValid()) { - return out; - } - return null; - } - - void showMenu() { - mFadeInAnimation.start(); - setAlpha(1.0f); - grantWindowFocus(true); - } - - void hideMenu() { - mFadeOutAnimation.start(); - setAlpha(0.0f); - grantWindowFocus(false); - } - - private void grantWindowFocus(boolean grantFocus) { - try { - WindowManagerGlobal.getWindowSession().grantEmbeddedWindowFocus(null /* window */, - getViewRootImpl().getInputToken(), grantFocus); - } catch (Exception e) { - Log.e(TAG, "Unable to update focus as menu disappears", e); - } - } - - void setOnBackPressListener(OnBackPressListener onBackPressListener) { - mOnBackPressListener = onBackPressListener; - } - - @Override - public boolean dispatchKeyEvent(KeyEvent event) { - if (event.getKeyCode() == KEYCODE_BACK && event.getAction() == ACTION_UP - && mOnBackPressListener != null) { - mOnBackPressListener.onBackPress(); - return true; - } else { - return super.dispatchKeyEvent(event); - } - } - - void setAppActions(ParceledListSlice<RemoteAction> actions) { - if (DEBUG) Log.d(TAG, "onPipMenuActionsChanged()"); - - boolean hasCustomActions = actions != null && !actions.getList().isEmpty(); - mPipControlsViewController.setCustomActions( - hasCustomActions ? actions.getList() : Collections.emptyList()); - } - - interface OnBackPressListener { - void onBackPress(); - } -} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipControlButtonView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuActionButton.java index 4e82bb557fb9..6f7cd82f8da0 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipControlButtonView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuActionButton.java @@ -31,64 +31,51 @@ import android.widget.TextView; import com.android.wm.shell.R; /** - * A view containing PIP controls including fullscreen, close, and media controls. + * A View that represents Pip Menu action button, such as "Fullscreen" and "Close" as well custom + * (provided by the application in Pip) and media buttons. */ -public class PipControlButtonView extends RelativeLayout { - - private OnFocusChangeListener mFocusChangeListener; - private ImageView mIconImageView; - ImageView mButtonImageView; - private TextView mDescriptionTextView; +public class TvPipMenuActionButton extends RelativeLayout implements View.OnClickListener { + private final ImageView mIconImageView; + private final ImageView mButtonImageView; + private final TextView mDescriptionTextView; private Animator mTextFocusGainAnimator; private Animator mButtonFocusGainAnimator; private Animator mTextFocusLossAnimator; private Animator mButtonFocusLossAnimator; + private OnClickListener mOnClickListener; - private final OnFocusChangeListener mInternalFocusChangeListener = - new OnFocusChangeListener() { - @Override - public void onFocusChange(View v, boolean hasFocus) { - if (hasFocus) { - startFocusGainAnimation(); - } else { - startFocusLossAnimation(); - } - - if (mFocusChangeListener != null) { - mFocusChangeListener.onFocusChange(PipControlButtonView.this, hasFocus); - } - } - }; - - public PipControlButtonView(Context context) { + public TvPipMenuActionButton(Context context) { this(context, null, 0, 0); } - public PipControlButtonView(Context context, AttributeSet attrs) { + public TvPipMenuActionButton(Context context, AttributeSet attrs) { this(context, attrs, 0, 0); } - public PipControlButtonView(Context context, AttributeSet attrs, int defStyleAttr) { + public TvPipMenuActionButton(Context context, AttributeSet attrs, int defStyleAttr) { this(context, attrs, defStyleAttr, 0); } - public PipControlButtonView( + public TvPipMenuActionButton( Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); - LayoutInflater inflater = (LayoutInflater) getContext() + final LayoutInflater inflater = (LayoutInflater) getContext() .getSystemService(Context.LAYOUT_INFLATER_SERVICE); - inflater.inflate(R.layout.tv_pip_control_button, this); + inflater.inflate(R.layout.tv_pip_menu_action_button, this); mIconImageView = findViewById(R.id.icon); mButtonImageView = findViewById(R.id.button); mDescriptionTextView = findViewById(R.id.desc); - int[] values = new int[]{android.R.attr.src, android.R.attr.text}; - TypedArray typedArray = context.obtainStyledAttributes(attrs, values, defStyleAttr, + final int[] values = new int[]{android.R.attr.src, android.R.attr.text}; + final TypedArray typedArray = context.obtainStyledAttributes(attrs, values, defStyleAttr, defStyleRes); setImageResource(typedArray.getResourceId(0, 0)); - setText(typedArray.getResourceId(1, 0)); + final int textResId = typedArray.getResourceId(1, 0); + if (textResId != 0) { + setTextAndDescription(getContext().getString(textResId)); + } typedArray.recycle(); } @@ -96,7 +83,13 @@ public class PipControlButtonView extends RelativeLayout { @Override public void onFinishInflate() { super.onFinishInflate(); - mButtonImageView.setOnFocusChangeListener(mInternalFocusChangeListener); + mButtonImageView.setOnFocusChangeListener((v, hasFocus) -> { + if (hasFocus) { + startFocusGainAnimation(); + } else { + startFocusLossAnimation(); + } + }); mTextFocusGainAnimator = AnimatorInflater.loadAnimator(getContext(), R.anim.tv_pip_controls_focus_gain_animation); @@ -115,12 +108,19 @@ public class PipControlButtonView extends RelativeLayout { @Override public void setOnClickListener(OnClickListener listener) { - mButtonImageView.setOnClickListener(listener); + // We do not want to set an OnClickListener to the TvPipMenuActionButton itself, but only to + // the ImageView. So let's "cash" the listener we've been passed here and set a "proxy" + // listener to the ImageView. + mOnClickListener = listener; + mButtonImageView.setOnClickListener(listener != null ? this : null); } @Override - public void setOnFocusChangeListener(OnFocusChangeListener listener) { - mFocusChangeListener = listener; + public void onClick(View v) { + if (mOnClickListener != null) { + // Pass the correct view - this. + mOnClickListener.onClick(this); + } } /** @@ -142,21 +142,11 @@ public class PipControlButtonView extends RelativeLayout { /** * Sets the text for description the with the given string. */ - public void setText(CharSequence text) { + public void setTextAndDescription(CharSequence text) { mButtonImageView.setContentDescription(text); mDescriptionTextView.setText(text); } - /** - * Sets the text for description the with the given resource id. - */ - public void setText(int resId) { - if (resId != 0) { - mButtonImageView.setContentDescription(getContext().getString(resId)); - mDescriptionTextView.setText(resId); - } - } - private static void cancelAnimator(Animator animator) { if (animator.isStarted()) { animator.cancel(); @@ -187,8 +177,8 @@ public class PipControlButtonView extends RelativeLayout { mTextFocusLossAnimator.start(); if (mButtonImageView.hasFocus()) { // Button uses ripple that has the default animation for the focus changes. - // Howevever, it doesn't expose the API to fade out while it is focused, - // so we should manually run the fade out animation when PIP controls row loses focus. + // However, it doesn't expose the API to fade out while it is focused, so we should + // manually run the fade out animation when PIP controls row loses focus. mButtonFocusLossAnimator.start(); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java index 5d0d761abd93..9192cf14cd9b 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java @@ -19,38 +19,97 @@ package com.android.wm.shell.pip.tv; import static android.view.WindowManager.SHELL_ROOT_LAYER_PIP; import android.app.RemoteAction; +import android.content.BroadcastReceiver; import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; import android.content.pm.ParceledListSlice; import android.util.Log; import android.view.SurfaceControl; +import androidx.annotation.Nullable; + import com.android.wm.shell.common.SystemWindows; import com.android.wm.shell.pip.PipBoundsState; +import com.android.wm.shell.pip.PipMediaController; import com.android.wm.shell.pip.PipMenuController; +import java.util.ArrayList; +import java.util.List; + /** * Manages the visibility of the PiP Menu as user interacts with PiP. */ -public class TvPipMenuController implements PipMenuController { +public class TvPipMenuController implements PipMenuController, TvPipMenuView.Listener { private static final String TAG = "TvPipMenuController"; private static final boolean DEBUG = PipController.DEBUG; private final Context mContext; private final SystemWindows mSystemWindows; private final PipBoundsState mPipBoundsState; - private PipMenuView mMenuView; - private PipController mPipController; + + private Delegate mDelegate; private SurfaceControl mLeash; + private TvPipMenuView mMenuView; + + private final List<RemoteAction> mMediaActions = new ArrayList<>(); + private final List<RemoteAction> mAppActions = new ArrayList<>(); public TvPipMenuController(Context context, PipBoundsState pipBoundsState, - SystemWindows systemWindows) { + SystemWindows systemWindows, PipMediaController pipMediaController) { mContext = context; mPipBoundsState = pipBoundsState; mSystemWindows = systemWindows; + + // We need to "close" the menu the platform call for all the system dialogs to close (for + // example, on the Home button press). + final BroadcastReceiver closeSystemDialogsBroadcastReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + hideMenu(); + } + }; + context.registerReceiver(closeSystemDialogsBroadcastReceiver, + new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)); + + pipMediaController.addActionListener(this::onMediaActionsChanged); } - void attachPipController(PipController pipController) { - mPipController = pipController; + void setDelegate(Delegate delegate) { + if (DEBUG) Log.d(TAG, "setDelegate(), delegate=" + delegate); + if (mDelegate != null) { + throw new IllegalStateException( + "The delegate has already been set and should not change."); + } + if (delegate == null) { + throw new IllegalArgumentException("The delegate must not be null."); + } + + mDelegate = delegate; + } + + @Override + public void attach(SurfaceControl leash) { + if (mDelegate == null) { + throw new IllegalStateException("Delegate is not set."); + } + + mLeash = leash; + attachPipMenuView(); + } + + private void attachPipMenuView() { + if (DEBUG) Log.d(TAG, "attachPipMenuView()"); + + if (mMenuView != null) { + detachPipMenuView(); + } + + mMenuView = new TvPipMenuView(mContext); + mMenuView.setListener(this); + mSystemWindows.addView(mMenuView, + getPipMenuLayoutParams(MENU_WINDOW_TITLE, 0 /* width */, 0 /* height */), + 0, SHELL_ROOT_LAYER_PIP); } @Override @@ -61,7 +120,8 @@ public class TvPipMenuController implements PipMenuController { mSystemWindows.updateViewLayout(mMenuView, getPipMenuLayoutParams(MENU_WINDOW_TITLE, mPipBoundsState.getDisplayBounds().width(), mPipBoundsState.getDisplayBounds().height())); - mMenuView.showMenu(); + maybeUpdateMenuViewActions(); + mMenuView.show(); // By default, SystemWindows views are above everything else. // Set the relative z-order so the menu is below PiP. @@ -77,38 +137,18 @@ public class TvPipMenuController implements PipMenuController { if (DEBUG) Log.d(TAG, "hideMenu()"); if (isMenuVisible()) { - mMenuView.hideMenu(); - mPipController.resizePinnedStack(PipController.STATE_PIP); + mMenuView.hide(); + mDelegate.movePipToNormalPosition(); } } @Override - public void attach(SurfaceControl leash) { - mLeash = leash; - attachPipMenuView(); - } - - @Override public void detach() { hideMenu(); detachPipMenuView(); mLeash = null; } - private void attachPipMenuView() { - if (DEBUG) Log.d(TAG, "attachPipMenuView()"); - - if (mMenuView != null) { - detachPipMenuView(); - } - - mMenuView = new PipMenuView(mContext, mPipController); - mMenuView.setOnBackPressListener(this::hideMenu); - mSystemWindows.addView(mMenuView, - getPipMenuLayoutParams(MENU_WINDOW_TITLE, 0 /* width */, 0 /* height */), - 0, SHELL_ROOT_LAYER_PIP); - } - private void detachPipMenuView() { if (DEBUG) Log.d(TAG, "detachPipMenuView()"); @@ -121,18 +161,65 @@ public class TvPipMenuController implements PipMenuController { } @Override - public void setAppActions(ParceledListSlice<RemoteAction> appActions) { - if (DEBUG) Log.d(TAG, "setAppActions(), actions=" + appActions); + public void setAppActions(ParceledListSlice<RemoteAction> actions) { + if (DEBUG) Log.d(TAG, "setAppActions()"); + updateAdditionalActionsList(mAppActions, actions.getList()); + } - if (mMenuView != null) { - mMenuView.setAppActions(appActions); + private void onMediaActionsChanged(List<RemoteAction> actions) { + if (DEBUG) Log.d(TAG, "onMediaActionsChanged()"); + updateAdditionalActionsList(mMediaActions, actions); + } + + private void updateAdditionalActionsList( + List<RemoteAction> destination, @Nullable List<RemoteAction> source) { + final int number = source != null ? source.size() : 0; + if (number == 0 && destination.isEmpty()) { + // Nothing changed. + return; + } + + destination.clear(); + if (number > 0) { + destination.addAll(source); + } + maybeUpdateMenuViewActions(); + } + + private void maybeUpdateMenuViewActions() { + if (mMenuView == null) { + return; + } + if (!mAppActions.isEmpty()) { + mMenuView.setAdditionalActions(mAppActions); } else { - Log.w(TAG, "Cannot set remote actions, there is no View"); + mMenuView.setAdditionalActions(mMediaActions); } } @Override public boolean isMenuVisible() { - return mMenuView != null && mMenuView.getAlpha() == 1.0f; + return mMenuView != null && mMenuView.isVisible(); + } + + @Override + public void onBackPress() { + hideMenu(); + } + + @Override + public void onCloseButtonClick() { + mDelegate.closePip(); + } + + @Override + public void onFullscreenButtonClick() { + mDelegate.movePipToFullscreen(); + } + + interface Delegate { + void movePipToNormalPosition(); + void movePipToFullscreen(); + void closePip(); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java new file mode 100644 index 000000000000..f7b76c1ec745 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java @@ -0,0 +1,235 @@ +/* + * 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.wm.shell.pip.tv; + +import static android.animation.AnimatorInflater.loadAnimator; +import static android.view.KeyEvent.ACTION_UP; +import static android.view.KeyEvent.KEYCODE_BACK; + +import android.animation.Animator; +import android.app.PendingIntent; +import android.app.RemoteAction; +import android.content.Context; +import android.graphics.Color; +import android.os.Handler; +import android.os.Looper; +import android.util.AttributeSet; +import android.util.Log; +import android.view.KeyEvent; +import android.view.LayoutInflater; +import android.view.SurfaceControl; +import android.view.View; +import android.view.ViewRootImpl; +import android.view.WindowManagerGlobal; +import android.widget.FrameLayout; +import android.widget.LinearLayout; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.android.wm.shell.R; + +import java.util.ArrayList; +import java.util.List; + +/** + * A View that represents Pip Menu on TV. It's responsible for displaying 2 ever-present Pip Menu + * actions: Fullscreen and Close, but could also display "additional" actions, that may be set via + * a {@link #setAdditionalActions(List)} call. + */ +public class TvPipMenuView extends FrameLayout implements View.OnClickListener { + private static final String TAG = "TvPipMenuView"; + private static final boolean DEBUG = PipController.DEBUG; + + private static final float DISABLED_ACTION_ALPHA = 0.54f; + + private final Handler mUiThreadHandler; + private final Animator mFadeInAnimation; + private final Animator mFadeOutAnimation; + @Nullable private Listener mListener; + + private final LinearLayout mActionButtonsContainer; + private final List<TvPipMenuActionButton> mAdditionalButtons = new ArrayList<>(); + + public TvPipMenuView(@NonNull Context context) { + this(context, null); + } + + public TvPipMenuView(@NonNull Context context, @Nullable AttributeSet attrs) { + this(context, attrs, 0); + } + + public TvPipMenuView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + this(context, attrs, defStyleAttr, 0); + } + + public TvPipMenuView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, + int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + mUiThreadHandler = new Handler(Looper.getMainLooper()); + + inflate(context, R.layout.tv_pip_menu, this); + + mActionButtonsContainer = findViewById(R.id.tv_pip_menu_action_buttons); + mActionButtonsContainer.findViewById(R.id.tv_pip_menu_fullscreen_button) + .setOnClickListener(this); + mActionButtonsContainer.findViewById(R.id.tv_pip_menu_close_button) + .setOnClickListener(this); + + mFadeInAnimation = loadAnimator(mContext, R.anim.tv_pip_menu_fade_in_animation); + mFadeInAnimation.setTarget(mActionButtonsContainer); + + mFadeOutAnimation = loadAnimator(mContext, R.anim.tv_pip_menu_fade_out_animation); + mFadeOutAnimation.setTarget(mActionButtonsContainer); + } + + void setListener(@Nullable Listener listener) { + mListener = listener; + } + + void show() { + if (DEBUG) Log.d(TAG, "show()"); + + mFadeInAnimation.start(); + setAlpha(1.0f); + grantWindowFocus(true); + } + + void hide() { + if (DEBUG) Log.d(TAG, "hide()"); + + mFadeOutAnimation.start(); + setAlpha(0.0f); + grantWindowFocus(false); + } + + boolean isVisible() { + return getAlpha() == 1.0f; + } + + private void grantWindowFocus(boolean grantFocus) { + if (DEBUG) Log.d(TAG, "grantWindowFocus(" + grantFocus + ")"); + + try { + WindowManagerGlobal.getWindowSession().grantEmbeddedWindowFocus(null /* window */, + getViewRootImpl().getInputToken(), grantFocus); + } catch (Exception e) { + Log.e(TAG, "Unable to update focus", e); + } + } + + void setAdditionalActions(List<RemoteAction> actions) { + if (DEBUG) Log.d(TAG, "setAdditionalActions()"); + + // Make sure we exactly as many additional buttons as we have actions to display. + final int actionsNumber = actions.size(); + int buttonsNumber = mAdditionalButtons.size(); + if (actionsNumber > buttonsNumber) { + final LayoutInflater layoutInflater = LayoutInflater.from(mContext); + // Add buttons until we have enough to display all of the actions. + while (actionsNumber > buttonsNumber) { + final TvPipMenuActionButton button = (TvPipMenuActionButton) layoutInflater.inflate( + R.layout.tv_pip_menu_additional_action_button, mActionButtonsContainer, + false); + button.setOnClickListener(this); + + mActionButtonsContainer.addView(button); + mAdditionalButtons.add(button); + + buttonsNumber++; + } + } else if (actionsNumber < buttonsNumber) { + // Hide buttons until we as many as the actions. + while (actionsNumber < buttonsNumber) { + final View button = mAdditionalButtons.get(buttonsNumber - 1); + button.setVisibility(View.GONE); + button.setTag(null); + + buttonsNumber--; + } + } + + // "Assign" actions to the buttons. + for (int index = 0; index < actionsNumber; index++) { + final RemoteAction action = actions.get(index); + final TvPipMenuActionButton button = mAdditionalButtons.get(index); + button.setVisibility(View.VISIBLE); // Ensure the button is visible. + button.setTextAndDescription(action.getContentDescription()); + button.setEnabled(action.isEnabled()); + button.setAlpha(action.isEnabled() ? 1f : DISABLED_ACTION_ALPHA); + button.setTag(action); + + action.getIcon().loadDrawableAsync(mContext, drawable -> { + drawable.setTint(Color.WHITE); + button.setImageDrawable(drawable); + }, mUiThreadHandler); + } + } + + @Nullable + SurfaceControl getWindowSurfaceControl() { + final ViewRootImpl root = getViewRootImpl(); + if (root == null) { + return null; + } + final SurfaceControl out = root.getSurfaceControl(); + if (out != null && out.isValid()) { + return out; + } + return null; + } + + @Override + public void onClick(View v) { + if (mListener == null) return; + + final int id = v.getId(); + if (id == R.id.tv_pip_menu_fullscreen_button) { + mListener.onFullscreenButtonClick(); + } else if (id == R.id.tv_pip_menu_close_button) { + mListener.onCloseButtonClick(); + } else { + // This should be an "additional action" + final RemoteAction action = (RemoteAction) v.getTag(); + if (action != null) { + try { + action.getActionIntent().send(); + } catch (PendingIntent.CanceledException e) { + Log.w(TAG, "Failed to send action", e); + } + } else { + Log.w(TAG, "RemoteAction is null"); + } + } + } + + @Override + public boolean dispatchKeyEvent(KeyEvent event) { + if (event.getAction() == ACTION_UP && event.getKeyCode() == KEYCODE_BACK + && mListener != null) { + mListener.onBackPress(); + return true; + } + return super.dispatchKeyEvent(event); + } + + interface Listener { + void onBackPress(); + void onCloseButtonClick(); + void onFullscreenButtonClick(); + } +} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt index 5125a3972cf4..796c8c4a6ad0 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt @@ -31,6 +31,10 @@ const val TEST_APP_PIP_MENU_ACTION_CLEAR = "Clear" // Test App > Ime Activity const val TEST_APP_IME_ACTIVITY_LABEL = "ImeApp" +const val TEST_APP_IME_ACTIVITY_ACTION_OPEN_IME = + "com.android.wm.shell.flicker.testapp.action.OPEN_IME" +const val TEST_APP_IME_ACTIVITY_ACTION_CLOSE_IME = + "com.android.wm.shell.flicker.testapp.action.CLOSE_IME" // Test App > Test Activity const val TEST_APP_FIXED_ACTIVITY_LABEL = "FixedApp" diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/BaseAppHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/BaseAppHelper.kt index 6fd1df3b3f30..f8efd0e9c761 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/BaseAppHelper.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/BaseAppHelper.kt @@ -62,8 +62,9 @@ abstract class BaseAppHelper( val ui: UiObject2? get() = uiDevice.findObject(appSelector) - fun launchViaIntent(stringExtras: Map<String, String> = mapOf()) { + fun launchViaIntent(action: String? = null, stringExtras: Map<String, String> = mapOf()) { val intent = openAppIntent + intent.action = action stringExtras.forEach() { intent.putExtra(it.key, it.value) } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/ImeAppHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/ImeAppHelper.kt index c546a4d18027..eb20d73cdcd1 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/ImeAppHelper.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/ImeAppHelper.kt @@ -20,6 +20,8 @@ import android.app.Instrumentation import androidx.test.uiautomator.By import androidx.test.uiautomator.Until import com.android.server.wm.flicker.helpers.FIND_TIMEOUT +import com.android.wm.shell.flicker.TEST_APP_IME_ACTIVITY_ACTION_CLOSE_IME +import com.android.wm.shell.flicker.TEST_APP_IME_ACTIVITY_ACTION_OPEN_IME import com.android.wm.shell.flicker.TEST_APP_IME_ACTIVITY_LABEL import com.android.wm.shell.flicker.testapp.Components import org.junit.Assert @@ -32,15 +34,27 @@ open class ImeAppHelper( Components.ImeActivity() ) { fun openIME() { - val editText = uiDevice.wait( - Until.findObject(By.res(getPackage(), "plain_text_input")), - FIND_TIMEOUT) - Assert.assertNotNull("Text field not found, this usually happens when the device " + - "was left in an unknown state (e.g. in split screen)", editText) - editText.click() + if (!isTelevision) { + val editText = uiDevice.wait( + Until.findObject(By.res(getPackage(), "plain_text_input")), + FIND_TIMEOUT) + Assert.assertNotNull("Text field not found, this usually happens when the device " + + "was left in an unknown state (e.g. in split screen)", editText) + editText.click() + } else { + // If we do the same thing as above - editText.click() - on TV, that's going to force TV + // into the touch mode. We really don't want that. + launchViaIntent(action = TEST_APP_IME_ACTIVITY_ACTION_OPEN_IME) + } } fun closeIME() { - uiDevice.pressBack() + if (!isTelevision) { + uiDevice.pressBack() + } else { + // While pressing the back button should close the IME on TV as well, it may also lead + // to the app closing. So let's instead just ask the app to close the IME. + launchViaIntent(action = TEST_APP_IME_ACTIVITY_ACTION_CLOSE_IME) + } } }
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipMenuTests.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipMenuTests.kt index 66efb5ae3c2d..6105f50562d7 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipMenuTests.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipMenuTests.kt @@ -208,8 +208,8 @@ class TvPipMenuTests : TvPipTestBase() { @Test fun pipMenu_customActions_override_mediaControls() { // Start media session before entering PiP with custom actions. - testApp.clickStartMediaSessionButton() testApp.checkWithCustomActionsCheckbox() + testApp.clickStartMediaSessionButton() enterPip_openMenu_assertShown() // PiP menu should contain "No-Op", "Off" and "Clear" buttons for the custom actions... diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvUtils.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvUtils.kt index 587b5510b0b4..1b73920046dc 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvUtils.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvUtils.kt @@ -26,78 +26,109 @@ import com.android.wm.shell.flicker.SYSTEM_UI_PACKAGE_NAME /** Id of the root view in the com.android.wm.shell.pip.tv.PipMenuActivity */ private const val TV_PIP_MENU_ROOT_ID = "tv_pip_menu" -private const val TV_PIP_MENU_CONTROLS_ID = "pip_controls" -private const val TV_PIP_MENU_CLOSE_BUTTON_ID = "close_button" -private const val TV_PIP_MENU_FULLSCREEN_BUTTON_ID = "full_button" +private const val TV_PIP_MENU_BUTTONS_CONTAINER_ID = "tv_pip_menu_action_buttons" +private const val TV_PIP_MENU_CLOSE_BUTTON_ID = "tv_pip_menu_close_button" +private const val TV_PIP_MENU_FULLSCREEN_BUTTON_ID = "tv_pip_menu_fullscreen_button" private const val FOCUS_ATTEMPTS = 10 private const val WAIT_TIME_MS = 3_000L +private val TV_PIP_MENU_SELECTOR = + By.res(SYSTEM_UI_PACKAGE_NAME, TV_PIP_MENU_ROOT_ID) +private val TV_PIP_MENU_BUTTONS_CONTAINER_SELECTOR = + By.res(SYSTEM_UI_PACKAGE_NAME, TV_PIP_MENU_BUTTONS_CONTAINER_ID) private val TV_PIP_MENU_CLOSE_BUTTON_SELECTOR = By.res(SYSTEM_UI_PACKAGE_NAME, TV_PIP_MENU_CLOSE_BUTTON_ID) private val TV_PIP_MENU_FULLSCREEN_BUTTON_SELECTOR = By.res(SYSTEM_UI_PACKAGE_NAME, TV_PIP_MENU_FULLSCREEN_BUTTON_ID) -private val tvPipMenuSelector = By.res(SYSTEM_UI_PACKAGE_NAME, TV_PIP_MENU_ROOT_ID) - -fun UiDevice.pressWindowKey() = pressKeyCode(KeyEvent.KEYCODE_WINDOW) - fun UiDevice.waitForTvPipMenu(): UiObject2? = - wait(Until.findObject(tvPipMenuSelector), WAIT_TIME_MS) + wait(Until.findObject(TV_PIP_MENU_SELECTOR), WAIT_TIME_MS) -fun UiDevice.waitForTvPipMenuToClose(): Boolean = wait(Until.gone(tvPipMenuSelector), WAIT_TIME_MS) +fun UiDevice.waitForTvPipMenuToClose(): Boolean = + wait(Until.gone(TV_PIP_MENU_SELECTOR), WAIT_TIME_MS) fun UiDevice.findTvPipMenuControls(): UiObject2? = - findObject(tvPipMenuSelector) - ?.findObject(By.res(SYSTEM_UI_PACKAGE_NAME, TV_PIP_MENU_CONTROLS_ID)) + findTvPipMenuElement(TV_PIP_MENU_BUTTONS_CONTAINER_SELECTOR) fun UiDevice.findTvPipMenuCloseButton(): UiObject2? = - findObject(tvPipMenuSelector)?.findObject(TV_PIP_MENU_CLOSE_BUTTON_SELECTOR) + findTvPipMenuElement(TV_PIP_MENU_CLOSE_BUTTON_SELECTOR) + +fun UiDevice.findTvPipMenuFullscreenButton(): UiObject2? = + findTvPipMenuElement(TV_PIP_MENU_FULLSCREEN_BUTTON_SELECTOR) + +fun UiDevice.findTvPipMenuElementWithDescription(desc: String): UiObject2? = + findTvPipMenuElement(By.desc(desc)) + +private fun UiDevice.findTvPipMenuElement(selector: BySelector): UiObject2? = + findObject(TV_PIP_MENU_SELECTOR)?.findObject(selector) + +fun UiDevice.waitForTvPipMenuElementWithDescription(desc: String): UiObject2? { + // Ideally, we'd want to wait for an element with the given description that has the Pip Menu as + // its parent, but the API does not allow us to construct a query exactly that way. + // So instead we'll wait for a Pip Menu that has the element, which we are looking for, as a + // descendant and then retrieve the element from the menu and return to the caller of this + // method. + val elementSelector = By.desc(desc) + val menuContainingElementSelector = By.copy(TV_PIP_MENU_SELECTOR).hasDescendant(elementSelector) + + return wait(Until.findObject(menuContainingElementSelector), WAIT_TIME_MS) + ?.findObject(elementSelector) +} + +fun UiDevice.waitUntilTvPipMenuElementWithDescriptionIsGone(desc: String): Boolean? { + val elementSelector = By.desc(desc) + val menuContainingElementSelector = By.copy(TV_PIP_MENU_SELECTOR).hasDescendant(elementSelector) + + return wait(Until.gone(menuContainingElementSelector), WAIT_TIME_MS) +} fun UiDevice.clickTvPipMenuCloseButton() { - focusOnObjectInTvPipMenu(TV_PIP_MENU_CLOSE_BUTTON_SELECTOR) || + focusOnAndClickTvPipMenuElement(TV_PIP_MENU_CLOSE_BUTTON_SELECTOR) || error("Could not focus on the Close button") - pressDPadCenter() } -fun UiDevice.findTvPipMenuFullscreenButton(): UiObject2? = - findObject(tvPipMenuSelector)?.findObject(TV_PIP_MENU_FULLSCREEN_BUTTON_SELECTOR) - fun UiDevice.clickTvPipMenuFullscreenButton() { - focusOnObjectInTvPipMenu(TV_PIP_MENU_FULLSCREEN_BUTTON_SELECTOR) || + focusOnAndClickTvPipMenuElement(TV_PIP_MENU_FULLSCREEN_BUTTON_SELECTOR) || error("Could not focus on the Fullscreen button") - pressDPadCenter() } -fun UiDevice.findTvPipMenuElementWithDescription(desc: String): UiObject2? = - findObject(tvPipMenuSelector)?.findObject(By.desc(desc).pkg(SYSTEM_UI_PACKAGE_NAME)) - fun UiDevice.clickTvPipMenuElementWithDescription(desc: String) { - focusOnObjectInTvPipMenu(By.desc(desc).pkg(SYSTEM_UI_PACKAGE_NAME)) || + focusOnAndClickTvPipMenuElement(By.desc(desc).pkg(SYSTEM_UI_PACKAGE_NAME)) || error("Could not focus on the Pip menu object with \"$desc\" description") - pressDPadCenter() + // So apparently Accessibility framework on TV is not very reliable and sometimes the state of + // the tree of accessibility nodes as seen by the accessibility clients kind of lags behind of + // the "real" state of the "UI tree". It seems, however, that moving focus around the tree + // forces the AccessibilityNodeInfo tree to get properly updated. + // So since we suspect that clicking on a Pip Menu element may cause some UI changes and we want + // those changes to be seen by the UiAutomator, which is using Accessibility framework under the + // hood for inspecting UI, we'll move the focus around a little. + moveFocus() } -fun UiDevice.waitForTvPipMenuElementWithDescription(desc: String): UiObject2? { - val buttonSelector = By.desc(desc) - val menuWithButtonSelector = By.copy(tvPipMenuSelector).hasDescendant(buttonSelector) - return wait(Until.findObject(menuWithButtonSelector), WAIT_TIME_MS) - ?.findObject(buttonSelector) -} - -fun UiDevice.waitUntilTvPipMenuElementWithDescriptionIsGone(desc: String): Boolean? = - wait(Until.gone(By.copy(tvPipMenuSelector).hasDescendant(By.desc(desc))), WAIT_TIME_MS) +private fun UiDevice.focusOnAndClickTvPipMenuElement(selector: BySelector): Boolean { + repeat(FOCUS_ATTEMPTS) { + val element = findTvPipMenuElement(selector) + ?: error("The Pip Menu element we try to focus on is gone.") + + if (element.isFocusedOrHasFocusedChild) { + pressDPadCenter() + return true + } + + findTvPipMenuElement(By.focused(true))?.let { focused -> + if (element.visibleCenter.x < focused.visibleCenter.x) + pressDPadLeft() else pressDPadRight() + waitForIdle() + } ?: error("Pip menu does not contain a focused element") + } -fun UiObject2.isFullscreen(uiDevice: UiDevice): Boolean = visibleBounds.run { - height() == uiDevice.displayHeight && width() == uiDevice.displayWidth + return false } -val UiObject2.isFocusedOrHasFocusedChild: Boolean - get() = isFocused || findObject(By.focused(true)) != null - fun UiDevice.closeTvPipWindow() { // Check if Pip menu is Open. If it's not, open it. - if (findObject(tvPipMenuSelector) == null) { + if (findObject(TV_PIP_MENU_SELECTOR) == null) { pressWindowKey() waitForTvPipMenu() ?: error("Could not open Pip menu") } @@ -106,17 +137,25 @@ fun UiDevice.closeTvPipWindow() { waitForTvPipMenuToClose() } -private fun UiDevice.focusOnObjectInTvPipMenu(objectSelector: BySelector): Boolean { - repeat(FOCUS_ATTEMPTS) { - val menu = findObject(tvPipMenuSelector) ?: error("Pip Menu is now shown") - val objectToFocus = menu.findObject(objectSelector) - .apply { if (isFocusedOrHasFocusedChild) return true } - ?: error("The object we try to focus on is gone.") - val currentlyFocused = menu.findObject(By.focused(true)) - ?: error("Pip menu does not contain a focused element") - if (objectToFocus.visibleCenter.x < currentlyFocused.visibleCenter.x) - pressDPadLeft() else pressDPadRight() - waitForIdle() - } - return false -}
\ No newline at end of file +/** + * Simply presses the D-Pad Left and Right buttons once, which should move the focus on the screen, + * which should cause Accessibility events to be fired, which should, hopefully, properly update + * AccessibilityNodeInfo tree dispatched by the platform to the Accessibility services, one of which + * is the UiAutomator. + */ +private fun UiDevice.moveFocus() { + waitForIdle() + pressDPadLeft() + waitForIdle() + pressDPadRight() + waitForIdle() +} + +fun UiDevice.pressWindowKey() = pressKeyCode(KeyEvent.KEYCODE_WINDOW) + +fun UiObject2.isFullscreen(uiDevice: UiDevice): Boolean = visibleBounds.run { + height() == uiDevice.displayHeight && width() == uiDevice.displayWidth +} + +val UiObject2.isFocusedOrHasFocusedChild: Boolean + get() = isFocused || findObject(By.focused(true)) != null diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/AndroidManifest.xml b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/AndroidManifest.xml index 28ed3431db62..5549330df766 100644 --- a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/AndroidManifest.xml +++ b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/AndroidManifest.xml @@ -53,6 +53,7 @@ <activity android:name=".ImeActivity" android:taskAffinity="com.android.wm.shell.flicker.testapp.ImeActivity" android:label="ImeApp" + android:launchMode="singleTop" android:exported="true"> <intent-filter> <action android:name="android.intent.action.MAIN"/> diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/ImeActivity.java b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/ImeActivity.java index 856728715c1c..59c64a1345ab 100644 --- a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/ImeActivity.java +++ b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/ImeActivity.java @@ -17,10 +17,21 @@ package com.android.wm.shell.flicker.testapp; import android.app.Activity; +import android.content.Intent; import android.os.Bundle; +import android.view.View; import android.view.WindowManager; +import android.view.inputmethod.InputMethodManager; public class ImeActivity extends Activity { + private static final String ACTION_OPEN_IME = + "com.android.wm.shell.flicker.testapp.action.OPEN_IME"; + private static final String ACTION_CLOSE_IME = + "com.android.wm.shell.flicker.testapp.action.CLOSE_IME"; + + private InputMethodManager mImm; + private View mEditText; + @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -29,5 +40,27 @@ public class ImeActivity extends Activity { .LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES; getWindow().setAttributes(p); setContentView(R.layout.activity_ime); + + mEditText = findViewById(R.id.plain_text_input); + mImm = getSystemService(InputMethodManager.class); + + handleIntent(getIntent()); + } + + @Override + protected void onNewIntent(Intent intent) { + super.onNewIntent(intent); + handleIntent(intent); + } + + private void handleIntent(Intent intent) { + final String action = intent.getAction(); + if (ACTION_OPEN_IME.equals(action)) { + mEditText.requestFocus(); + mImm.showSoftInput(mEditText, InputMethodManager.SHOW_FORCED); + } else if (ACTION_CLOSE_IME.equals(action)) { + mImm.hideSoftInputFromWindow(mEditText.getWindowToken(), 0); + mEditText.clearFocus(); + } } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleTest.java index 069305212958..bde04b604c79 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleTest.java @@ -71,7 +71,7 @@ public class BubbleTest extends ShellTestCase { Intent target = new Intent(mContext, BubblesTestActivity.class); Notification.BubbleMetadata metadata = new Notification.BubbleMetadata.Builder( - PendingIntent.getActivity(mContext, 0, target, 0), + PendingIntent.getActivity(mContext, 0, target, PendingIntent.FLAG_MUTABLE), Icon.createWithResource(mContext, R.drawable.bubble_ic_create_bubble)) .build(); when(mSbn.getNotification()).thenReturn(mNotif); diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp index acb74f415f41..815ffdeade45 100644 --- a/libs/hwui/SkiaCanvas.cpp +++ b/libs/hwui/SkiaCanvas.cpp @@ -814,6 +814,18 @@ void SkiaCanvas::drawCircle(uirenderer::CanvasPropertyPrimitive* x, mCanvas->drawDrawable(drawable.get()); } +void SkiaCanvas::drawRipple(uirenderer::CanvasPropertyPrimitive* x, + uirenderer::CanvasPropertyPrimitive* y, + uirenderer::CanvasPropertyPrimitive* radius, + uirenderer::CanvasPropertyPaint* paint, + uirenderer::CanvasPropertyPrimitive* progress, + sk_sp<SkRuntimeEffect> runtimeEffect) { + sk_sp<uirenderer::skiapipeline::AnimatedRipple> drawable( + new uirenderer::skiapipeline::AnimatedRipple(x, y, radius, paint, progress, + runtimeEffect)); + mCanvas->drawDrawable(drawable.get()); +} + void SkiaCanvas::drawPicture(const SkPicture& picture) { // TODO: Change to mCanvas->drawPicture()? SkCanvas::drawPicture seems to be // where the logic is for playback vs. ref picture. Using picture.playback here diff --git a/libs/hwui/SkiaCanvas.h b/libs/hwui/SkiaCanvas.h index 584321ec9094..fa7d373308d3 100644 --- a/libs/hwui/SkiaCanvas.h +++ b/libs/hwui/SkiaCanvas.h @@ -147,6 +147,12 @@ public: uirenderer::CanvasPropertyPrimitive* y, uirenderer::CanvasPropertyPrimitive* radius, uirenderer::CanvasPropertyPaint* paint) override; + virtual void drawRipple(uirenderer::CanvasPropertyPrimitive* x, + uirenderer::CanvasPropertyPrimitive* y, + uirenderer::CanvasPropertyPrimitive* radius, + uirenderer::CanvasPropertyPaint* paint, + uirenderer::CanvasPropertyPrimitive* progress, + sk_sp<SkRuntimeEffect> runtimeEffect) override; virtual void drawLayer(uirenderer::DeferredLayerUpdater* layerHandle) override; virtual void drawRenderNode(uirenderer::RenderNode* renderNode) override; diff --git a/libs/hwui/canvas/CanvasOpTypes.h b/libs/hwui/canvas/CanvasOpTypes.h index f0aa7774a6cc..cde50bd66a15 100644 --- a/libs/hwui/canvas/CanvasOpTypes.h +++ b/libs/hwui/canvas/CanvasOpTypes.h @@ -42,6 +42,7 @@ enum class CanvasOpType : int8_t { DrawRoundRectProperty, DrawDoubleRoundRect, DrawCircleProperty, + DrawRippleProperty, DrawCircle, DrawOval, DrawArc, diff --git a/libs/hwui/canvas/CanvasOps.h b/libs/hwui/canvas/CanvasOps.h index 62c26c7b6f6a..ea9fea974d06 100644 --- a/libs/hwui/canvas/CanvasOps.h +++ b/libs/hwui/canvas/CanvasOps.h @@ -23,6 +23,7 @@ #include <SkVertices.h> #include <SkImage.h> #include <SkPicture.h> +#include <SkRuntimeEffect.h> #include <hwui/Bitmap.h> #include <log/log.h> #include "CanvasProperty.h" @@ -142,6 +143,42 @@ struct CanvasOp<CanvasOpType::DrawCircleProperty> { ASSERT_DRAWABLE() }; +template<> +struct CanvasOp<CanvasOpType::DrawRippleProperty> { + sp<uirenderer::CanvasPropertyPrimitive> x; + sp<uirenderer::CanvasPropertyPrimitive> y; + sp<uirenderer::CanvasPropertyPrimitive> radius; + sp<uirenderer::CanvasPropertyPaint> paint; + sp<uirenderer::CanvasPropertyPrimitive> progress; + sk_sp<SkRuntimeEffect> effect; + + void draw(SkCanvas* canvas) const { + SkRuntimeShaderBuilder runtimeEffectBuilder(effect); + + SkRuntimeShaderBuilder::BuilderUniform center = runtimeEffectBuilder.uniform("in_origin"); + if (center.fVar != nullptr) { + center = SkV2{x->value, y->value}; + } + + SkRuntimeShaderBuilder::BuilderUniform radiusU = + runtimeEffectBuilder.uniform("in_maxRadius"); + if (radiusU.fVar != nullptr) { + radiusU = radius->value; + } + + SkRuntimeShaderBuilder::BuilderUniform progressU = + runtimeEffectBuilder.uniform("in_progress"); + if (progressU.fVar != nullptr) { + progressU = progress->value; + } + + SkPaint paintMod = paint->value; + paintMod.setShader(runtimeEffectBuilder.makeShader(nullptr, false)); + canvas->drawCircle(x->value, y->value, radius->value, paintMod); + } + ASSERT_DRAWABLE() +}; + template <> struct CanvasOp<CanvasOpType::DrawColor> { SkColor4f color; diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h index 11fa3223a9c8..d0c996bee449 100644 --- a/libs/hwui/hwui/Canvas.h +++ b/libs/hwui/hwui/Canvas.h @@ -31,6 +31,7 @@ class SkAnimatedImage; class SkCanvasState; +class SkRuntimeEffect; class SkVertices; namespace minikin { @@ -133,6 +134,12 @@ public: uirenderer::CanvasPropertyPrimitive* y, uirenderer::CanvasPropertyPrimitive* radius, uirenderer::CanvasPropertyPaint* paint) = 0; + virtual void drawRipple(uirenderer::CanvasPropertyPrimitive* x, + uirenderer::CanvasPropertyPrimitive* y, + uirenderer::CanvasPropertyPrimitive* radius, + uirenderer::CanvasPropertyPaint* paint, + uirenderer::CanvasPropertyPrimitive* progress, + sk_sp<SkRuntimeEffect> runtimeEffect) = 0; virtual void drawLayer(uirenderer::DeferredLayerUpdater* layerHandle) = 0; virtual void drawRenderNode(uirenderer::RenderNode* renderNode) = 0; diff --git a/libs/hwui/hwui/ImageDecoder.cpp b/libs/hwui/hwui/ImageDecoder.cpp index 688913471913..b3f7627b1cf7 100644 --- a/libs/hwui/hwui/ImageDecoder.cpp +++ b/libs/hwui/hwui/ImageDecoder.cpp @@ -17,11 +17,19 @@ #include "ImageDecoder.h" #include <hwui/Bitmap.h> +#include <log/log.h> #include <SkAndroidCodec.h> +#include <SkBitmap.h> +#include <SkBlendMode.h> #include <SkCanvas.h> +#include <SkEncodedOrigin.h> +#include <SkFilterQuality.h> #include <SkPaint.h> +#undef LOG_TAG +#define LOG_TAG "ImageDecoder" + using namespace android; sk_sp<SkColorSpace> ImageDecoder::getDefaultColorSpace() const { @@ -44,17 +52,29 @@ ImageDecoder::ImageDecoder(std::unique_ptr<SkAndroidCodec> codec, sk_sp<SkPngChu , mOutColorType(mCodec->computeOutputColorType(kN32_SkColorType)) , mUnpremultipliedRequired(false) , mOutColorSpace(getDefaultColorSpace()) - , mSampleSize(1) { mTargetSize = swapWidthHeight() ? SkISize { mDecodeSize.height(), mDecodeSize.width() } : mDecodeSize; + this->rewind(); } +ImageDecoder::~ImageDecoder() = default; + SkAlphaType ImageDecoder::getOutAlphaType() const { return opaque() ? kOpaque_SkAlphaType : mUnpremultipliedRequired ? kUnpremul_SkAlphaType : kPremul_SkAlphaType; } +static SkISize swapped(const SkISize& size) { + return SkISize { size.height(), size.width() }; +} + +static bool requires_matrix_scaling(bool swapWidthHeight, const SkISize& decodeSize, + const SkISize& targetSize) { + return (swapWidthHeight && decodeSize != swapped(targetSize)) + || (!swapWidthHeight && decodeSize != targetSize); +} + bool ImageDecoder::setTargetSize(int width, int height) { if (width <= 0 || height <= 0) { return false; @@ -78,17 +98,21 @@ bool ImageDecoder::setTargetSize(int width, int height) { } } - SkISize targetSize = { width, height }; - SkISize decodeSize = swapWidthHeight() ? SkISize { height, width } : targetSize; + const bool swap = swapWidthHeight(); + const SkISize targetSize = { width, height }; + SkISize decodeSize = swap ? SkISize { height, width } : targetSize; int sampleSize = mCodec->computeSampleSize(&decodeSize); - if (decodeSize != targetSize && mUnpremultipliedRequired && !opaque()) { - return false; + if (mUnpremultipliedRequired && !opaque()) { + // Allow using a matrix to handle orientation, but not scaling. + if (requires_matrix_scaling(swap, decodeSize, targetSize)) { + return false; + } } mTargetSize = targetSize; mDecodeSize = decodeSize; - mSampleSize = sampleSize; + mOptions.fSampleSize = sampleSize; return true; } @@ -137,8 +161,10 @@ bool ImageDecoder::setOutColorType(SkColorType colorType) { } bool ImageDecoder::setUnpremultipliedRequired(bool required) { - if (required && !opaque() && mDecodeSize != mTargetSize) { - return false; + if (required && !opaque()) { + if (requires_matrix_scaling(swapWidthHeight(), mDecodeSize, mTargetSize)) { + return false; + } } mUnpremultipliedRequired = required; return true; @@ -176,26 +202,202 @@ int ImageDecoder::height() const { } bool ImageDecoder::opaque() const { - return mCodec->getInfo().alphaType() == kOpaque_SkAlphaType; + return mCurrentFrameIsOpaque; } bool ImageDecoder::gray() const { return mCodec->getInfo().colorType() == kGray_8_SkColorType; } +bool ImageDecoder::isAnimated() { + return mCodec->codec()->getFrameCount() > 1; +} + +int ImageDecoder::currentFrame() const { + return mOptions.fFrameIndex; +} + +bool ImageDecoder::rewind() { + mOptions.fFrameIndex = 0; + mOptions.fPriorFrame = SkCodec::kNoFrame; + mCurrentFrameIsIndependent = true; + mCurrentFrameIsOpaque = mCodec->getInfo().isOpaque(); + mRestoreState = RestoreState::kDoNothing; + mRestoreFrame = nullptr; + + // TODO: Rewind the input now instead of in the next call to decode, and + // plumb through whether rewind succeeded. + return true; +} + +bool ImageDecoder::advanceFrame() { + const int frameIndex = ++mOptions.fFrameIndex; + const int frameCount = mCodec->codec()->getFrameCount(); + if (frameIndex >= frameCount) { + // Prevent overflow from repeated calls to advanceFrame. + mOptions.fFrameIndex = frameCount; + return false; + } + + SkCodec::FrameInfo frameInfo; + if (!mCodec->codec()->getFrameInfo(frameIndex, &frameInfo) + || !frameInfo.fFullyReceived) { + // Mark the decoder as finished, requiring a rewind. + mOptions.fFrameIndex = frameCount; + return false; + } + + mCurrentFrameIsIndependent = frameInfo.fRequiredFrame == SkCodec::kNoFrame; + mCurrentFrameIsOpaque = frameInfo.fAlphaType == kOpaque_SkAlphaType; + + if (frameInfo.fDisposalMethod == SkCodecAnimation::DisposalMethod::kRestorePrevious) { + switch (mRestoreState) { + case RestoreState::kDoNothing: + case RestoreState::kNeedsRestore: + mRestoreState = RestoreState::kFirstRPFrame; + break; + case RestoreState::kFirstRPFrame: + mRestoreState = RestoreState::kRPFrame; + break; + case RestoreState::kRPFrame: + // Unchanged. + break; + } + } else { // New frame is not restore previous + switch (mRestoreState) { + case RestoreState::kFirstRPFrame: + case RestoreState::kRPFrame: + mRestoreState = RestoreState::kNeedsRestore; + break; + case RestoreState::kNeedsRestore: + mRestoreState = RestoreState::kDoNothing; + mRestoreFrame = nullptr; + [[fallthrough]]; + case RestoreState::kDoNothing: + mOptions.fPriorFrame = frameIndex - 1; + break; + } + } + + return true; +} + +SkCodec::FrameInfo ImageDecoder::getCurrentFrameInfo() { + LOG_ALWAYS_FATAL_IF(finished()); + + auto dims = mCodec->codec()->dimensions(); + SkCodec::FrameInfo info; + if (!mCodec->codec()->getFrameInfo(mOptions.fFrameIndex, &info)) { + // SkCodec may return false for a non-animated image. Provide defaults. + info.fRequiredFrame = SkCodec::kNoFrame; + info.fDuration = 0; + info.fFullyReceived = true; + info.fAlphaType = mCodec->codec()->getInfo().alphaType(); + info.fHasAlphaWithinBounds = info.fAlphaType != kOpaque_SkAlphaType; + info.fDisposalMethod = SkCodecAnimation::DisposalMethod::kKeep; + info.fBlend = SkCodecAnimation::Blend::kSrc; + info.fFrameRect = SkIRect::MakeSize(dims); + } + + if (auto origin = mCodec->codec()->getOrigin(); origin != kDefault_SkEncodedOrigin) { + if (SkEncodedOriginSwapsWidthHeight(origin)) { + dims = swapped(dims); + } + auto matrix = SkEncodedOriginToMatrix(origin, dims.width(), dims.height()); + auto rect = SkRect::Make(info.fFrameRect); + LOG_ALWAYS_FATAL_IF(!matrix.mapRect(&rect)); + rect.roundIn(&info.fFrameRect); + } + return info; +} + +bool ImageDecoder::finished() const { + return mOptions.fFrameIndex >= mCodec->codec()->getFrameCount(); +} + SkCodec::Result ImageDecoder::decode(void* pixels, size_t rowBytes) { + // This was checked inside setTargetSize, but it's possible the first frame + // was opaque, so that method succeeded, but after calling advanceFrame, the + // current frame is not opaque. + if (mUnpremultipliedRequired && !opaque()) { + // Allow using a matrix to handle orientation, but not scaling. + if (requires_matrix_scaling(swapWidthHeight(), mDecodeSize, mTargetSize)) { + return SkCodec::kInvalidScale; + } + } + void* decodePixels = pixels; size_t decodeRowBytes = rowBytes; - auto decodeInfo = SkImageInfo::Make(mDecodeSize, mOutColorType, getOutAlphaType(), - getOutputColorSpace()); + const auto decodeInfo = SkImageInfo::Make(mDecodeSize, mOutColorType, getOutAlphaType(), + getOutputColorSpace()); + const auto outputInfo = getOutputInfo(); + switch (mRestoreState) { + case RestoreState::kFirstRPFrame:{ + // This frame is marked kRestorePrevious. The prior frame should be in + // |pixels|, and it is what we'll restore after each consecutive + // kRestorePrevious frame. Cache it now. + if (!(mRestoreFrame = Bitmap::allocateHeapBitmap(outputInfo))) { + return SkCodec::kInternalError; + } + + const uint8_t* srcRow = static_cast<uint8_t*>(pixels); + uint8_t* dstRow = static_cast<uint8_t*>(mRestoreFrame->pixels()); + for (int y = 0; y < outputInfo.height(); y++) { + memcpy(dstRow, srcRow, outputInfo.minRowBytes()); + srcRow += rowBytes; + dstRow += mRestoreFrame->rowBytes(); + } + break; + } + case RestoreState::kRPFrame: + case RestoreState::kNeedsRestore: + // Restore the cached frame. It's possible that the client skipped decoding a frame, so + // we never cached it. + if (mRestoreFrame) { + const uint8_t* srcRow = static_cast<uint8_t*>(mRestoreFrame->pixels()); + uint8_t* dstRow = static_cast<uint8_t*>(pixels); + for (int y = 0; y < outputInfo.height(); y++) { + memcpy(dstRow, srcRow, outputInfo.minRowBytes()); + srcRow += mRestoreFrame->rowBytes(); + dstRow += rowBytes; + } + } + break; + case RestoreState::kDoNothing: + break; + } + // Used if we need a temporary before scaling or subsetting. // FIXME: Use scanline decoding on only a couple lines to save memory. b/70709380. SkBitmap tmp; const bool scale = mDecodeSize != mTargetSize; const auto origin = mCodec->codec()->getOrigin(); const bool handleOrigin = origin != kDefault_SkEncodedOrigin; + SkMatrix outputMatrix; if (scale || handleOrigin || mCropRect) { - if (!tmp.setInfo(decodeInfo)) { + if (mCropRect) { + outputMatrix.setTranslate(-mCropRect->fLeft, -mCropRect->fTop); + } + + int targetWidth = mTargetSize.width(); + int targetHeight = mTargetSize.height(); + if (handleOrigin) { + outputMatrix.preConcat(SkEncodedOriginToMatrix(origin, targetWidth, targetHeight)); + if (SkEncodedOriginSwapsWidthHeight(origin)) { + std::swap(targetWidth, targetHeight); + } + } + if (scale) { + float scaleX = (float) targetWidth / mDecodeSize.width(); + float scaleY = (float) targetHeight / mDecodeSize.height(); + outputMatrix.preScale(scaleX, scaleY); + } + // It's possible that this portion *does* have alpha, even if the + // composed frame does not. In that case, the SkBitmap needs to have + // alpha so it blends properly. + if (!tmp.setInfo(decodeInfo.makeAlphaType(mUnpremultipliedRequired ? kUnpremul_SkAlphaType + : kPremul_SkAlphaType))) + { return SkCodec::kInternalError; } if (!Bitmap::allocateHeapBitmap(&tmp)) { @@ -203,15 +405,28 @@ SkCodec::Result ImageDecoder::decode(void* pixels, size_t rowBytes) { } decodePixels = tmp.getPixels(); decodeRowBytes = tmp.rowBytes(); + + if (!mCurrentFrameIsIndependent) { + SkMatrix inverse; + if (outputMatrix.invert(&inverse)) { + SkCanvas canvas(tmp, SkCanvas::ColorBehavior::kLegacy); + canvas.setMatrix(inverse); + SkPaint paint; + paint.setFilterQuality(kLow_SkFilterQuality); // bilinear + SkBitmap priorFrame; + priorFrame.installPixels(outputInfo, pixels, rowBytes); + canvas.drawBitmap(priorFrame, 0, 0, &paint); + } else { + ALOGE("Failed to invert matrix!"); + } + } } - SkAndroidCodec::AndroidOptions options; - options.fSampleSize = mSampleSize; - auto result = mCodec->getAndroidPixels(decodeInfo, decodePixels, decodeRowBytes, &options); + auto result = mCodec->getAndroidPixels(decodeInfo, decodePixels, decodeRowBytes, &mOptions); if (scale || handleOrigin || mCropRect) { SkBitmap scaledBm; - if (!scaledBm.installPixels(getOutputInfo(), pixels, rowBytes)) { + if (!scaledBm.installPixels(outputInfo, pixels, rowBytes)) { return SkCodec::kInternalError; } @@ -220,26 +435,6 @@ SkCodec::Result ImageDecoder::decode(void* pixels, size_t rowBytes) { paint.setFilterQuality(kLow_SkFilterQuality); // bilinear filtering SkCanvas canvas(scaledBm, SkCanvas::ColorBehavior::kLegacy); - SkMatrix outputMatrix; - if (mCropRect) { - outputMatrix.setTranslate(-mCropRect->fLeft, -mCropRect->fTop); - } - - int targetWidth = mTargetSize.width(); - int targetHeight = mTargetSize.height(); - if (handleOrigin) { - outputMatrix.preConcat(SkEncodedOriginToMatrix(origin, targetWidth, targetHeight)); - if (SkEncodedOriginSwapsWidthHeight(origin)) { - std::swap(targetWidth, targetHeight); - } - } - - if (scale) { - float scaleX = (float) targetWidth / mDecodeSize.width(); - float scaleY = (float) targetHeight / mDecodeSize.height(); - outputMatrix.preScale(scaleX, scaleY); - } - canvas.setMatrix(outputMatrix); canvas.drawBitmap(tmp, 0.0f, 0.0f, &paint); } diff --git a/libs/hwui/hwui/ImageDecoder.h b/libs/hwui/hwui/ImageDecoder.h index a08e92478fb0..90261b16e57e 100644 --- a/libs/hwui/hwui/ImageDecoder.h +++ b/libs/hwui/hwui/ImageDecoder.h @@ -15,6 +15,7 @@ */ #pragma once +#include <SkAndroidCodec.h> #include <SkCodec.h> #include <SkImageInfo.h> #include <SkPngChunkReader.h> @@ -24,17 +25,18 @@ #include <optional> -class SkAndroidCodec; - namespace android { -class ANDROID_API ImageDecoder { +class Bitmap; + +class ANDROID_API ImageDecoder final { public: std::unique_ptr<SkAndroidCodec> mCodec; sk_sp<SkPngChunkReader> mPeeker; ImageDecoder(std::unique_ptr<SkAndroidCodec> codec, sk_sp<SkPngChunkReader> peeker = nullptr); + ~ImageDecoder(); bool setTargetSize(int width, int height); bool setCropRect(const SkIRect*); @@ -46,25 +48,65 @@ public: sk_sp<SkColorSpace> getDefaultColorSpace() const; void setOutColorSpace(sk_sp<SkColorSpace> cs); - // The size is the final size after scaling and cropping. + // The size is the final size after scaling, adjusting for the origin, and + // cropping. SkImageInfo getOutputInfo() const; int width() const; int height() const; + // True if the current frame is opaque. bool opaque() const; bool gray() const; SkCodec::Result decode(void* pixels, size_t rowBytes); + // Return true if the decoder has advanced beyond all frames. + bool finished() const; + + bool advanceFrame(); + bool rewind(); + + bool isAnimated(); + int currentFrame() const; + + SkCodec::FrameInfo getCurrentFrameInfo(); + private: + // State machine for keeping track of how to handle RestorePrevious (RP) + // frames in decode(). + enum class RestoreState { + // Neither this frame nor the prior is RP, so there is no need to cache + // or restore. + kDoNothing, + + // This is the first in a sequence of one or more RP frames. decode() + // needs to cache the provided pixels. + kFirstRPFrame, + + // This is the second (or later) in a sequence of multiple RP frames. + // decode() needs to restore the cached frame that preceded the first RP + // frame in the sequence. + kRPFrame, + + // This is the first non-RP frame after a sequence of one or more RP + // frames. decode() still needs to restore the cached frame. Separate + // from kRPFrame because if the following frame is RP the state will + // change to kFirstRPFrame. + kNeedsRestore, + }; + SkISize mTargetSize; SkISize mDecodeSize; SkColorType mOutColorType; bool mUnpremultipliedRequired; sk_sp<SkColorSpace> mOutColorSpace; - int mSampleSize; + SkAndroidCodec::AndroidOptions mOptions; + bool mCurrentFrameIsIndependent; + bool mCurrentFrameIsOpaque; + RestoreState mRestoreState; + sk_sp<Bitmap> mRestoreFrame; std::optional<SkIRect> mCropRect; ImageDecoder(const ImageDecoder&) = delete; diff --git a/libs/hwui/jni/android_graphics_DisplayListCanvas.cpp b/libs/hwui/jni/android_graphics_DisplayListCanvas.cpp index 7c1422de0984..f4877f4a4fc4 100644 --- a/libs/hwui/jni/android_graphics_DisplayListCanvas.cpp +++ b/libs/hwui/jni/android_graphics_DisplayListCanvas.cpp @@ -20,8 +20,8 @@ #include <utils/Looper.h> #endif -#include <SkBitmap.h> #include <SkRegion.h> +#include <SkRuntimeEffect.h> #include <Rect.h> #include <RenderNode.h> @@ -139,6 +139,21 @@ static void android_view_DisplayListCanvas_drawCircleProps(CRITICAL_JNI_PARAMS_C canvas->drawCircle(xProp, yProp, radiusProp, paintProp); } +static void android_view_DisplayListCanvas_drawRippleProps(CRITICAL_JNI_PARAMS_COMMA jlong canvasPtr, + jlong xPropPtr, jlong yPropPtr, + jlong radiusPropPtr, jlong paintPropPtr, + jlong progressPropPtr, jlong effectPtr) { + Canvas* canvas = reinterpret_cast<Canvas*>(canvasPtr); + CanvasPropertyPrimitive* xProp = reinterpret_cast<CanvasPropertyPrimitive*>(xPropPtr); + CanvasPropertyPrimitive* yProp = reinterpret_cast<CanvasPropertyPrimitive*>(yPropPtr); + CanvasPropertyPrimitive* radiusProp = reinterpret_cast<CanvasPropertyPrimitive*>(radiusPropPtr); + CanvasPropertyPaint* paintProp = reinterpret_cast<CanvasPropertyPaint*>(paintPropPtr); + CanvasPropertyPrimitive* progressProp = + reinterpret_cast<CanvasPropertyPrimitive*>(progressPropPtr); + SkRuntimeEffect* effect = reinterpret_cast<SkRuntimeEffect*>(effectPtr); + canvas->drawRipple(xProp, yProp, radiusProp, paintProp, progressProp, sk_ref_sp(effect)); +} + static void android_view_DisplayListCanvas_drawWebViewFunctor(CRITICAL_JNI_PARAMS_COMMA jlong canvasPtr, jint functor) { Canvas* canvas = reinterpret_cast<Canvas*>(canvasPtr); canvas->drawWebViewFunctor(functor); @@ -163,6 +178,7 @@ static JNINativeMethod gMethods[] = { { "nDrawCircle", "(JJJJJ)V", (void*) android_view_DisplayListCanvas_drawCircleProps }, { "nDrawRoundRect", "(JJJJJJJJ)V",(void*) android_view_DisplayListCanvas_drawRoundRectProps }, { "nDrawWebViewFunctor", "(JI)V", (void*) android_view_DisplayListCanvas_drawWebViewFunctor }, + { "nDrawRipple", "(JJJJJJJ)V", (void*) android_view_DisplayListCanvas_drawRippleProps }, }; int register_android_view_DisplayListCanvas(JNIEnv* env) { diff --git a/libs/hwui/pipeline/skia/AnimatedDrawables.h b/libs/hwui/pipeline/skia/AnimatedDrawables.h index bf19655825b3..3142d927d4c4 100644 --- a/libs/hwui/pipeline/skia/AnimatedDrawables.h +++ b/libs/hwui/pipeline/skia/AnimatedDrawables.h @@ -18,6 +18,7 @@ #include <SkCanvas.h> #include <SkDrawable.h> +#include <SkRuntimeEffect.h> #include <utils/RefBase.h> #include "CanvasProperty.h" @@ -54,6 +55,59 @@ private: sp<uirenderer::CanvasPropertyPaint> mPaint; }; +class AnimatedRipple : public SkDrawable { +public: + AnimatedRipple(uirenderer::CanvasPropertyPrimitive* x, uirenderer::CanvasPropertyPrimitive* y, + uirenderer::CanvasPropertyPrimitive* radius, + uirenderer::CanvasPropertyPaint* paint, + uirenderer::CanvasPropertyPrimitive* progress, + sk_sp<SkRuntimeEffect> runtimeEffect) + : mX(x) + , mY(y) + , mRadius(radius) + , mPaint(paint) + , mProgress(progress) + , mRuntimeEffectBuilder(std::move(runtimeEffect)) {} + +protected: + virtual SkRect onGetBounds() override { + const float x = mX->value; + const float y = mY->value; + const float radius = mRadius->value; + return SkRect::MakeLTRB(x - radius, y - radius, x + radius, y + radius); + } + virtual void onDraw(SkCanvas* canvas) override { + SkRuntimeShaderBuilder::BuilderUniform center = mRuntimeEffectBuilder.uniform("in_origin"); + if (center.fVar != nullptr) { + center = SkV2{mX->value, mY->value}; + } + + SkRuntimeShaderBuilder::BuilderUniform radiusU = + mRuntimeEffectBuilder.uniform("in_maxRadius"); + if (radiusU.fVar != nullptr) { + radiusU = mRadius->value; + } + + SkRuntimeShaderBuilder::BuilderUniform progressU = + mRuntimeEffectBuilder.uniform("in_progress"); + if (progressU.fVar != nullptr) { + progressU = mProgress->value; + } + + SkPaint paint = mPaint->value; + paint.setShader(mRuntimeEffectBuilder.makeShader(nullptr, false)); + canvas->drawCircle(mX->value, mY->value, mRadius->value, paint); + } + +private: + sp<uirenderer::CanvasPropertyPrimitive> mX; + sp<uirenderer::CanvasPropertyPrimitive> mY; + sp<uirenderer::CanvasPropertyPrimitive> mRadius; + sp<uirenderer::CanvasPropertyPaint> mPaint; + sp<uirenderer::CanvasPropertyPrimitive> mProgress; + SkRuntimeShaderBuilder mRuntimeEffectBuilder; +}; + class AnimatedCircle : public SkDrawable { public: AnimatedCircle(uirenderer::CanvasPropertyPrimitive* x, uirenderer::CanvasPropertyPrimitive* y, diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp index a43627803e71..7faebda8e043 100644 --- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp +++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp @@ -85,6 +85,16 @@ void SkiaRecordingCanvas::drawCircle(uirenderer::CanvasPropertyPrimitive* x, drawDrawable(mDisplayList->allocateDrawable<AnimatedCircle>(x, y, radius, paint)); } +void SkiaRecordingCanvas::drawRipple(uirenderer::CanvasPropertyPrimitive* x, + uirenderer::CanvasPropertyPrimitive* y, + uirenderer::CanvasPropertyPrimitive* radius, + uirenderer::CanvasPropertyPaint* paint, + uirenderer::CanvasPropertyPrimitive* progress, + sk_sp<SkRuntimeEffect> runtimeEffect) { + drawDrawable(mDisplayList->allocateDrawable<AnimatedRipple>(x, y, radius, paint, progress, + runtimeEffect)); +} + void SkiaRecordingCanvas::enableZ(bool enableZ) { if (mCurrentBarrier && enableZ) { // Already in a re-order section, nothing to do diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h index 83e934974afd..622df43b0528 100644 --- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h +++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h @@ -66,6 +66,12 @@ public: uirenderer::CanvasPropertyPrimitive* y, uirenderer::CanvasPropertyPrimitive* radius, uirenderer::CanvasPropertyPaint* paint) override; + virtual void drawRipple(uirenderer::CanvasPropertyPrimitive* x, + uirenderer::CanvasPropertyPrimitive* y, + uirenderer::CanvasPropertyPrimitive* radius, + uirenderer::CanvasPropertyPaint* paint, + uirenderer::CanvasPropertyPrimitive* progress, + sk_sp<SkRuntimeEffect> runtimeEffect) override; virtual void drawVectorDrawable(VectorDrawableRoot* vectorDrawable) override; diff --git a/media/java/android/media/ExifInterface.java b/media/java/android/media/ExifInterface.java index 8ea380a9f3b7..21f8623b1953 100644 --- a/media/java/android/media/ExifInterface.java +++ b/media/java/android/media/ExifInterface.java @@ -5019,13 +5019,18 @@ public class ExifInterface { @Override public int skipBytes(int byteCount) throws IOException { - int totalSkip = Math.min(byteCount, mLength - mPosition); - int skipped = 0; - while (skipped < totalSkip) { - skipped += mDataInputStream.skipBytes(totalSkip - skipped); + int totalBytesToSkip = Math.min(byteCount, mLength - mPosition); + int totalSkipped = 0; + while (totalSkipped < totalBytesToSkip) { + int skipped = mDataInputStream.skipBytes(totalBytesToSkip - totalSkipped); + if (skipped > 0) { + totalSkipped += skipped; + } else { + break; + } } - mPosition += skipped; - return skipped; + mPosition += totalSkipped; + return totalSkipped; } public int readUnsignedShort() throws IOException { diff --git a/media/java/android/media/musicrecognition/OWNERS b/media/java/android/media/musicrecognition/OWNERS new file mode 100644 index 000000000000..58f5d40dd8c3 --- /dev/null +++ b/media/java/android/media/musicrecognition/OWNERS @@ -0,0 +1,6 @@ +# Bug component: 830636 + +joannechung@google.com +oni@google.com +volnov@google.com + diff --git a/media/java/android/media/session/MediaSessionManager.java b/media/java/android/media/session/MediaSessionManager.java index f22bcd88f8c3..1fd132d00f10 100644 --- a/media/java/android/media/session/MediaSessionManager.java +++ b/media/java/android/media/session/MediaSessionManager.java @@ -547,6 +547,7 @@ public final class MediaSessionManager { * @param keyEvent the key event to send * @hide */ + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) public void dispatchMediaKeyEvent(@NonNull KeyEvent keyEvent) { dispatchMediaKeyEventInternal(keyEvent, /*asSystemService=*/false, /*needWakeLock=*/false); } @@ -558,6 +559,7 @@ public final class MediaSessionManager { * @param needWakeLock true if a wake lock should be held while sending the key * @hide */ + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) public void dispatchMediaKeyEvent(@NonNull KeyEvent keyEvent, boolean needWakeLock) { dispatchMediaKeyEventInternal(keyEvent, /*asSystemService=*/false, needWakeLock); } @@ -630,6 +632,7 @@ public final class MediaSessionManager { * @param musicOnly true if key event should only be sent to music stream * @hide */ + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) public void dispatchVolumeKeyEvent(@NonNull KeyEvent keyEvent, int streamType, boolean musicOnly) { dispatchVolumeKeyEventInternal(keyEvent, streamType, musicOnly, /*asSystemService=*/false); diff --git a/media/java/android/media/tv/tuner/frontend/FrontendInfo.java b/media/java/android/media/tv/tuner/frontend/FrontendInfo.java index 67d6e9d266c8..e96cae6ba1bc 100644 --- a/media/java/android/media/tv/tuner/frontend/FrontendInfo.java +++ b/media/java/android/media/tv/tuner/frontend/FrontendInfo.java @@ -46,6 +46,10 @@ public class FrontendInfo { FrontendCapabilities frontendCap) { mId = id; mType = type; + // if max Frequency is negative, we set it as max value of the Integer. + if (maxFrequency < 0) { + maxFrequency = Integer.MAX_VALUE; + } mFrequencyRange = new Range<>(minFrequency, maxFrequency); mSymbolRateRange = new Range<>(minSymbolRate, maxSymbolRate); mAcquireRange = acquireRange; diff --git a/native/graphics/jni/imagedecoder.cpp b/native/graphics/jni/imagedecoder.cpp index 4aeebe47f1ae..0f6190722aa3 100644 --- a/native/graphics/jni/imagedecoder.cpp +++ b/native/graphics/jni/imagedecoder.cpp @@ -61,6 +61,37 @@ int ResultToErrorCode(SkCodec::Result result) { } } +const char* AImageDecoder_resultToString(int result) { + switch (result) { + case ANDROID_IMAGE_DECODER_SUCCESS: + return "ANDROID_IMAGE_DECODER_SUCCESS"; + case ANDROID_IMAGE_DECODER_INCOMPLETE: + return "ANDROID_IMAGE_DECODER_INCOMPLETE"; + case ANDROID_IMAGE_DECODER_ERROR: + return "ANDROID_IMAGE_DECODER_ERROR"; + case ANDROID_IMAGE_DECODER_INVALID_CONVERSION: + return "ANDROID_IMAGE_DECODER_INVALID_CONVERSION"; + case ANDROID_IMAGE_DECODER_INVALID_SCALE: + return "ANDROID_IMAGE_DECODER_INVALID_SCALE"; + case ANDROID_IMAGE_DECODER_BAD_PARAMETER: + return "ANDROID_IMAGE_DECODER_BAD_PARAMETER"; + case ANDROID_IMAGE_DECODER_INVALID_INPUT: + return "ANDROID_IMAGE_DECODER_INVALID_INPUT"; + case ANDROID_IMAGE_DECODER_SEEK_ERROR: + return "ANDROID_IMAGE_DECODER_SEEK_ERROR"; + case ANDROID_IMAGE_DECODER_INTERNAL_ERROR: + return "ANDROID_IMAGE_DECODER_INTERNAL_ERROR"; + case ANDROID_IMAGE_DECODER_UNSUPPORTED_FORMAT: + return "ANDROID_IMAGE_DECODER_UNSUPPORTED_FORMAT"; + case ANDROID_IMAGE_DECODER_FINISHED: + return "ANDROID_IMAGE_DECODER_FINISHED"; + case ANDROID_IMAGE_DECODER_INVALID_STATE: + return "ANDROID_IMAGE_DECODER_INVALID_STATE"; + default: + return nullptr; + } +} + static int createFromStream(std::unique_ptr<SkStreamRewindable> stream, AImageDecoder** outDecoder) { SkCodec::Result result; auto codec = SkCodec::MakeFromStream(std::move(stream), &result, nullptr, @@ -173,7 +204,13 @@ int AImageDecoder_setAndroidBitmapFormat(AImageDecoder* decoder, int32_t format) || format > ANDROID_BITMAP_FORMAT_RGBA_F16) { return ANDROID_IMAGE_DECODER_BAD_PARAMETER; } - return toDecoder(decoder)->setOutColorType(getColorType((AndroidBitmapFormat) format)) + + auto* imageDecoder = toDecoder(decoder); + if (imageDecoder->currentFrame() != 0) { + return ANDROID_IMAGE_DECODER_INVALID_STATE; + } + + return imageDecoder->setOutColorType(getColorType((AndroidBitmapFormat) format)) ? ANDROID_IMAGE_DECODER_SUCCESS : ANDROID_IMAGE_DECODER_INVALID_CONVERSION; } @@ -185,6 +222,10 @@ int AImageDecoder_setDataSpace(AImageDecoder* decoder, int32_t dataspace) { } ImageDecoder* imageDecoder = toDecoder(decoder); + if (imageDecoder->currentFrame() != 0) { + return ANDROID_IMAGE_DECODER_INVALID_STATE; + } + imageDecoder->setOutColorSpace(std::move(cs)); return ANDROID_IMAGE_DECODER_SUCCESS; } @@ -279,7 +320,12 @@ int AImageDecoder_setUnpremultipliedRequired(AImageDecoder* decoder, bool requir return ANDROID_IMAGE_DECODER_BAD_PARAMETER; } - return toDecoder(decoder)->setUnpremultipliedRequired(required) + auto* imageDecoder = toDecoder(decoder); + if (imageDecoder->currentFrame() != 0) { + return ANDROID_IMAGE_DECODER_INVALID_STATE; + } + + return imageDecoder->setUnpremultipliedRequired(required) ? ANDROID_IMAGE_DECODER_SUCCESS : ANDROID_IMAGE_DECODER_INVALID_CONVERSION; } @@ -288,7 +334,12 @@ int AImageDecoder_setTargetSize(AImageDecoder* decoder, int32_t width, int32_t h return ANDROID_IMAGE_DECODER_BAD_PARAMETER; } - return toDecoder(decoder)->setTargetSize(width, height) + auto* imageDecoder = toDecoder(decoder); + if (imageDecoder->currentFrame() != 0) { + return ANDROID_IMAGE_DECODER_INVALID_STATE; + } + + return imageDecoder->setTargetSize(width, height) ? ANDROID_IMAGE_DECODER_SUCCESS : ANDROID_IMAGE_DECODER_INVALID_SCALE; } @@ -309,10 +360,15 @@ int AImageDecoder_setCrop(AImageDecoder* decoder, ARect crop) { return ANDROID_IMAGE_DECODER_BAD_PARAMETER; } + auto* imageDecoder = toDecoder(decoder); + if (imageDecoder->currentFrame() != 0) { + return ANDROID_IMAGE_DECODER_INVALID_STATE; + } + SkIRect cropIRect; cropIRect.setLTRB(crop.left, crop.top, crop.right, crop.bottom); SkIRect* cropPtr = cropIRect == SkIRect::MakeEmpty() ? nullptr : &cropIRect; - return toDecoder(decoder)->setCropRect(cropPtr) + return imageDecoder->setCropRect(cropPtr) ? ANDROID_IMAGE_DECODER_SUCCESS : ANDROID_IMAGE_DECODER_BAD_PARAMETER; } @@ -341,6 +397,10 @@ int AImageDecoder_decodeImage(AImageDecoder* decoder, return ANDROID_IMAGE_DECODER_BAD_PARAMETER; } + if (imageDecoder->finished()) { + return ANDROID_IMAGE_DECODER_FINISHED; + } + return ResultToErrorCode(imageDecoder->decode(pixels, stride)); } @@ -352,7 +412,7 @@ bool AImageDecoder_isAnimated(AImageDecoder* decoder) { if (!decoder) return false; ImageDecoder* imageDecoder = toDecoder(decoder); - return imageDecoder->mCodec->codec()->getFrameCount() > 1; + return imageDecoder->isAnimated(); } int32_t AImageDecoder_getRepeatCount(AImageDecoder* decoder) { @@ -369,3 +429,109 @@ int32_t AImageDecoder_getRepeatCount(AImageDecoder* decoder) { } return count; } + +int AImageDecoder_advanceFrame(AImageDecoder* decoder) { + if (!decoder) return ANDROID_IMAGE_DECODER_BAD_PARAMETER; + + ImageDecoder* imageDecoder = toDecoder(decoder); + if (!imageDecoder->isAnimated()) { + return ANDROID_IMAGE_DECODER_BAD_PARAMETER; + } + + if (imageDecoder->advanceFrame()) { + return ANDROID_IMAGE_DECODER_SUCCESS; + } + + if (imageDecoder->finished()) { + return ANDROID_IMAGE_DECODER_FINISHED; + } + + return ANDROID_IMAGE_DECODER_INCOMPLETE; +} + +int AImageDecoder_rewind(AImageDecoder* decoder) { + if (!decoder) return ANDROID_IMAGE_DECODER_BAD_PARAMETER; + + ImageDecoder* imageDecoder = toDecoder(decoder); + if (!imageDecoder->isAnimated()) { + return ANDROID_IMAGE_DECODER_BAD_PARAMETER; + } + + return imageDecoder->rewind() ? ANDROID_IMAGE_DECODER_SUCCESS + : ANDROID_IMAGE_DECODER_SEEK_ERROR; +} + +AImageDecoderFrameInfo* AImageDecoderFrameInfo_create() { + return reinterpret_cast<AImageDecoderFrameInfo*>(new SkCodec::FrameInfo); +} + +static SkCodec::FrameInfo* toFrameInfo(AImageDecoderFrameInfo* info) { + return reinterpret_cast<SkCodec::FrameInfo*>(info); +} + +static const SkCodec::FrameInfo* toFrameInfo(const AImageDecoderFrameInfo* info) { + return reinterpret_cast<const SkCodec::FrameInfo*>(info); +} + +void AImageDecoderFrameInfo_delete(AImageDecoderFrameInfo* info) { + delete toFrameInfo(info); +} + +int AImageDecoder_getFrameInfo(AImageDecoder* decoder, + AImageDecoderFrameInfo* info) { + if (!decoder || !info) { + return ANDROID_IMAGE_DECODER_BAD_PARAMETER; + } + + auto* imageDecoder = toDecoder(decoder); + if (imageDecoder->finished()) { + return ANDROID_IMAGE_DECODER_FINISHED; + } + + *toFrameInfo(info) = imageDecoder->getCurrentFrameInfo(); + return ANDROID_IMAGE_DECODER_SUCCESS; +} + +int64_t AImageDecoderFrameInfo_getDuration(const AImageDecoderFrameInfo* info) { + if (!info) return 0; + + return toFrameInfo(info)->fDuration * 1'000'000; +} + +ARect AImageDecoderFrameInfo_getFrameRect(const AImageDecoderFrameInfo* info) { + if (!info) { + return { 0, 0, 0, 0}; + } + + const SkIRect& r = toFrameInfo(info)->fFrameRect; + return { r.left(), r.top(), r.right(), r.bottom() }; +} + +bool AImageDecoderFrameInfo_hasAlphaWithinBounds(const AImageDecoderFrameInfo* info) { + if (!info) return false; + + return toFrameInfo(info)->fHasAlphaWithinBounds; +} + +int32_t AImageDecoderFrameInfo_getDisposeOp(const AImageDecoderFrameInfo* info) { + if (!info) return ANDROID_IMAGE_DECODER_BAD_PARAMETER; + + static_assert(static_cast<int>(SkCodecAnimation::DisposalMethod::kKeep) + == ANDROID_IMAGE_DECODER_DISPOSE_OP_NONE); + static_assert(static_cast<int>(SkCodecAnimation::DisposalMethod::kRestoreBGColor) + == ANDROID_IMAGE_DECODER_DISPOSE_OP_BACKGROUND); + static_assert(static_cast<int>(SkCodecAnimation::DisposalMethod::kRestorePrevious) + == ANDROID_IMAGE_DECODER_DISPOSE_OP_PREVIOUS); + return static_cast<int>(toFrameInfo(info)->fDisposalMethod); +} + +int32_t AImageDecoderFrameInfo_getBlendOp(const AImageDecoderFrameInfo* info) { + if (!info) return ANDROID_IMAGE_DECODER_BAD_PARAMETER; + + switch (toFrameInfo(info)->fBlend) { + case SkCodecAnimation::Blend::kSrc: + return ANDROID_IMAGE_DECODER_BLEND_OP_SRC; + case SkCodecAnimation::Blend::kSrcOver: + return ANDROID_IMAGE_DECODER_BLEND_OP_SRC_OVER; + } +} diff --git a/native/graphics/jni/libjnigraphics.map.txt b/native/graphics/jni/libjnigraphics.map.txt index a184ab9d42e9..d8c3cefd30ca 100644 --- a/native/graphics/jni/libjnigraphics.map.txt +++ b/native/graphics/jni/libjnigraphics.map.txt @@ -1,5 +1,6 @@ LIBJNIGRAPHICS { global: + AImageDecoder_resultToString; # introduced=31 AImageDecoder_createFromAAsset; # introduced=30 AImageDecoder_createFromFd; # introduced=30 AImageDecoder_createFromBuffer; # introduced=30 @@ -15,12 +16,22 @@ LIBJNIGRAPHICS { AImageDecoder_setCrop; # introduced=30 AImageDecoder_isAnimated; # introduced=31 AImageDecoder_getRepeatCount; # introduced=31 + AImageDecoder_advanceFrame; # introduced=31 + AImageDecoder_rewind; # introduced=31 + AImageDecoder_getFrameInfo; # introduced = 31 AImageDecoderHeaderInfo_getWidth; # introduced=30 AImageDecoderHeaderInfo_getHeight; # introduced=30 AImageDecoderHeaderInfo_getMimeType; # introduced=30 AImageDecoderHeaderInfo_getAlphaFlags; # introduced=30 AImageDecoderHeaderInfo_getAndroidBitmapFormat; # introduced=30 AImageDecoderHeaderInfo_getDataSpace; # introduced=30 + AImageDecoderFrameInfo_create; # introduced = 31 + AImageDecoderFrameInfo_delete; # introduced = 31 + AImageDecoderFrameInfo_getDuration; # introduced = 31 + AImageDecoderFrameInfo_getFrameRect; # introduced = 31 + AImageDecoderFrameInfo_hasAlphaWithinBounds; # introduced = 31 + AImageDecoderFrameInfo_getDisposeOp; # introduced = 31 + AImageDecoderFrameInfo_getBlendOp; # introduced = 31 AndroidBitmap_getInfo; AndroidBitmap_getDataSpace; AndroidBitmap_lockPixels; diff --git a/native/webview/TEST_MAPPING b/native/webview/TEST_MAPPING new file mode 100644 index 000000000000..bd25200ffc38 --- /dev/null +++ b/native/webview/TEST_MAPPING @@ -0,0 +1,36 @@ +{ + "presubmit": [ + { + "name": "CtsWebkitTestCases", + "options": [ + { + "exclude-annotation": "androidx.test.filters.FlakyTest" + } + ] + }, + { + "name": "CtsHostsideWebViewTests", + "options": [ + { + "exclude-annotation": "androidx.test.filters.FlakyTest" + } + ] + }, + { + "name": "GtsWebViewTestCases", + "options": [ + { + "exclude-annotation": "android.test.FlakyTest" + } + ] + }, + { + "name": "GtsWebViewHostTestCases", + "options": [ + { + "exclude-annotation": "android.test.FlakyTest" + } + ] + } + ] +} diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java index 1f3ee6d27685..b0c316908a10 100644 --- a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java +++ b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java @@ -347,17 +347,19 @@ public class PackageInstallerActivity extends AlertActivity { if (!wasSetUp) { return; } - - // load dummy layout with OK button disabled until we override this layout in - // startInstallConfirm - bindUi(); - checkIfAllowedAndInitiateInstall(); } @Override protected void onResume() { super.onResume(); + if (mAppSnippet != null) { + // load dummy layout with OK button disabled until we override this layout in + // startInstallConfirm + bindUi(); + checkIfAllowedAndInitiateInstall(); + } + if (mOk != null) { mOk.setEnabled(mEnableOk); } diff --git a/packages/SettingsLib/EmergencyNumber/src/com/android/settingslib/emergencynumber/EmergencyNumberUtils.java b/packages/SettingsLib/EmergencyNumber/src/com/android/settingslib/emergencynumber/EmergencyNumberUtils.java index 1c0e718a17b2..4d454941af7c 100644 --- a/packages/SettingsLib/EmergencyNumber/src/com/android/settingslib/emergencynumber/EmergencyNumberUtils.java +++ b/packages/SettingsLib/EmergencyNumber/src/com/android/settingslib/emergencynumber/EmergencyNumberUtils.java @@ -19,9 +19,11 @@ package com.android.settingslib.emergencynumber; import static android.telephony.emergency.EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DATABASE; import static android.telephony.emergency.EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_POLICE; +import android.content.ContentResolver; import android.content.Context; import android.content.pm.PackageManager; -import android.provider.Settings; +import android.net.Uri; +import android.os.Bundle; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; import android.telephony.emergency.EmergencyNumber; @@ -40,7 +42,16 @@ import java.util.Map; */ public class EmergencyNumberUtils { private static final String TAG = "EmergencyNumberUtils"; - private static final String EMERGENCY_GESTURE_CALL_NUMBER = "emergency_gesture_call_number"; + + public static final Uri EMERGENCY_NUMBER_OVERRIDE_AUTHORITY = new Uri.Builder().scheme( + ContentResolver.SCHEME_CONTENT) + .authority("com.android.emergency.numbers") + .build(); + public static final String METHOD_NAME_GET_EMERGENCY_NUMBER_OVERRIDE = + "GET_EMERGENCY_NUMBER_OVERRIDE"; + public static final String METHOD_NAME_SET_EMERGENCY_NUMBER_OVERRIDE = + "SET_EMERGENCY_NUMBER_OVERRIDE"; + public static final String EMERGENCY_GESTURE_CALL_NUMBER = "emergency_gesture_call_number"; @VisibleForTesting static final String FALL_BACK_NUMBER = "112"; @@ -77,12 +88,28 @@ public class EmergencyNumberUtils { * #getDefaultPoliceNumber()}). */ public String getPoliceNumber() { - final String userProvidedNumber = Settings.Secure.getString(mContext.getContentResolver(), - EMERGENCY_GESTURE_CALL_NUMBER); + final String userProvidedNumber = getEmergencyNumberOverride(); return TextUtils.isEmpty(userProvidedNumber) ? getDefaultPoliceNumber() : userProvidedNumber; } + /** + * Sets device-local emergency number override + */ + public void setEmergencyNumberOverride(String number) { + final Bundle bundle = new Bundle(); + bundle.putString(EMERGENCY_GESTURE_CALL_NUMBER, number); + mContext.getContentResolver().call(EMERGENCY_NUMBER_OVERRIDE_AUTHORITY, + METHOD_NAME_SET_EMERGENCY_NUMBER_OVERRIDE, null /* args */, bundle); + } + + private String getEmergencyNumberOverride() { + final Bundle bundle = mContext.getContentResolver().call( + EMERGENCY_NUMBER_OVERRIDE_AUTHORITY, + METHOD_NAME_GET_EMERGENCY_NUMBER_OVERRIDE, null /* args */, null /* bundle */); + return bundle == null ? null : bundle.getString(EMERGENCY_GESTURE_CALL_NUMBER); + } + private List<EmergencyNumber> getPromotedEmergencyNumbers(int categories) { // TODO(b/171542607): Use platform API when its bug is fixed. Map<Integer, List<EmergencyNumber>> allLists = filterEmergencyNumbersByCategories( diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml index c2b26bce6e1a..d76a8a177449 100644 --- a/packages/SettingsLib/res/values-af/strings.xml +++ b/packages/SettingsLib/res/values-af/strings.xml @@ -553,7 +553,8 @@ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Skep tans nuwe gebruiker …"</string> <string name="user_nickname" msgid="262624187455825083">"Bynaam"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Voeg gas by"</string> - <string name="guest_exit_guest" msgid="5908239569510734136">"Verwyder gas"</string> + <!-- no translation found for guest_exit_guest (4754204715192830850) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"Gas"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Neem \'n foto"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Kies \'n prent"</string> diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml index 65ce6e0200f4..85b5bc2a574e 100644 --- a/packages/SettingsLib/res/values-am/strings.xml +++ b/packages/SettingsLib/res/values-am/strings.xml @@ -553,7 +553,8 @@ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"አዲስ ተጠቃሚ በመፍጠር ላይ…"</string> <string name="user_nickname" msgid="262624187455825083">"ቅጽል ስም"</string> <string name="guest_new_guest" msgid="3482026122932643557">"እንግዳን አክል"</string> - <string name="guest_exit_guest" msgid="5908239569510734136">"እንግዳን አስወግድ"</string> + <!-- no translation found for guest_exit_guest (4754204715192830850) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"እንግዳ"</string> <string name="user_image_take_photo" msgid="467512954561638530">"ፎቶ አንሳ"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"ምስል ይምረጡ"</string> diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml index 3ea96c7dbf25..f2605597c803 100644 --- a/packages/SettingsLib/res/values-ar/strings.xml +++ b/packages/SettingsLib/res/values-ar/strings.xml @@ -450,7 +450,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> إلى أن يتم شحن الجهاز بالكامل"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> إلى أن يتم شحن الجهاز بالكامل"</string> - <string name="power_charging_limited" msgid="1956874810658999681">"<xliff:g id="LEVEL">%1$s</xliff:g> - التحسين لسلامة البطارية"</string> + <string name="power_charging_limited" msgid="1956874810658999681">"<xliff:g id="LEVEL">%1$s</xliff:g> - التحسين للحفاظ على سلامة البطارية"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"غير معروف"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"جارٍ الشحن"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"جارٍ الشحن سريعًا"</string> @@ -557,7 +557,8 @@ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"جارٍ إنشاء مستخدم جديد…"</string> <string name="user_nickname" msgid="262624187455825083">"اللقب"</string> <string name="guest_new_guest" msgid="3482026122932643557">"إضافة ضيف"</string> - <string name="guest_exit_guest" msgid="5908239569510734136">"إزالة جلسة الضيف"</string> + <!-- no translation found for guest_exit_guest (4754204715192830850) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"ضيف"</string> <string name="user_image_take_photo" msgid="467512954561638530">"التقاط صورة"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"اختيار صورة"</string> diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml index a2596878e33f..32be72d32a12 100644 --- a/packages/SettingsLib/res/values-as/strings.xml +++ b/packages/SettingsLib/res/values-as/strings.xml @@ -553,7 +553,7 @@ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"নতুন ব্যৱহাৰকাৰী সৃষ্টি কৰি থকা হৈছে…"</string> <string name="user_nickname" msgid="262624187455825083">"উপনাম"</string> <string name="guest_new_guest" msgid="3482026122932643557">"অতিথি যোগ কৰক"</string> - <string name="guest_exit_guest" msgid="5908239569510734136">"অতিথি আঁতৰাওক"</string> + <string name="guest_exit_guest" msgid="4754204715192830850">"অতিথিৰ ছেশ্বন সমাপ্ত কৰক"</string> <string name="guest_nickname" msgid="6332276931583337261">"অতিথি"</string> <string name="user_image_take_photo" msgid="467512954561638530">"এখন ফট’ তোলক"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"এখন প্ৰতিচ্ছবি বাছনি কৰক"</string> diff --git a/packages/SettingsLib/res/values-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml index 2cbd681cef45..88e99ce3c9fb 100644 --- a/packages/SettingsLib/res/values-az/strings.xml +++ b/packages/SettingsLib/res/values-az/strings.xml @@ -553,7 +553,8 @@ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Yeni istifadəçi yaradılır…"</string> <string name="user_nickname" msgid="262624187455825083">"Ləqəb"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Qonaq əlavə edin"</string> - <string name="guest_exit_guest" msgid="5908239569510734136">"Qonağı silin"</string> + <!-- no translation found for guest_exit_guest (4754204715192830850) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"Qonaq"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Foto çəkin"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Şəkil seçin"</string> diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml index 4d8e348cb746..4a820ea520d8 100644 --- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml +++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml @@ -450,7 +450,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Napuniće se za <xliff:g id="TIME">%1$s</xliff:g>"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – napuniće se za <xliff:g id="TIME">%2$s</xliff:g>"</string> - <string name="power_charging_limited" msgid="1956874810658999681">"<xliff:g id="LEVEL">%1$s</xliff:g> – Optimizuje se radi stanja baterije"</string> + <string name="power_charging_limited" msgid="1956874810658999681">"<xliff:g id="LEVEL">%1$s</xliff:g> – Optimizuje se radi boljeg stanja baterije"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Nepoznato"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Puni se"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Brzo se puni"</string> @@ -554,7 +554,8 @@ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Pravi se novi korisnik…"</string> <string name="user_nickname" msgid="262624187455825083">"Nadimak"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Dodaj gosta"</string> - <string name="guest_exit_guest" msgid="5908239569510734136">"Ukloni gosta"</string> + <!-- no translation found for guest_exit_guest (4754204715192830850) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"Gost"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Slikaj"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Odaberi sliku"</string> diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml index 4d6f3ac6a634..6a16246de0a3 100644 --- a/packages/SettingsLib/res/values-be/strings.xml +++ b/packages/SettingsLib/res/values-be/strings.xml @@ -555,7 +555,8 @@ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Ствараецца новы карыстальнік…"</string> <string name="user_nickname" msgid="262624187455825083">"Псеўданім"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Дадаць госця"</string> - <string name="guest_exit_guest" msgid="5908239569510734136">"Выдаліць госця"</string> + <!-- no translation found for guest_exit_guest (4754204715192830850) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"Госць"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Зрабіць фота"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Выбраць відарыс"</string> diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml index ef0412b1485d..07ba66a59e92 100644 --- a/packages/SettingsLib/res/values-bg/strings.xml +++ b/packages/SettingsLib/res/values-bg/strings.xml @@ -450,7 +450,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Оставащо време до пълно зареждане: <xliff:g id="TIME">%1$s</xliff:g>"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до пълно зареждане"</string> - <string name="power_charging_limited" msgid="1956874810658999681">"<xliff:g id="LEVEL">%1$s</xliff:g> – Оптимизиране за състоян. на батерията"</string> + <string name="power_charging_limited" msgid="1956874810658999681">"<xliff:g id="LEVEL">%1$s</xliff:g> – Оптимизиране с цел състоянието на батерията"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Неизвестно"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Зарежда се"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Зарежда се бързо"</string> @@ -553,7 +553,7 @@ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Създава се нов потребител…"</string> <string name="user_nickname" msgid="262624187455825083">"Псевдоним"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Добавяне на гост"</string> - <string name="guest_exit_guest" msgid="5908239569510734136">"Премахване на госта"</string> + <string name="guest_exit_guest" msgid="4754204715192830850">"Прекратяване на сесията като гост"</string> <string name="guest_nickname" msgid="6332276931583337261">"Гост"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Правене на снимка"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Избиране на изображение"</string> diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml index 5b95399e83b8..99ee1d646346 100644 --- a/packages/SettingsLib/res/values-bn/strings.xml +++ b/packages/SettingsLib/res/values-bn/strings.xml @@ -553,7 +553,8 @@ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"নতুন ব্যবহারকারী তৈরি করা হচ্ছে…"</string> <string name="user_nickname" msgid="262624187455825083">"বিশেষ নাম"</string> <string name="guest_new_guest" msgid="3482026122932643557">"অতিথি যোগ করুন"</string> - <string name="guest_exit_guest" msgid="5908239569510734136">"অতিথি সরান"</string> + <!-- no translation found for guest_exit_guest (4754204715192830850) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"অতিথি"</string> <string name="user_image_take_photo" msgid="467512954561638530">"ফটো তুলুন"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"একটি ইমেজ বেছে নিন"</string> diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml index 00d8087e5483..eedad341189e 100644 --- a/packages/SettingsLib/res/values-bs/strings.xml +++ b/packages/SettingsLib/res/values-bs/strings.xml @@ -554,7 +554,8 @@ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Kreiranje novog korisnika…"</string> <string name="user_nickname" msgid="262624187455825083">"Nadimak"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Dodaj gosta"</string> - <string name="guest_exit_guest" msgid="5908239569510734136">"Ukloni gosta"</string> + <!-- no translation found for guest_exit_guest (4754204715192830850) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"Gost"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Snimite fotografiju"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Odaberite sliku"</string> diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml index 98d502d050eb..209b2d601933 100644 --- a/packages/SettingsLib/res/values-ca/strings.xml +++ b/packages/SettingsLib/res/values-ca/strings.xml @@ -450,7 +450,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> per completar la càrrega"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="TIME">%2$s</xliff:g> per completar la càrrega"</string> - <string name="power_charging_limited" msgid="1956874810658999681">"<xliff:g id="LEVEL">%1$s</xliff:g>: optimitzant per a l\'estat de la bateria"</string> + <string name="power_charging_limited" msgid="1956874810658999681">"<xliff:g id="LEVEL">%1$s</xliff:g>: s\'està optimitzant per preservar l\'estat de la bateria"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Desconegut"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"S\'està carregant"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Carregant ràpidament"</string> @@ -553,7 +553,8 @@ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"S\'està creant l\'usuari…"</string> <string name="user_nickname" msgid="262624187455825083">"Àlies"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Afegeix un convidat"</string> - <string name="guest_exit_guest" msgid="5908239569510734136">"Suprimeix el convidat"</string> + <!-- no translation found for guest_exit_guest (4754204715192830850) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"Convidat"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Fes una foto"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Tria una imatge"</string> diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml index a9a5f483eaa3..b3141e405860 100644 --- a/packages/SettingsLib/res/values-cs/strings.xml +++ b/packages/SettingsLib/res/values-cs/strings.xml @@ -555,7 +555,8 @@ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Vytváření nového uživatele…"</string> <string name="user_nickname" msgid="262624187455825083">"Přezdívka"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Přidat hosta"</string> - <string name="guest_exit_guest" msgid="5908239569510734136">"Odstranit hosta"</string> + <!-- no translation found for guest_exit_guest (4754204715192830850) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"Host"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Pořídit fotku"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Vybrat obrázek"</string> diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml index 738ed3c697a8..a7fa91f34dda 100644 --- a/packages/SettingsLib/res/values-da/strings.xml +++ b/packages/SettingsLib/res/values-da/strings.xml @@ -553,7 +553,8 @@ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Opretter ny bruger…"</string> <string name="user_nickname" msgid="262624187455825083">"Kaldenavn"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Tilføj gæsten"</string> - <string name="guest_exit_guest" msgid="5908239569510734136">"Fjern gæsten"</string> + <!-- no translation found for guest_exit_guest (4754204715192830850) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"Gæst"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Tag et billede"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Vælg et billede"</string> diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml index e274d639b33c..361b932cea48 100644 --- a/packages/SettingsLib/res/values-de/strings.xml +++ b/packages/SettingsLib/res/values-de/strings.xml @@ -553,7 +553,8 @@ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Neuer Nutzer wird erstellt…"</string> <string name="user_nickname" msgid="262624187455825083">"Alias"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Gast hinzufügen"</string> - <string name="guest_exit_guest" msgid="5908239569510734136">"Gast entfernen"</string> + <!-- no translation found for guest_exit_guest (4754204715192830850) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"Gast"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Foto machen"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Bild auswählen"</string> diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml index 66244eb3b0f6..a3b3d26c92c5 100644 --- a/packages/SettingsLib/res/values-el/strings.xml +++ b/packages/SettingsLib/res/values-el/strings.xml @@ -450,7 +450,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Απομένουν <xliff:g id="TIME">%1$s</xliff:g> για ολοκλήρωση της φόρτισης"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> για την ολοκλήρωση της φόρτισης"</string> - <string name="power_charging_limited" msgid="1956874810658999681">"<xliff:g id="LEVEL">%1$s</xliff:g> - Βελτιστοποίηση κατάστασης μπαταρίας"</string> + <string name="power_charging_limited" msgid="1956874810658999681">"<xliff:g id="LEVEL">%1$s</xliff:g> - Βελτιστοποίηση για τη διατήρηση της καλής κατάστασης της μπαταρίας"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Άγνωστο"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Φόρτιση"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Ταχεία φόρτιση"</string> @@ -553,7 +553,8 @@ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Δημιουργία νέου χρήστη…"</string> <string name="user_nickname" msgid="262624187455825083">"Ψευδώνυμο"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Προσθήκη επισκέπτη"</string> - <string name="guest_exit_guest" msgid="5908239569510734136">"Κατάργηση επισκέπτη"</string> + <!-- no translation found for guest_exit_guest (4754204715192830850) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"Επισκέπτης"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Λήψη φωτογραφίας"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Επιλογή εικόνας"</string> diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml index f5a0e2c1fb9b..fe7515f68954 100644 --- a/packages/SettingsLib/res/values-en-rAU/strings.xml +++ b/packages/SettingsLib/res/values-en-rAU/strings.xml @@ -553,7 +553,7 @@ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Creating new user…"</string> <string name="user_nickname" msgid="262624187455825083">"Nickname"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Add guest"</string> - <string name="guest_exit_guest" msgid="5908239569510734136">"Remove guest"</string> + <string name="guest_exit_guest" msgid="4754204715192830850">"End Guest session"</string> <string name="guest_nickname" msgid="6332276931583337261">"Guest"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Take a photo"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Choose an image"</string> diff --git a/packages/SettingsLib/res/values-en-rCA/strings.xml b/packages/SettingsLib/res/values-en-rCA/strings.xml index 6f11e804e893..75a66781131b 100644 --- a/packages/SettingsLib/res/values-en-rCA/strings.xml +++ b/packages/SettingsLib/res/values-en-rCA/strings.xml @@ -553,7 +553,7 @@ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Creating new user…"</string> <string name="user_nickname" msgid="262624187455825083">"Nickname"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Add guest"</string> - <string name="guest_exit_guest" msgid="5908239569510734136">"Remove guest"</string> + <string name="guest_exit_guest" msgid="4754204715192830850">"End Guest session"</string> <string name="guest_nickname" msgid="6332276931583337261">"Guest"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Take a photo"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Choose an image"</string> diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml index f5a0e2c1fb9b..fe7515f68954 100644 --- a/packages/SettingsLib/res/values-en-rGB/strings.xml +++ b/packages/SettingsLib/res/values-en-rGB/strings.xml @@ -553,7 +553,7 @@ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Creating new user…"</string> <string name="user_nickname" msgid="262624187455825083">"Nickname"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Add guest"</string> - <string name="guest_exit_guest" msgid="5908239569510734136">"Remove guest"</string> + <string name="guest_exit_guest" msgid="4754204715192830850">"End Guest session"</string> <string name="guest_nickname" msgid="6332276931583337261">"Guest"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Take a photo"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Choose an image"</string> diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml index f5a0e2c1fb9b..fe7515f68954 100644 --- a/packages/SettingsLib/res/values-en-rIN/strings.xml +++ b/packages/SettingsLib/res/values-en-rIN/strings.xml @@ -553,7 +553,7 @@ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Creating new user…"</string> <string name="user_nickname" msgid="262624187455825083">"Nickname"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Add guest"</string> - <string name="guest_exit_guest" msgid="5908239569510734136">"Remove guest"</string> + <string name="guest_exit_guest" msgid="4754204715192830850">"End Guest session"</string> <string name="guest_nickname" msgid="6332276931583337261">"Guest"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Take a photo"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Choose an image"</string> diff --git a/packages/SettingsLib/res/values-en-rXC/strings.xml b/packages/SettingsLib/res/values-en-rXC/strings.xml index bfca10955c4b..aed0800ba197 100644 --- a/packages/SettingsLib/res/values-en-rXC/strings.xml +++ b/packages/SettingsLib/res/values-en-rXC/strings.xml @@ -553,7 +553,7 @@ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Creating new user…"</string> <string name="user_nickname" msgid="262624187455825083">"Nickname"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Add guest"</string> - <string name="guest_exit_guest" msgid="5908239569510734136">"Remove guest"</string> + <string name="guest_exit_guest" msgid="4754204715192830850">"End guest session"</string> <string name="guest_nickname" msgid="6332276931583337261">"Guest"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Take a photo"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Choose an image"</string> diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml index 44ae8c2e6545..f9260a8745af 100644 --- a/packages/SettingsLib/res/values-es-rUS/strings.xml +++ b/packages/SettingsLib/res/values-es-rUS/strings.xml @@ -553,7 +553,7 @@ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Creando usuario nuevo…"</string> <string name="user_nickname" msgid="262624187455825083">"Sobrenombre"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Agregar invitado"</string> - <string name="guest_exit_guest" msgid="5908239569510734136">"Quitar invitado"</string> + <string name="guest_exit_guest" msgid="4754204715192830850">"Finalizar sesión de invitado"</string> <string name="guest_nickname" msgid="6332276931583337261">"Invitado"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Tomar una foto"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Elegir una imagen"</string> diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml index 236394c5e641..bc176234e09b 100644 --- a/packages/SettingsLib/res/values-es/strings.xml +++ b/packages/SettingsLib/res/values-es/strings.xml @@ -553,7 +553,7 @@ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Creando usuario…"</string> <string name="user_nickname" msgid="262624187455825083">"Apodo"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Añadir invitado"</string> - <string name="guest_exit_guest" msgid="5908239569510734136">"Quitar invitado"</string> + <string name="guest_exit_guest" msgid="4754204715192830850">"Finalizar sesión de invitado"</string> <string name="guest_nickname" msgid="6332276931583337261">"Invitado"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Hacer foto"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Seleccionar una imagen"</string> diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml index c8fd94f72849..c73f1365c8c3 100644 --- a/packages/SettingsLib/res/values-et/strings.xml +++ b/packages/SettingsLib/res/values-et/strings.xml @@ -553,7 +553,8 @@ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Uue kasutaja loomine …"</string> <string name="user_nickname" msgid="262624187455825083">"Hüüdnimi"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Lisa külaline"</string> - <string name="guest_exit_guest" msgid="5908239569510734136">"Eemalda külaline"</string> + <!-- no translation found for guest_exit_guest (4754204715192830850) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"Külaline"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Pildistage"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Valige pilt"</string> diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml index 226ffba24621..2f51ac03ed77 100644 --- a/packages/SettingsLib/res/values-eu/strings.xml +++ b/packages/SettingsLib/res/values-eu/strings.xml @@ -450,7 +450,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> guztiz kargatu arte"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> guztiz kargatu arte"</string> - <string name="power_charging_limited" msgid="1956874810658999681">"<xliff:g id="LEVEL">%1$s</xliff:g> - Optimizatzen bateria egoera onean mantentzeko"</string> + <string name="power_charging_limited" msgid="1956874810658999681">"<xliff:g id="LEVEL">%1$s</xliff:g> - Optimizatzen, bateria egoera onean mantentzeko"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Ezezaguna"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Kargatzen"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Bizkor kargatzen"</string> @@ -553,7 +553,7 @@ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Beste erabiltzaile bat sortzen…"</string> <string name="user_nickname" msgid="262624187455825083">"Goitizena"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Gehitu gonbidatua"</string> - <string name="guest_exit_guest" msgid="5908239569510734136">"Kendu gonbidatua"</string> + <string name="guest_exit_guest" msgid="4754204715192830850">"Amaitu gonbidatuentzako saioa"</string> <string name="guest_nickname" msgid="6332276931583337261">"Gonbidatua"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Atera argazki bat"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Aukeratu irudi bat"</string> diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml index 917a637f1e01..038b81503c89 100644 --- a/packages/SettingsLib/res/values-fa/strings.xml +++ b/packages/SettingsLib/res/values-fa/strings.xml @@ -553,7 +553,8 @@ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"درحال ایجاد کاربر جدید…"</string> <string name="user_nickname" msgid="262624187455825083">"نام مستعار"</string> <string name="guest_new_guest" msgid="3482026122932643557">"افزودن مهمان"</string> - <string name="guest_exit_guest" msgid="5908239569510734136">"حذف مهمان"</string> + <!-- no translation found for guest_exit_guest (4754204715192830850) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"مهمان"</string> <string name="user_image_take_photo" msgid="467512954561638530">"عکس گرفتن"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"انتخاب تصویر"</string> diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml index b1038025bf1e..33b6142194dd 100644 --- a/packages/SettingsLib/res/values-fi/strings.xml +++ b/packages/SettingsLib/res/values-fi/strings.xml @@ -553,7 +553,8 @@ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Luodaan uutta käyttäjää…"</string> <string name="user_nickname" msgid="262624187455825083">"Lempinimi"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Lisää vieras"</string> - <string name="guest_exit_guest" msgid="5908239569510734136">"Poista vieras"</string> + <!-- no translation found for guest_exit_guest (4754204715192830850) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"Vieras"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Ota kuva"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Valitse kuva"</string> diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml index 91d14d40e269..01492c2f9719 100644 --- a/packages/SettingsLib/res/values-fr-rCA/strings.xml +++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml @@ -450,7 +450,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> jusqu\'à la charge complète"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> : <xliff:g id="TIME">%2$s</xliff:g> jusqu\'à la charge complète"</string> - <string name="power_charging_limited" msgid="1956874810658999681">"<xliff:g id="LEVEL">%1$s</xliff:g> – Optimisation pour la santé de la pile"</string> + <string name="power_charging_limited" msgid="1956874810658999681">"<xliff:g id="LEVEL">%1$s</xliff:g> – Optimisation pour préserver la pile"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Inconnu"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Charge en cours…"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Recharge rapide"</string> @@ -553,7 +553,7 @@ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Créer un utilisateur…"</string> <string name="user_nickname" msgid="262624187455825083">"Pseudo"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Ajouter un invité"</string> - <string name="guest_exit_guest" msgid="5908239569510734136">"Supprimer l\'invité"</string> + <string name="guest_exit_guest" msgid="4754204715192830850">"Mettre fin à la session d\'invité"</string> <string name="guest_nickname" msgid="6332276931583337261">"Invité"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Prendre une photo"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Sélectionner une image"</string> diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml index 2518fe10d06e..04ed5fee3bde 100644 --- a/packages/SettingsLib/res/values-fr/strings.xml +++ b/packages/SettingsLib/res/values-fr/strings.xml @@ -553,7 +553,8 @@ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Création d\'un nouvel utilisateur…"</string> <string name="user_nickname" msgid="262624187455825083">"Pseudo"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Ajouter un invité"</string> - <string name="guest_exit_guest" msgid="5908239569510734136">"Supprimer l\'invité"</string> + <!-- no translation found for guest_exit_guest (4754204715192830850) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"Invité"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Prendre une photo"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Choisir une image"</string> diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml index 486afe346ac2..b59395cf89e9 100644 --- a/packages/SettingsLib/res/values-gl/strings.xml +++ b/packages/SettingsLib/res/values-gl/strings.xml @@ -450,7 +450,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> para completar a carga"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="TIME">%2$s</xliff:g> para completar a carga"</string> - <string name="power_charging_limited" msgid="1956874810658999681">"<xliff:g id="LEVEL">%1$s</xliff:g>: optimizando para manter a batería en bo estado"</string> + <string name="power_charging_limited" msgid="1956874810658999681">"<xliff:g id="LEVEL">%1$s</xliff:g>: optimizando a preservación da batería"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Descoñecido"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Cargando"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Cargando rapidamente"</string> @@ -553,7 +553,8 @@ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Creando usuario novo…"</string> <string name="user_nickname" msgid="262624187455825083">"Alcume"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Engadir convidado"</string> - <string name="guest_exit_guest" msgid="5908239569510734136">"Quitar convidado"</string> + <!-- no translation found for guest_exit_guest (4754204715192830850) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"Convidado"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Tirar foto"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Escoller imaxe"</string> diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml index 2bebe36818c5..601ffde9d50e 100644 --- a/packages/SettingsLib/res/values-gu/strings.xml +++ b/packages/SettingsLib/res/values-gu/strings.xml @@ -553,7 +553,8 @@ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"નવા વપરાશકર્તા બનાવી રહ્યાં છીએ…"</string> <string name="user_nickname" msgid="262624187455825083">"ઉપનામ"</string> <string name="guest_new_guest" msgid="3482026122932643557">"અતિથિ ઉમેરો"</string> - <string name="guest_exit_guest" msgid="5908239569510734136">"અતિથિને કાઢી નાખો"</string> + <!-- no translation found for guest_exit_guest (4754204715192830850) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"અતિથિ"</string> <string name="user_image_take_photo" msgid="467512954561638530">"ફોટો લો"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"છબી પસંદ કરો"</string> diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml index 824ab25a5bcc..974d00cd10f8 100644 --- a/packages/SettingsLib/res/values-hi/strings.xml +++ b/packages/SettingsLib/res/values-hi/strings.xml @@ -553,7 +553,7 @@ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"नया उपयोगकर्ता बनाया जा रहा है…"</string> <string name="user_nickname" msgid="262624187455825083">"प्रचलित नाम"</string> <string name="guest_new_guest" msgid="3482026122932643557">"मेहमान जोड़ें"</string> - <string name="guest_exit_guest" msgid="5908239569510734136">"मेहमान हटाएं"</string> + <string name="guest_exit_guest" msgid="4754204715192830850">"मेहमान के तौर पर ब्राउज़ करने का सेशन खत्म करें"</string> <string name="guest_nickname" msgid="6332276931583337261">"मेहमान"</string> <string name="user_image_take_photo" msgid="467512954561638530">"फ़ोटो खींचें"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"कोई इमेज चुनें"</string> diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml index cd21a5fe1da5..f7924285f9ee 100644 --- a/packages/SettingsLib/res/values-hr/strings.xml +++ b/packages/SettingsLib/res/values-hr/strings.xml @@ -554,7 +554,8 @@ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Izrada novog korisnika…"</string> <string name="user_nickname" msgid="262624187455825083">"Nadimak"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Dodavanje gosta"</string> - <string name="guest_exit_guest" msgid="5908239569510734136">"Uklanjanje gosta"</string> + <!-- no translation found for guest_exit_guest (4754204715192830850) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"Gost"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Fotografiraj"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Odaberi sliku"</string> diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml index c3b859cb0724..2bf5325b24ff 100644 --- a/packages/SettingsLib/res/values-hu/strings.xml +++ b/packages/SettingsLib/res/values-hu/strings.xml @@ -553,7 +553,8 @@ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Új felhasználó létrehozása…"</string> <string name="user_nickname" msgid="262624187455825083">"Becenév"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Vendég hozzáadása"</string> - <string name="guest_exit_guest" msgid="5908239569510734136">"Vendég munkamenet eltávolítása"</string> + <!-- no translation found for guest_exit_guest (4754204715192830850) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"Vendég"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Fotó készítése"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Kép kiválasztása"</string> diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml index 041ad0e191d5..fea1e128d216 100644 --- a/packages/SettingsLib/res/values-hy/strings.xml +++ b/packages/SettingsLib/res/values-hy/strings.xml @@ -450,7 +450,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> մինչև լիցքավորումը"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> մինչև լիցքավորումը"</string> - <string name="power_charging_limited" msgid="1956874810658999681">"<xliff:g id="LEVEL">%1$s</xliff:g> – Օպտիմալացվում է"</string> + <string name="power_charging_limited" msgid="1956874810658999681">"<xliff:g id="LEVEL">%1$s</xliff:g> – Օպտիմալացվում է մարտկոցի պահպանման համար"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Անհայտ"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Լիցքավորում"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Արագ լիցքավորում"</string> @@ -553,7 +553,7 @@ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Ստեղծվում է օգտատիրոջ նոր պրոֆիլ…"</string> <string name="user_nickname" msgid="262624187455825083">"Կեղծանուն"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Ավելացնել հյուր"</string> - <string name="guest_exit_guest" msgid="5908239569510734136">"Հեռացնել հյուրին"</string> + <string name="guest_exit_guest" msgid="4754204715192830850">"Ավարտել հյուրի աշխատաշրջանը"</string> <string name="guest_nickname" msgid="6332276931583337261">"Հյուր"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Լուսանկարել"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Ընտրել պատկեր"</string> diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml index 332d5b35e857..7298cea4e00d 100644 --- a/packages/SettingsLib/res/values-in/strings.xml +++ b/packages/SettingsLib/res/values-in/strings.xml @@ -553,7 +553,8 @@ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Membuat pengguna baru …"</string> <string name="user_nickname" msgid="262624187455825083">"Nama panggilan"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Tambahkan tamu"</string> - <string name="guest_exit_guest" msgid="5908239569510734136">"Hapus tamu"</string> + <!-- no translation found for guest_exit_guest (4754204715192830850) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"Tamu"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Ambil foto"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Pilih gambar"</string> diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml index f40136452674..bf6c03167476 100644 --- a/packages/SettingsLib/res/values-is/strings.xml +++ b/packages/SettingsLib/res/values-is/strings.xml @@ -553,7 +553,8 @@ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Stofnar nýjan notanda…"</string> <string name="user_nickname" msgid="262624187455825083">"Gælunafn"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Bæta gesti við"</string> - <string name="guest_exit_guest" msgid="5908239569510734136">"Fjarlægja gest"</string> + <!-- no translation found for guest_exit_guest (4754204715192830850) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"Gestur"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Taka mynd"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Velja mynd"</string> diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml index ef5e2ff912ad..2ed92e4be0b8 100644 --- a/packages/SettingsLib/res/values-it/strings.xml +++ b/packages/SettingsLib/res/values-it/strings.xml @@ -553,7 +553,8 @@ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Creazione nuovo utente…"</string> <string name="user_nickname" msgid="262624187455825083">"Nickname"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Aggiungi ospite"</string> - <string name="guest_exit_guest" msgid="5908239569510734136">"Rimuovi ospite"</string> + <!-- no translation found for guest_exit_guest (4754204715192830850) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"Ospite"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Scatta una foto"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Scegli un\'immagine"</string> diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml index db2ffe5d2f17..bf4c35e000ef 100644 --- a/packages/SettingsLib/res/values-iw/strings.xml +++ b/packages/SettingsLib/res/values-iw/strings.xml @@ -555,7 +555,8 @@ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"בתהליך יצירה של משתמש חדש…"</string> <string name="user_nickname" msgid="262624187455825083">"כינוי"</string> <string name="guest_new_guest" msgid="3482026122932643557">"הוספת אורח"</string> - <string name="guest_exit_guest" msgid="5908239569510734136">"הסרת אורח"</string> + <!-- no translation found for guest_exit_guest (4754204715192830850) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"אורח"</string> <string name="user_image_take_photo" msgid="467512954561638530">"צילום תמונה"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"לבחירת תמונה"</string> diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml index 9e9db3d2be38..f467c6049722 100644 --- a/packages/SettingsLib/res/values-ja/strings.xml +++ b/packages/SettingsLib/res/values-ja/strings.xml @@ -553,7 +553,7 @@ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"新しいユーザーを作成しています…"</string> <string name="user_nickname" msgid="262624187455825083">"ニックネーム"</string> <string name="guest_new_guest" msgid="3482026122932643557">"ゲストを追加"</string> - <string name="guest_exit_guest" msgid="5908239569510734136">"ゲストを削除"</string> + <string name="guest_exit_guest" msgid="4754204715192830850">"ゲスト セッションを終了する"</string> <string name="guest_nickname" msgid="6332276931583337261">"ゲスト"</string> <string name="user_image_take_photo" msgid="467512954561638530">"写真を撮る"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"画像を選択"</string> diff --git a/packages/SettingsLib/res/values-ka/strings.xml b/packages/SettingsLib/res/values-ka/strings.xml index ec3900883140..493839f0a07f 100644 --- a/packages/SettingsLib/res/values-ka/strings.xml +++ b/packages/SettingsLib/res/values-ka/strings.xml @@ -553,7 +553,8 @@ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"მიმდინარეობს ახალი მომხმარებლის შექმნა…"</string> <string name="user_nickname" msgid="262624187455825083">"მეტსახელი"</string> <string name="guest_new_guest" msgid="3482026122932643557">"სტუმრის დამატება"</string> - <string name="guest_exit_guest" msgid="5908239569510734136">"სტუმრის ამოშლა"</string> + <!-- no translation found for guest_exit_guest (4754204715192830850) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"სტუმარი"</string> <string name="user_image_take_photo" msgid="467512954561638530">"ფოტოს გადაღება"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"აირჩიეთ სურათი"</string> diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml index 881a13cf1cba..83d859b83255 100644 --- a/packages/SettingsLib/res/values-kk/strings.xml +++ b/packages/SettingsLib/res/values-kk/strings.xml @@ -450,7 +450,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Зарядталғанға дейін <xliff:g id="TIME">%1$s</xliff:g> қалды"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – зарядталғанға дейін <xliff:g id="TIME">%2$s</xliff:g>"</string> - <string name="power_charging_limited" msgid="1956874810658999681">"<xliff:g id="LEVEL">%1$s</xliff:g> - Батареяның жұмыс істеу қабілеті оңтайландырылуда"</string> + <string name="power_charging_limited" msgid="1956874810658999681">"<xliff:g id="LEVEL">%1$s</xliff:g> - Батарея жұмысын оңтайландыру"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Белгісіз"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Зарядталуда"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Жылдам зарядталуда"</string> @@ -553,7 +553,8 @@ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Жаңа пайдаланушы профилі жасалуда…"</string> <string name="user_nickname" msgid="262624187455825083">"Лақап ат"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Қонақты енгізу"</string> - <string name="guest_exit_guest" msgid="5908239569510734136">"Қонақты өшіру"</string> + <!-- no translation found for guest_exit_guest (4754204715192830850) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"Қонақ"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Фотосуретке түсіру"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Сурет таңдау"</string> diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml index 14f77889d83d..db571d6f2dfe 100644 --- a/packages/SettingsLib/res/values-km/strings.xml +++ b/packages/SettingsLib/res/values-km/strings.xml @@ -553,7 +553,8 @@ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"កំពុងបង្កើតអ្នកប្រើប្រាស់ថ្មី…"</string> <string name="user_nickname" msgid="262624187455825083">"ឈ្មោះហៅក្រៅ"</string> <string name="guest_new_guest" msgid="3482026122932643557">"បញ្ចូលភ្ញៀវ"</string> - <string name="guest_exit_guest" msgid="5908239569510734136">"លុបភ្ញៀវ"</string> + <!-- no translation found for guest_exit_guest (4754204715192830850) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"ភ្ញៀវ"</string> <string name="user_image_take_photo" msgid="467512954561638530">"ថតរូប"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"ជ្រើសរើសរូបភាព"</string> diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml index a279d22538a1..2e8b9f1438d9 100644 --- a/packages/SettingsLib/res/values-kn/strings.xml +++ b/packages/SettingsLib/res/values-kn/strings.xml @@ -450,7 +450,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"ಚಾರ್ಜ್ ಆಗಲು <xliff:g id="TIME">%1$s</xliff:g> ಸಮಯ ಬಾಕಿ ಇದೆ"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - ಚಾರ್ಜ್ ಆಗಲು <xliff:g id="TIME">%2$s</xliff:g> ಸಮಯ ಬೇಕು"</string> - <string name="power_charging_limited" msgid="1956874810658999681">"<xliff:g id="LEVEL">%1$s</xliff:g> - ಬ್ಯಾಟರಿಯ ಆರೋಗ್ಯಕ್ಕಾಗಿ ಆಪ್ಟಿಮೈಸ್ ಮಾಡಲಾಗುತ್ತಿದೆ"</string> + <string name="power_charging_limited" msgid="1956874810658999681">"<xliff:g id="LEVEL">%1$s</xliff:g> - ಬ್ಯಾಟರಿಯ ಸುಸ್ಥಿತಿಗಾಗಿ ಆಪ್ಟಿಮೈಸ್ ಮಾಡಲಾಗುತ್ತಿದೆ"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"ಅಪರಿಚಿತ"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"ಚಾರ್ಜ್ ಆಗುತ್ತಿದೆ"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"ವೇಗದ ಚಾರ್ಜಿಂಗ್"</string> @@ -553,7 +553,8 @@ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"ಹೊಸ ಬಳಕೆದಾರರನ್ನು ರಚಿಸಲಾಗುತ್ತಿದೆ…"</string> <string name="user_nickname" msgid="262624187455825083">"ಅಡ್ಡ ಹೆಸರು"</string> <string name="guest_new_guest" msgid="3482026122932643557">"ಅತಿಥಿಯನ್ನು ಸೇರಿಸಿ"</string> - <string name="guest_exit_guest" msgid="5908239569510734136">"ಅತಿಥಿಯನ್ನು ತೆಗೆದುಹಾಕಿ"</string> + <!-- no translation found for guest_exit_guest (4754204715192830850) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"ಅತಿಥಿ"</string> <string name="user_image_take_photo" msgid="467512954561638530">"ಫೋಟೋ ತೆಗೆದುಕೊಳ್ಳಿ"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"ಚಿತ್ರವನ್ನು ಆರಿಸಿ"</string> diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml index de694884466b..0294e9d4d8c3 100644 --- a/packages/SettingsLib/res/values-ko/strings.xml +++ b/packages/SettingsLib/res/values-ko/strings.xml @@ -553,7 +553,8 @@ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"새로운 사용자를 만드는 중…"</string> <string name="user_nickname" msgid="262624187455825083">"닉네임"</string> <string name="guest_new_guest" msgid="3482026122932643557">"게스트 추가"</string> - <string name="guest_exit_guest" msgid="5908239569510734136">"게스트 삭제"</string> + <!-- no translation found for guest_exit_guest (4754204715192830850) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"게스트"</string> <string name="user_image_take_photo" msgid="467512954561638530">"사진 찍기"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"이미지 선택"</string> diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml index e70460ffa06d..0c73cf67c517 100644 --- a/packages/SettingsLib/res/values-ky/strings.xml +++ b/packages/SettingsLib/res/values-ky/strings.xml @@ -553,7 +553,8 @@ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Жаңы колдонуучу түзүлүүдө…"</string> <string name="user_nickname" msgid="262624187455825083">"Ылакап аты"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Конок кошуу"</string> - <string name="guest_exit_guest" msgid="5908239569510734136">"Конокту өчүрүү"</string> + <!-- no translation found for guest_exit_guest (4754204715192830850) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"Конок"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Сүрөткө тартуу"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Сүрөт тандаңыз"</string> diff --git a/packages/SettingsLib/res/values-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml index c63cec8fc7a2..fbe814ab619b 100644 --- a/packages/SettingsLib/res/values-lo/strings.xml +++ b/packages/SettingsLib/res/values-lo/strings.xml @@ -553,7 +553,8 @@ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"ກຳລັງສ້າງຜູ້ໃຊ້ໃໝ່…"</string> <string name="user_nickname" msgid="262624187455825083">"ຊື່ຫຼິ້ນ"</string> <string name="guest_new_guest" msgid="3482026122932643557">"ເພີ່ມແຂກ"</string> - <string name="guest_exit_guest" msgid="5908239569510734136">"ລຶບແຂກອອກ"</string> + <!-- no translation found for guest_exit_guest (4754204715192830850) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"ແຂກ"</string> <string name="user_image_take_photo" msgid="467512954561638530">"ຖ່າຍຮູບ"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"ເລືອກຮູບ"</string> diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml index d040edf25b3b..b3d8e171d22e 100644 --- a/packages/SettingsLib/res/values-lt/strings.xml +++ b/packages/SettingsLib/res/values-lt/strings.xml @@ -555,7 +555,8 @@ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Kuriamas naujas naudotojas…"</string> <string name="user_nickname" msgid="262624187455825083">"Slapyvardis"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Pridėti svečią"</string> - <string name="guest_exit_guest" msgid="5908239569510734136">"Pašalinti svečią"</string> + <!-- no translation found for guest_exit_guest (4754204715192830850) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"Svečias"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Fotografuoti"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Pasirinkti vaizdą"</string> diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml index 74c0dd5ccad6..3c69e642156a 100644 --- a/packages/SettingsLib/res/values-lv/strings.xml +++ b/packages/SettingsLib/res/values-lv/strings.xml @@ -554,7 +554,8 @@ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Notiek jauna lietotāja izveide…"</string> <string name="user_nickname" msgid="262624187455825083">"Segvārds"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Pievienot viesi"</string> - <string name="guest_exit_guest" msgid="5908239569510734136">"Noņemt viesi"</string> + <!-- no translation found for guest_exit_guest (4754204715192830850) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"Viesis"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Uzņemt fotoattēlu"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Izvēlēties attēlu"</string> diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml index 739bceb81e1a..b909359faf3b 100644 --- a/packages/SettingsLib/res/values-mk/strings.xml +++ b/packages/SettingsLib/res/values-mk/strings.xml @@ -553,7 +553,8 @@ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Се создава нов корисник…"</string> <string name="user_nickname" msgid="262624187455825083">"Прекар"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Додај гостин"</string> - <string name="guest_exit_guest" msgid="5908239569510734136">"Отстрани гостин"</string> + <!-- no translation found for guest_exit_guest (4754204715192830850) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"Гостин"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Фотографирајте"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Одберете слика"</string> diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml index b7de4293bc1f..8f83ee103de3 100644 --- a/packages/SettingsLib/res/values-ml/strings.xml +++ b/packages/SettingsLib/res/values-ml/strings.xml @@ -553,7 +553,8 @@ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"പുതിയ ഉപയോക്താവിനെ സൃഷ്ടിക്കുന്നു…"</string> <string name="user_nickname" msgid="262624187455825083">"വിളിപ്പേര്"</string> <string name="guest_new_guest" msgid="3482026122932643557">"അതിഥിയെ ചേർക്കുക"</string> - <string name="guest_exit_guest" msgid="5908239569510734136">"അതിഥിയെ നീക്കം ചെയ്യുക"</string> + <!-- no translation found for guest_exit_guest (4754204715192830850) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"അതിഥി"</string> <string name="user_image_take_photo" msgid="467512954561638530">"ഒരു ഫോട്ടോ എടുക്കുക"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"ഒരു ചിത്രം തിരഞ്ഞെടുക്കുക"</string> diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml index c7a2edd7c3fb..8168b7922e63 100644 --- a/packages/SettingsLib/res/values-mn/strings.xml +++ b/packages/SettingsLib/res/values-mn/strings.xml @@ -450,7 +450,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Цэнэглэх хүртэл үлдсэн <xliff:g id="TIME">%1$s</xliff:g>"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - цэнэглэх хүртэл <xliff:g id="TIME">%2$s</xliff:g>"</string> - <string name="power_charging_limited" msgid="1956874810658999681">"<xliff:g id="LEVEL">%1$s</xliff:g> - Батарейн чанарыг оновчилж байна"</string> + <string name="power_charging_limited" msgid="1956874810658999681">"<xliff:g id="LEVEL">%1$s</xliff:g> - Батарейн барилтыг оновчилж байна"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Тодорхойгүй"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Цэнэглэж байна"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Хурдан цэнэглэж байна"</string> @@ -553,7 +553,7 @@ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Шинэ хэрэглэгч үүсгэж байна…"</string> <string name="user_nickname" msgid="262624187455825083">"Хоч"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Зочин нэмэх"</string> - <string name="guest_exit_guest" msgid="5908239569510734136">"Зочин хасах"</string> + <string name="guest_exit_guest" msgid="4754204715192830850">"Зочны сургалтыг дуусгах"</string> <string name="guest_nickname" msgid="6332276931583337261">"Зочин"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Зураг авах"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Зураг сонгох"</string> diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml index d29cb590da81..f47d8e005508 100644 --- a/packages/SettingsLib/res/values-mr/strings.xml +++ b/packages/SettingsLib/res/values-mr/strings.xml @@ -553,7 +553,8 @@ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"नवीन वापरकर्ता तयार करत आहे…"</string> <string name="user_nickname" msgid="262624187455825083">"टोपणनाव"</string> <string name="guest_new_guest" msgid="3482026122932643557">"अतिथी जोडा"</string> - <string name="guest_exit_guest" msgid="5908239569510734136">"अतिथी काढून टाका"</string> + <!-- no translation found for guest_exit_guest (4754204715192830850) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"अतिथी"</string> <string name="user_image_take_photo" msgid="467512954561638530">"फोटो काढा"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"इमेज निवडा"</string> diff --git a/packages/SettingsLib/res/values-ms/strings.xml b/packages/SettingsLib/res/values-ms/strings.xml index c593e4b8f9d9..fb2826f9609c 100644 --- a/packages/SettingsLib/res/values-ms/strings.xml +++ b/packages/SettingsLib/res/values-ms/strings.xml @@ -553,7 +553,7 @@ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Mencipta pengguna baharu…"</string> <string name="user_nickname" msgid="262624187455825083">"Nama panggilan"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Tambah tetamu"</string> - <string name="guest_exit_guest" msgid="5908239569510734136">"Alih keluar tetamu"</string> + <string name="guest_exit_guest" msgid="4754204715192830850">"Tamatkan sesi tetamu"</string> <string name="guest_nickname" msgid="6332276931583337261">"Tetamu"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Ambil foto"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Pilih imej"</string> diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml index aa9d3d63fb6c..d98d4429b314 100644 --- a/packages/SettingsLib/res/values-my/strings.xml +++ b/packages/SettingsLib/res/values-my/strings.xml @@ -553,7 +553,8 @@ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"အသုံးပြုသူအသစ် ပြုလုပ်နေသည်…"</string> <string name="user_nickname" msgid="262624187455825083">"နာမည်ပြောင်"</string> <string name="guest_new_guest" msgid="3482026122932643557">"ဧည့်သည့် ထည့်ရန်"</string> - <string name="guest_exit_guest" msgid="5908239569510734136">"ဧည့်သည်ကို ဖယ်ထုတ်ရန်"</string> + <!-- no translation found for guest_exit_guest (4754204715192830850) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"ဧည့်သည်"</string> <string name="user_image_take_photo" msgid="467512954561638530">"ဓာတ်ပုံရိုက်ရန်"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"ပုံရွေးရန်"</string> diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml index 9494c398a93b..944c48ebdde2 100644 --- a/packages/SettingsLib/res/values-nb/strings.xml +++ b/packages/SettingsLib/res/values-nb/strings.xml @@ -450,7 +450,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> til batteriet er fulladet"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> til batteriet er fulladet"</string> - <string name="power_charging_limited" msgid="1956874810658999681">"<xliff:g id="LEVEL">%1$s</xliff:g> – Optimaliserer batteritilstanden"</string> + <string name="power_charging_limited" msgid="1956874810658999681">"<xliff:g id="LEVEL">%1$s</xliff:g> – optimaliserer batteritilstanden"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Ukjent"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Lader"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Lader raskt"</string> @@ -553,7 +553,8 @@ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Oppretter en ny bruker …"</string> <string name="user_nickname" msgid="262624187455825083">"Kallenavn"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Legg til en gjest"</string> - <string name="guest_exit_guest" msgid="5908239569510734136">"Fjern gjesten"</string> + <!-- no translation found for guest_exit_guest (4754204715192830850) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"Gjest"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Ta et bilde"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Velg et bilde"</string> diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml index e31fb4eff9b1..9a0d2eda8626 100644 --- a/packages/SettingsLib/res/values-ne/strings.xml +++ b/packages/SettingsLib/res/values-ne/strings.xml @@ -553,7 +553,8 @@ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"नयाँ प्रयोगकर्ता बनाउँदै…"</string> <string name="user_nickname" msgid="262624187455825083">"उपनाम"</string> <string name="guest_new_guest" msgid="3482026122932643557">"अतिथि थप्नुहोस्"</string> - <string name="guest_exit_guest" msgid="5908239569510734136">"अतिथि हटाउनुहोस्"</string> + <!-- no translation found for guest_exit_guest (4754204715192830850) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"अतिथि"</string> <string name="user_image_take_photo" msgid="467512954561638530">"फोटो खिच्नुहोस्"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"कुनै फोटो छनौट गर्नुहोस्"</string> diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml index 0b75fdd16a29..a8e4fb5b2d26 100644 --- a/packages/SettingsLib/res/values-nl/strings.xml +++ b/packages/SettingsLib/res/values-nl/strings.xml @@ -553,7 +553,7 @@ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Nieuwe gebruiker maken…"</string> <string name="user_nickname" msgid="262624187455825083">"Bijnaam"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Gast toevoegen"</string> - <string name="guest_exit_guest" msgid="5908239569510734136">"Gast verwijderen"</string> + <string name="guest_exit_guest" msgid="4754204715192830850">"Gastsessie beëindigen"</string> <string name="guest_nickname" msgid="6332276931583337261">"Gast"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Foto maken"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Afbeelding kiezen"</string> diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml index cfde61f86677..306ef440a02c 100644 --- a/packages/SettingsLib/res/values-or/strings.xml +++ b/packages/SettingsLib/res/values-or/strings.xml @@ -553,7 +553,8 @@ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"ନୂଆ ଉପଯୋଗକର୍ତ୍ତା ତିଆରି କରାଯାଉଛି…"</string> <string name="user_nickname" msgid="262624187455825083">"ଡାକନାମ"</string> <string name="guest_new_guest" msgid="3482026122932643557">"ଅତିଥି ଯୋଗ କରନ୍ତୁ"</string> - <string name="guest_exit_guest" msgid="5908239569510734136">"ଅତିଥିଙ୍କୁ କାଢ଼ି ଦିଅନ୍ତୁ"</string> + <!-- no translation found for guest_exit_guest (4754204715192830850) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"ଅତିଥି"</string> <string name="user_image_take_photo" msgid="467512954561638530">"ଗୋଟିଏ ଫଟୋ ଉଠାନ୍ତୁ"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"ଏକ ଛବି ବାଛନ୍ତୁ"</string> diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml index 122186256a1e..e231a26671e6 100644 --- a/packages/SettingsLib/res/values-pa/strings.xml +++ b/packages/SettingsLib/res/values-pa/strings.xml @@ -450,7 +450,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"ਚਾਰਜ ਹੋਣ ਵਿੱਚ <xliff:g id="TIME">%1$s</xliff:g> ਬਾਕੀ"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> ਤੱਕ ਚਾਰਜ ਹੋ ਜਾਵੇਗੀ"</string> - <string name="power_charging_limited" msgid="1956874810658999681">"<xliff:g id="LEVEL">%1$s</xliff:g> - ਬੈਟਰੀ ਸਥਿਤੀ ਲਈ ਅਨੁਕੂਲ ਬਣਾਇਆ ਜਾ ਰਿਹਾ"</string> + <string name="power_charging_limited" msgid="1956874810658999681">"<xliff:g id="LEVEL">%1$s</xliff:g> - ਬੈਟਰੀ ਦੀ ਸਥਿਤੀ ਲਈ ਅਨੁਕੂਲ ਬਣਾਇਆ ਜਾ ਰਿਹਾ ਹੈ"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"ਅਗਿਆਤ"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"ਚਾਰਜ ਹੋ ਰਿਹਾ ਹੈ"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"ਤੇਜ਼ ਚਾਰਜ ਹੋ ਰਹੀ ਹੈ"</string> @@ -553,7 +553,8 @@ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"ਨਵਾਂ ਵਰਤੋਂਕਾਰ ਬਣਾਇਆ ਜਾ ਰਿਹਾ ਹੈ…"</string> <string name="user_nickname" msgid="262624187455825083">"ਉਪਨਾਮ"</string> <string name="guest_new_guest" msgid="3482026122932643557">"ਮਹਿਮਾਨ ਸ਼ਾਮਲ ਕਰੋ"</string> - <string name="guest_exit_guest" msgid="5908239569510734136">"ਮਹਿਮਾਨ ਹਟਾਓ"</string> + <!-- no translation found for guest_exit_guest (4754204715192830850) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"ਮਹਿਮਾਨ"</string> <string name="user_image_take_photo" msgid="467512954561638530">"ਇੱਕ ਫ਼ੋਟੋ ਖਿੱਚੋ"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"ਕੋਈ ਚਿੱਤਰ ਚੁਣੋ"</string> diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml index 102835c9bd97..a62e9cc440e1 100644 --- a/packages/SettingsLib/res/values-pl/strings.xml +++ b/packages/SettingsLib/res/values-pl/strings.xml @@ -450,7 +450,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Do naładowania <xliff:g id="TIME">%1$s</xliff:g>"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – do naładowania <xliff:g id="TIME">%2$s</xliff:g>"</string> - <string name="power_charging_limited" msgid="1956874810658999681">"<xliff:g id="LEVEL">%1$s</xliff:g> – Optymalizuję, by utrzymać baterię w dobrym stanie"</string> + <string name="power_charging_limited" msgid="1956874810658999681">"<xliff:g id="LEVEL">%1$s</xliff:g> – Optymalizuję, aby utrzymać baterię w dobrym stanie"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Nieznane"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Ładowanie"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Szybkie ładowanie"</string> @@ -555,7 +555,8 @@ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Tworzę nowego użytkownika…"</string> <string name="user_nickname" msgid="262624187455825083">"Pseudonim"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Dodaj gościa"</string> - <string name="guest_exit_guest" msgid="5908239569510734136">"Usuń gościa"</string> + <!-- no translation found for guest_exit_guest (4754204715192830850) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"Gość"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Zrób zdjęcie"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Wybierz obraz"</string> diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml index 99019a683d96..fe922c0926ec 100644 --- a/packages/SettingsLib/res/values-pt-rBR/strings.xml +++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml @@ -553,7 +553,8 @@ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Criando novo usuário…"</string> <string name="user_nickname" msgid="262624187455825083">"Apelido"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Adicionar convidado"</string> - <string name="guest_exit_guest" msgid="5908239569510734136">"Remover convidado"</string> + <!-- no translation found for guest_exit_guest (4754204715192830850) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"Convidado"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Tirar uma foto"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Escolher uma imagem"</string> diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml index 238e54bb5fd3..5d1f88049160 100644 --- a/packages/SettingsLib/res/values-pt-rPT/strings.xml +++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml @@ -553,7 +553,8 @@ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"A criar novo utilizador…"</string> <string name="user_nickname" msgid="262624187455825083">"Pseudónimo"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Adicionar convidado"</string> - <string name="guest_exit_guest" msgid="5908239569510734136">"Remover convidado"</string> + <!-- no translation found for guest_exit_guest (4754204715192830850) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"Convidado"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Tirar uma foto"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Escolher uma imagem"</string> diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml index 99019a683d96..fe922c0926ec 100644 --- a/packages/SettingsLib/res/values-pt/strings.xml +++ b/packages/SettingsLib/res/values-pt/strings.xml @@ -553,7 +553,8 @@ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Criando novo usuário…"</string> <string name="user_nickname" msgid="262624187455825083">"Apelido"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Adicionar convidado"</string> - <string name="guest_exit_guest" msgid="5908239569510734136">"Remover convidado"</string> + <!-- no translation found for guest_exit_guest (4754204715192830850) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"Convidado"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Tirar uma foto"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Escolher uma imagem"</string> diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml index df95dbc76ad4..765f83a59a3a 100644 --- a/packages/SettingsLib/res/values-ro/strings.xml +++ b/packages/SettingsLib/res/values-ro/strings.xml @@ -554,7 +554,8 @@ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Se creează un utilizator nou…"</string> <string name="user_nickname" msgid="262624187455825083">"Pseudonim"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Adăugați un invitat"</string> - <string name="guest_exit_guest" msgid="5908239569510734136">"Ștergeți invitatul"</string> + <!-- no translation found for guest_exit_guest (4754204715192830850) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"Invitat"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Faceți o fotografie"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Alegeți o imagine"</string> diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml index 68826d7552fc..98b320394c0a 100644 --- a/packages/SettingsLib/res/values-ru/strings.xml +++ b/packages/SettingsLib/res/values-ru/strings.xml @@ -555,7 +555,8 @@ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Создаем нового пользователя…"</string> <string name="user_nickname" msgid="262624187455825083">"Псевдоним"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Добавить аккаунт гостя"</string> - <string name="guest_exit_guest" msgid="5908239569510734136">"Удалить аккаунт гостя"</string> + <!-- no translation found for guest_exit_guest (4754204715192830850) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"Гость"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Сделать снимок"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Выбрать фото"</string> diff --git a/packages/SettingsLib/res/values-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml index 736445da0910..087f140362b7 100644 --- a/packages/SettingsLib/res/values-si/strings.xml +++ b/packages/SettingsLib/res/values-si/strings.xml @@ -450,7 +450,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"ආරෝපණය වන තෙක් <xliff:g id="TIME">%1$s</xliff:g> ඇත"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - ආරෝපණය වන තෙක් <xliff:g id="TIME">%2$s</xliff:g>"</string> - <string name="power_charging_limited" msgid="1956874810658999681">"<xliff:g id="LEVEL">%1$s</xliff:g> - බැටරි සෞඛ්යය සඳහා ප්රශස්ත කරමින්"</string> + <string name="power_charging_limited" msgid="1956874810658999681">"<xliff:g id="LEVEL">%1$s</xliff:g> - බැටරි ආයු කාලය වැඩි දියුණු කරමින්"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"නොදනී"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"ආරෝපණය වෙමින්"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"ශීඝ්ර ආරෝපණය"</string> @@ -553,7 +553,8 @@ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"නව පරිශීලක තනමින්…"</string> <string name="user_nickname" msgid="262624187455825083">"අපනාමය"</string> <string name="guest_new_guest" msgid="3482026122932643557">"අමුත්තා එක් කරන්න"</string> - <string name="guest_exit_guest" msgid="5908239569510734136">"අමුත්තා ඉවත් කරන්න"</string> + <!-- no translation found for guest_exit_guest (4754204715192830850) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"අමුත්තා"</string> <string name="user_image_take_photo" msgid="467512954561638530">"ඡායාරූපයක් ගන්න"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"රූපයක් තෝරන්න"</string> diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml index 265ec8fcff04..858f01758a86 100644 --- a/packages/SettingsLib/res/values-sk/strings.xml +++ b/packages/SettingsLib/res/values-sk/strings.xml @@ -555,7 +555,8 @@ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Vytvára sa nový používateľ…"</string> <string name="user_nickname" msgid="262624187455825083">"Prezývka"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Pridať hosťa"</string> - <string name="guest_exit_guest" msgid="5908239569510734136">"Odobrať hosťa"</string> + <!-- no translation found for guest_exit_guest (4754204715192830850) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"Hosť"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Odfotiť"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Vybrať obrázok"</string> diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml index 2e6fc0ecdfe1..2386b13873e8 100644 --- a/packages/SettingsLib/res/values-sl/strings.xml +++ b/packages/SettingsLib/res/values-sl/strings.xml @@ -450,7 +450,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Še <xliff:g id="TIME">%1$s</xliff:g> do polne napolnjenosti"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do polne napolnjenosti"</string> - <string name="power_charging_limited" msgid="1956874810658999681">"<xliff:g id="LEVEL">%1$s</xliff:g> – Optimizacija za ohran. zmog. baterije"</string> + <string name="power_charging_limited" msgid="1956874810658999681">"<xliff:g id="LEVEL">%1$s</xliff:g> – Optimizacija za ohranjanje zmogljivosti baterije"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Neznano"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Polnjenje"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Hitro polnjenje"</string> @@ -555,7 +555,8 @@ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Ustvarjanje novega uporabnika …"</string> <string name="user_nickname" msgid="262624187455825083">"Vzdevek"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Dodajanje gosta"</string> - <string name="guest_exit_guest" msgid="5908239569510734136">"Odstranitev gosta"</string> + <!-- no translation found for guest_exit_guest (4754204715192830850) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"Gost"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Fotografiranje"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Izberi sliko"</string> diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml index 821e897b6417..8d53b256f874 100644 --- a/packages/SettingsLib/res/values-sq/strings.xml +++ b/packages/SettingsLib/res/values-sq/strings.xml @@ -553,7 +553,7 @@ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Po krijohet një përdorues i ri…"</string> <string name="user_nickname" msgid="262624187455825083">"Pseudonimi"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Shto të ftuar"</string> - <string name="guest_exit_guest" msgid="5908239569510734136">"Hiq të ftuarin"</string> + <string name="guest_exit_guest" msgid="4754204715192830850">"Jepi fund sesionit të vizitorit"</string> <string name="guest_nickname" msgid="6332276931583337261">"I ftuar"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Bëj një fotografi"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Zgjidh një imazh"</string> diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml index 207ca7437e5a..0b64a705cd7d 100644 --- a/packages/SettingsLib/res/values-sr/strings.xml +++ b/packages/SettingsLib/res/values-sr/strings.xml @@ -450,7 +450,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Напуниће се за <xliff:g id="TIME">%1$s</xliff:g>"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – напуниће се за <xliff:g id="TIME">%2$s</xliff:g>"</string> - <string name="power_charging_limited" msgid="1956874810658999681">"<xliff:g id="LEVEL">%1$s</xliff:g> – Оптимизује се ради стања батерије"</string> + <string name="power_charging_limited" msgid="1956874810658999681">"<xliff:g id="LEVEL">%1$s</xliff:g> – Оптимизује се ради бољег стања батерије"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Непознато"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Пуни се"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Брзо се пуни"</string> @@ -554,7 +554,8 @@ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Прави се нови корисник…"</string> <string name="user_nickname" msgid="262624187455825083">"Надимак"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Додај госта"</string> - <string name="guest_exit_guest" msgid="5908239569510734136">"Уклони госта"</string> + <!-- no translation found for guest_exit_guest (4754204715192830850) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"Гост"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Сликај"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Одабери слику"</string> diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml index 7afd34464620..58eda86276e3 100644 --- a/packages/SettingsLib/res/values-sv/strings.xml +++ b/packages/SettingsLib/res/values-sv/strings.xml @@ -553,7 +553,8 @@ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Skapar ny användare …"</string> <string name="user_nickname" msgid="262624187455825083">"Smeknamn"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Lägg till gäst"</string> - <string name="guest_exit_guest" msgid="5908239569510734136">"Ta bort gäst"</string> + <!-- no translation found for guest_exit_guest (4754204715192830850) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"Gäst"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Ta ett foto"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Välj en bild"</string> diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml index b08da895c4b1..889163bcbd40 100644 --- a/packages/SettingsLib/res/values-sw/strings.xml +++ b/packages/SettingsLib/res/values-sw/strings.xml @@ -492,8 +492,8 @@ <string name="status_unavailable" msgid="5279036186589861608">"Hamna"</string> <string name="wifi_status_mac_randomized" msgid="466382542497832189">"Imechagua anwani ya MAC kwa nasibu"</string> <plurals name="wifi_tether_connected_summary" formatted="false" msgid="6317236306047306139"> - <item quantity="other">Imeunganisha vifaa %1$d</item> - <item quantity="one">Imeunganisha kifaa %1$d</item> + <item quantity="other">Vifaa %1$d vimeunganishwa</item> + <item quantity="one">Kifaa %1$d kimeunganishwa</item> </plurals> <string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"Muda zaidi."</string> <string name="accessibility_manual_zen_less_time" msgid="6828877595848229965">"Muda kidogo."</string> @@ -553,7 +553,8 @@ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Inaweka mtumiaji mpya…"</string> <string name="user_nickname" msgid="262624187455825083">"Jina wakilishi"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Weka mgeni"</string> - <string name="guest_exit_guest" msgid="5908239569510734136">"Ondoa mgeni"</string> + <!-- no translation found for guest_exit_guest (4754204715192830850) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"Mgeni"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Piga picha"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Chagua picha"</string> diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml index 5fdf8bd61308..e0de7782f5b2 100644 --- a/packages/SettingsLib/res/values-ta/strings.xml +++ b/packages/SettingsLib/res/values-ta/strings.xml @@ -553,7 +553,8 @@ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"புதிய பயனரை உருவாக்குகிறது…"</string> <string name="user_nickname" msgid="262624187455825083">"புனைப்பெயர்"</string> <string name="guest_new_guest" msgid="3482026122932643557">"கெஸ்ட்டைச் சேர்"</string> - <string name="guest_exit_guest" msgid="5908239569510734136">"கெஸ்ட்டை அகற்று"</string> + <!-- no translation found for guest_exit_guest (4754204715192830850) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"கெஸ்ட்"</string> <string name="user_image_take_photo" msgid="467512954561638530">"படமெடுங்கள்"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"படத்தைத் தேர்வுசெய்யுங்கள்"</string> diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml index 38f08b2b8603..53f4abf90079 100644 --- a/packages/SettingsLib/res/values-te/strings.xml +++ b/packages/SettingsLib/res/values-te/strings.xml @@ -450,7 +450,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"ఛార్జ్ అవ్వడానికి <xliff:g id="TIME">%1$s</xliff:g> సమయం మిగిలి ఉంది"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - ఛార్జ్ అవ్వడానికి <xliff:g id="TIME">%2$s</xliff:g> పడుతుంది"</string> - <string name="power_charging_limited" msgid="1956874810658999681">"<xliff:g id="LEVEL">%1$s</xliff:g> - బ్యాటరీ స్థితిని ఆప్టిమైజ్ చేయడం కోసం"</string> + <string name="power_charging_limited" msgid="1956874810658999681">"<xliff:g id="LEVEL">%1$s</xliff:g> - బ్యాటరీ జీవితకాలాన్ని పెంచడం కోసం ఆప్టిమైజ్ చేస్తోంది"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"తెలియదు"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"ఛార్జ్ అవుతోంది"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"వేగవంతమైన ఛార్జింగ్"</string> @@ -553,7 +553,8 @@ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"కొత్త యూజర్ను క్రియేట్ చేస్తోంది…"</string> <string name="user_nickname" msgid="262624187455825083">"మారుపేరు"</string> <string name="guest_new_guest" msgid="3482026122932643557">"అతిథిని జోడించండి"</string> - <string name="guest_exit_guest" msgid="5908239569510734136">"అతిథిని తీసివేయండి"</string> + <!-- no translation found for guest_exit_guest (4754204715192830850) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"అతిథి"</string> <string name="user_image_take_photo" msgid="467512954561638530">"ఒక ఫోటో తీయండి"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"ఇమేజ్ను ఎంచుకోండి"</string> diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml index a8951388e488..5f93563229fa 100644 --- a/packages/SettingsLib/res/values-th/strings.xml +++ b/packages/SettingsLib/res/values-th/strings.xml @@ -284,7 +284,7 @@ <string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"เพิ่มระดับการบันทึก Wi‑Fi แสดงต่อ SSID RSSI ในตัวเลือก Wi‑Fi"</string> <string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"ลดการเปลืองแบตเตอรี่และเพิ่มประสิทธิภาพเครือข่าย"</string> <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"เมื่อเปิดใช้โหมดนี้ ที่อยู่ MAC ของอุปกรณ์นี้อาจเปลี่ยนทุกครั้งที่เชื่อมต่อกับเครือข่ายที่มีการเปิดใช้การสุ่ม MAC"</string> - <string name="wifi_metered_label" msgid="8737187690304098638">"แบบจำกัดปริมาณอินเทอร์เน็ต"</string> + <string name="wifi_metered_label" msgid="8737187690304098638">"แบบจำกัดปริมาณ"</string> <string name="wifi_unmetered_label" msgid="6174142840934095093">"ไม่มีการวัดปริมาณอินเทอร์เน็ต"</string> <string name="select_logd_size_title" msgid="1604578195914595173">"ขนาดบัฟเฟอร์ของตัวบันทึก"</string> <string name="select_logd_size_dialog_title" msgid="2105401994681013578">"เลือกขนาด Logger ต่อบัฟเฟอร์ไฟล์บันทึก"</string> @@ -553,7 +553,7 @@ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"กำลังสร้างผู้ใช้ใหม่…"</string> <string name="user_nickname" msgid="262624187455825083">"ชื่อเล่น"</string> <string name="guest_new_guest" msgid="3482026122932643557">"เพิ่มผู้เข้าร่วม"</string> - <string name="guest_exit_guest" msgid="5908239569510734136">"นำผู้เข้าร่วมออก"</string> + <string name="guest_exit_guest" msgid="4754204715192830850">"จบเซสชันผู้เยี่ยมชม"</string> <string name="guest_nickname" msgid="6332276931583337261">"ผู้ใช้ชั่วคราว"</string> <string name="user_image_take_photo" msgid="467512954561638530">"ถ่ายรูป"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"เลือกรูปภาพ"</string> diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml index b451d894d254..9bbfb1d82f68 100644 --- a/packages/SettingsLib/res/values-tl/strings.xml +++ b/packages/SettingsLib/res/values-tl/strings.xml @@ -553,7 +553,7 @@ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Gumagawa ng bagong user…"</string> <string name="user_nickname" msgid="262624187455825083">"Nickname"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Magdagdag ng bisita"</string> - <string name="guest_exit_guest" msgid="5908239569510734136">"Alisin ang bisita"</string> + <string name="guest_exit_guest" msgid="4754204715192830850">"Tapusin ang session ng bisita"</string> <string name="guest_nickname" msgid="6332276931583337261">"Bisita"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Kumuha ng larawan"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Pumili ng larawan"</string> diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml index e1ff56baf17d..9dd32b6f78ea 100644 --- a/packages/SettingsLib/res/values-tr/strings.xml +++ b/packages/SettingsLib/res/values-tr/strings.xml @@ -553,7 +553,7 @@ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Yeni kullanıcı oluşturuluyor…"</string> <string name="user_nickname" msgid="262624187455825083">"Takma ad"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Misafir ekle"</string> - <string name="guest_exit_guest" msgid="5908239569510734136">"Misafir oturumunu kaldır"</string> + <string name="guest_exit_guest" msgid="4754204715192830850">"Misafir oturumunu sonlandır"</string> <string name="guest_nickname" msgid="6332276931583337261">"Misafir"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Fotoğraf çek"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Resim seç"</string> diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml index 6806ce6f1f4a..99cc95871346 100644 --- a/packages/SettingsLib/res/values-uk/strings.xml +++ b/packages/SettingsLib/res/values-uk/strings.xml @@ -450,7 +450,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> до повного заряду"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до повного заряду"</string> - <string name="power_charging_limited" msgid="1956874810658999681">"<xliff:g id="LEVEL">%1$s</xliff:g> – стан акумулятора оптимізується"</string> + <string name="power_charging_limited" msgid="1956874810658999681">"<xliff:g id="LEVEL">%1$s</xliff:g> – Оптимізація для збереження заряду акумулятора"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Невідомо"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Заряджається"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Швидке заряджання"</string> @@ -555,7 +555,8 @@ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Створення нового користувача…"</string> <string name="user_nickname" msgid="262624187455825083">"Псевдонім"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Додати гостя"</string> - <string name="guest_exit_guest" msgid="5908239569510734136">"Видалити гостя"</string> + <!-- no translation found for guest_exit_guest (4754204715192830850) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"Гість"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Зробити фотографію"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Вибрати зображення"</string> diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml index 5a8b6dc87006..515cf702cdea 100644 --- a/packages/SettingsLib/res/values-ur/strings.xml +++ b/packages/SettingsLib/res/values-ur/strings.xml @@ -553,7 +553,8 @@ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"نیا صارف تخلیق کرنا…"</string> <string name="user_nickname" msgid="262624187455825083">"عرفی نام"</string> <string name="guest_new_guest" msgid="3482026122932643557">"مہمان کو شامل کریں"</string> - <string name="guest_exit_guest" msgid="5908239569510734136">"مہمان کو ہٹائیں"</string> + <!-- no translation found for guest_exit_guest (4754204715192830850) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"مہمان"</string> <string name="user_image_take_photo" msgid="467512954561638530">"ایک تصویر لیں"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"ایک تصویر منتخب کریں"</string> diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml index 7530016763b0..18300468023c 100644 --- a/packages/SettingsLib/res/values-uz/strings.xml +++ b/packages/SettingsLib/res/values-uz/strings.xml @@ -450,7 +450,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> ichida toʻladi"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> ichida toʻladi"</string> - <string name="power_charging_limited" msgid="1956874810658999681">"<xliff:g id="LEVEL">%1$s</xliff:g> – Batareya quvvati muvozanatlanmoqda"</string> + <string name="power_charging_limited" msgid="1956874810658999681">"<xliff:g id="LEVEL">%1$s</xliff:g> – Batareya uchun optimizatsiya"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Noma’lum"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Quvvat olmoqda"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Tezkor quvvat olmoqda"</string> @@ -553,7 +553,7 @@ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Yangi foydalanuvchi yaratilmoqda…"</string> <string name="user_nickname" msgid="262624187455825083">"Nik"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Mehmon kiritish"</string> - <string name="guest_exit_guest" msgid="5908239569510734136">"Mehmon rejimini olib tashlash"</string> + <string name="guest_exit_guest" msgid="4754204715192830850">"Mehmon seansini yakunlash"</string> <string name="guest_nickname" msgid="6332276931583337261">"Mehmon"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Suratga olish"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Rasm tanlash"</string> diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml index 643fdb9f7ab6..2d56e45aabf6 100644 --- a/packages/SettingsLib/res/values-vi/strings.xml +++ b/packages/SettingsLib/res/values-vi/strings.xml @@ -553,7 +553,7 @@ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Đang tạo người dùng mới…"</string> <string name="user_nickname" msgid="262624187455825083">"Biệt hiệu"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Thêm khách"</string> - <string name="guest_exit_guest" msgid="5908239569510734136">"Xóa phiên khách"</string> + <string name="guest_exit_guest" msgid="4754204715192830850">"Kết thúc phiên khách"</string> <string name="guest_nickname" msgid="6332276931583337261">"Khách"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Chụp ảnh"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Chọn một hình ảnh"</string> diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml index 81d8106b484e..6b64d2717266 100644 --- a/packages/SettingsLib/res/values-zh-rCN/strings.xml +++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml @@ -553,7 +553,8 @@ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"正在创建新用户…"</string> <string name="user_nickname" msgid="262624187455825083">"昵称"</string> <string name="guest_new_guest" msgid="3482026122932643557">"添加访客"</string> - <string name="guest_exit_guest" msgid="5908239569510734136">"移除访客"</string> + <!-- no translation found for guest_exit_guest (4754204715192830850) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"访客"</string> <string name="user_image_take_photo" msgid="467512954561638530">"拍摄照片"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"选择图片"</string> diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml index 39a12aa23bbd..4ab580e01f28 100644 --- a/packages/SettingsLib/res/values-zh-rHK/strings.xml +++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml @@ -450,7 +450,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"還需 <xliff:g id="TIME">%1$s</xliff:g>才能充滿電"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - 還需 <xliff:g id="TIME">%2$s</xliff:g>才能充滿電"</string> - <string name="power_charging_limited" msgid="1956874810658999681">"<xliff:g id="LEVEL">%1$s</xliff:g> - 正在優化電池狀態"</string> + <string name="power_charging_limited" msgid="1956874810658999681">"<xliff:g id="LEVEL">%1$s</xliff:g> - 優化電池效能"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"未知"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"充電中"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"正在快速充電"</string> @@ -553,7 +553,7 @@ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"正在建立新使用者…"</string> <string name="user_nickname" msgid="262624187455825083">"暱稱"</string> <string name="guest_new_guest" msgid="3482026122932643557">"新增訪客"</string> - <string name="guest_exit_guest" msgid="5908239569510734136">"移除訪客"</string> + <string name="guest_exit_guest" msgid="4754204715192830850">"結束訪客工作階段"</string> <string name="guest_nickname" msgid="6332276931583337261">"訪客"</string> <string name="user_image_take_photo" msgid="467512954561638530">"拍照"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"選擇圖片"</string> diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml index cc35ed302960..46695dbc88ba 100644 --- a/packages/SettingsLib/res/values-zh-rTW/strings.xml +++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml @@ -450,7 +450,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g>後充飽電"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>後充飽電"</string> - <string name="power_charging_limited" msgid="1956874810658999681">"<xliff:g id="LEVEL">%1$s</xliff:g> - 針對電池狀態進行最佳化調整"</string> + <string name="power_charging_limited" msgid="1956874810658999681">"<xliff:g id="LEVEL">%1$s</xliff:g> - 最佳化調整電池狀態"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"不明"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"充電中"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"快速充電中"</string> @@ -553,7 +553,8 @@ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"正在建立新使用者…"</string> <string name="user_nickname" msgid="262624187455825083">"暱稱"</string> <string name="guest_new_guest" msgid="3482026122932643557">"新增訪客"</string> - <string name="guest_exit_guest" msgid="5908239569510734136">"移除訪客"</string> + <!-- no translation found for guest_exit_guest (4754204715192830850) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"訪客"</string> <string name="user_image_take_photo" msgid="467512954561638530">"拍照"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"選擇圖片"</string> diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml index bcc6369bb99a..f4ff6ec89d94 100644 --- a/packages/SettingsLib/res/values-zu/strings.xml +++ b/packages/SettingsLib/res/values-zu/strings.xml @@ -553,7 +553,8 @@ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Idala umsebenzisi omusha…"</string> <string name="user_nickname" msgid="262624187455825083">"Isiteketiso"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Engeza isivakashi"</string> - <string name="guest_exit_guest" msgid="5908239569510734136">"Susa isihambeli"</string> + <!-- no translation found for guest_exit_guest (4754204715192830850) --> + <skip /> <string name="guest_nickname" msgid="6332276931583337261">"Isihambeli"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Thatha isithombe"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Khetha isithombe"</string> diff --git a/packages/SettingsLib/src/com/android/settingslib/connectivity/ConnectivitySubsystemsRecoveryManager.java b/packages/SettingsLib/src/com/android/settingslib/connectivity/ConnectivitySubsystemsRecoveryManager.java index a92105320a62..02d1c2e65c8d 100644 --- a/packages/SettingsLib/src/com/android/settingslib/connectivity/ConnectivitySubsystemsRecoveryManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/connectivity/ConnectivitySubsystemsRecoveryManager.java @@ -23,12 +23,12 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageManager; import android.net.wifi.WifiManager; +import android.net.wifi.WifiManager.SubsystemRestartTrackingCallback; import android.os.Handler; import android.os.HandlerExecutor; import android.provider.Settings; import android.telephony.PhoneStateListener; import android.telephony.TelephonyManager; -import android.text.TextUtils; import android.util.Log; /** @@ -58,35 +58,18 @@ public class ConnectivitySubsystemsRecoveryManager { private boolean mWifiRestartInProgress = false; private boolean mTelephonyRestartInProgress = false; private RecoveryStatusCallback mCurrentRecoveryCallback = null; - private final BroadcastReceiver mWifiMonitor = new BroadcastReceiver() { + private final SubsystemRestartTrackingCallback mWifiSubsystemRestartTrackingCallback = + new SubsystemRestartTrackingCallback() { @Override - public void onReceive(Context context, Intent intent) { - if (!mWifiRestartInProgress || mCurrentRecoveryCallback == null) { - stopTrackingWifiRestart(); - } - - // TODO: harden this code to avoid race condition. What if WiFi toggled just before - // recovery triggered. Either use new broadcasts from framework or detect more state - // changes. - boolean recoveryDone = false; - if (TextUtils.equals(intent.getAction(), WifiManager.WIFI_STATE_CHANGED_ACTION)) { - if (intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, - WifiManager.WIFI_STATE_UNKNOWN) == WifiManager.WIFI_STATE_ENABLED) { - recoveryDone = true; - } - } else if (TextUtils.equals(intent.getAction(), - WifiManager.WIFI_AP_STATE_CHANGED_ACTION)) { - if (intent.getIntExtra(WifiManager.EXTRA_WIFI_AP_STATE, - WifiManager.WIFI_AP_STATE_FAILED) == WifiManager.WIFI_AP_STATE_ENABLED) { - recoveryDone = true; - } - } + public void onSubsystemRestarting() { + // going to do nothing on this - already assuming that subsystem is restarting + } - if (recoveryDone) { - mWifiRestartInProgress = false; - stopTrackingWifiRestart(); - checkIfAllSubsystemsRestartsAreDone(); - } + @Override + public void onSubsystemRestarted() { + mWifiRestartInProgress = false; + stopTrackingWifiRestart(); + checkIfAllSubsystemsRestartsAreDone(); } }; private final PhoneStateListener mPhoneStateListener = new PhoneStateListener() { @@ -208,13 +191,13 @@ public class ConnectivitySubsystemsRecoveryManager { } private void startTrackingWifiRestart() { - IntentFilter filter = new IntentFilter(WifiManager.WIFI_STATE_CHANGED_ACTION); - filter.addAction(WifiManager.WIFI_AP_STATE_CHANGED_ACTION); - mContext.registerReceiver(mWifiMonitor, filter, null, mHandler); + mWifiManager.registerSubsystemRestartTrackingCallback(new HandlerExecutor(mHandler), + mWifiSubsystemRestartTrackingCallback); } private void stopTrackingWifiRestart() { - mContext.unregisterReceiver(mWifiMonitor); + mWifiManager.unregisterWifiSubsystemRestartTrackingCallback( + mWifiSubsystemRestartTrackingCallback); } private void startTrackingTelephonyRestart() { diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/emergencynumber/EmergencyNumberUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/emergencynumber/EmergencyNumberUtilsTest.java index 99d2ff7f322f..695b5b6ba061 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/emergencynumber/EmergencyNumberUtilsTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/emergencynumber/EmergencyNumberUtilsTest.java @@ -18,15 +18,21 @@ package com.android.settingslib.emergencynumber; import static android.telephony.emergency.EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_POLICE; +import static com.android.settingslib.emergencynumber.EmergencyNumberUtils.EMERGENCY_GESTURE_CALL_NUMBER; + import static com.google.common.truth.Truth.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.nullable; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import android.content.ContentResolver; import android.content.Context; import android.content.pm.PackageManager; -import android.provider.Settings; +import android.net.Uri; +import android.os.Bundle; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; import android.telephony.emergency.EmergencyNumber; @@ -38,7 +44,6 @@ import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; -import org.robolectric.RuntimeEnvironment; import java.util.ArrayList; import java.util.List; @@ -55,12 +60,15 @@ public class EmergencyNumberUtilsTest { private PackageManager mPackageManager; @Mock private TelephonyManager mTelephonyManager; + @Mock + ContentResolver mContentResolver; private EmergencyNumberUtils mUtils; @Before public void setup() { MockitoAnnotations.initMocks(this); when(mContext.getPackageManager()).thenReturn(mPackageManager); + when(mContext.getContentResolver()).thenReturn(mContentResolver); } @Test @@ -89,11 +97,10 @@ public class EmergencyNumberUtilsTest { when(mContext.getSystemService(TelephonyManager.class)).thenReturn(mTelephonyManager); addEmergencyNumberToTelephony(); - ContentResolver resolver = RuntimeEnvironment.application.getContentResolver(); - when(mContext.getContentResolver()).thenReturn(resolver); - Settings.Secure.putString(resolver, Settings.Secure.EMERGENCY_GESTURE_CALL_NUMBER, - USER_OVERRIDE_EMERGENCY_NUMBER); - + Bundle bundle = new Bundle(); + bundle.putString(EMERGENCY_GESTURE_CALL_NUMBER, USER_OVERRIDE_EMERGENCY_NUMBER); + when(mContentResolver.call(any(Uri.class), anyString(), nullable(String.class), nullable( + Bundle.class))).thenReturn(bundle); mUtils = new EmergencyNumberUtils(mContext); assertThat(mUtils.getPoliceNumber()).isEqualTo(USER_OVERRIDE_EMERGENCY_NUMBER); diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index f83f6706d55b..0a43014dee68 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -120,6 +120,7 @@ <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" /> <uses-permission android:name="android.permission.CREATE_USERS" /> <uses-permission android:name="android.permission.MANAGE_DEVICE_ADMINS" /> + <uses-permission android:name="android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS" /> <uses-permission android:name="android.permission.ACCESS_LOWPAN_STATE"/> <uses-permission android:name="android.permission.CHANGE_LOWPAN_STATE"/> <uses-permission android:name="android.permission.READ_LOWPAN_CREDENTIAL"/> diff --git a/packages/SystemUI/res-keyguard/values-ar/strings.xml b/packages/SystemUI/res-keyguard/values-ar/strings.xml index 459d162c65db..c027f8df1f66 100644 --- a/packages/SystemUI/res-keyguard/values-ar/strings.xml +++ b/packages/SystemUI/res-keyguard/values-ar/strings.xml @@ -38,7 +38,7 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • جارٍ الشحن"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • جارٍ الشحن سريعًا"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • جارٍ الشحن ببطء"</string> - <string name="keyguard_plugged_in_charging_limited" msgid="1158086783302116604">"<xliff:g id="PERCENTAGE">%s</xliff:g> • التحسين لسلامة البطارية"</string> + <string name="keyguard_plugged_in_charging_limited" msgid="1158086783302116604">"<xliff:g id="PERCENTAGE">%s</xliff:g> • التحسين للحفاظ على سلامة البطارية"</string> <string name="keyguard_low_battery" msgid="1868012396800230904">"توصيل جهاز الشحن."</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"اضغط على \"القائمة\" لإلغاء التأمين."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"الشبكة مؤمّنة"</string> diff --git a/packages/SystemUI/res-keyguard/values-b+sr+Latn/strings.xml b/packages/SystemUI/res-keyguard/values-b+sr+Latn/strings.xml index 346874060759..4220526635f7 100644 --- a/packages/SystemUI/res-keyguard/values-b+sr+Latn/strings.xml +++ b/packages/SystemUI/res-keyguard/values-b+sr+Latn/strings.xml @@ -38,7 +38,7 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Puni se"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Brzo se puni"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Sporo se puni"</string> - <string name="keyguard_plugged_in_charging_limited" msgid="1158086783302116604">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Optimizuje se radi dobrog stanja baterije"</string> + <string name="keyguard_plugged_in_charging_limited" msgid="1158086783302116604">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Optimizuje se radi boljeg stanja baterije"</string> <string name="keyguard_low_battery" msgid="1868012396800230904">"Priključite punjač."</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Pritisnite Meni da biste otključali."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Mreža je zaključana"</string> diff --git a/packages/SystemUI/res-keyguard/values-bg/strings.xml b/packages/SystemUI/res-keyguard/values-bg/strings.xml index 0911efade706..dc4ee693d89c 100644 --- a/packages/SystemUI/res-keyguard/values-bg/strings.xml +++ b/packages/SystemUI/res-keyguard/values-bg/strings.xml @@ -38,7 +38,7 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Зарежда се"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Зарежда се бързо"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Зарежда се бавно"</string> - <string name="keyguard_plugged_in_charging_limited" msgid="1158086783302116604">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Оптимизиране за състоянието на батерията"</string> + <string name="keyguard_plugged_in_charging_limited" msgid="1158086783302116604">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Оптимизиране с цел състоянието на батерията"</string> <string name="keyguard_low_battery" msgid="1868012396800230904">"Свържете зарядното си устройство."</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Натиснете „Меню“, за да отключите."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Мрежата е заключена"</string> diff --git a/packages/SystemUI/res-keyguard/values-ca/strings.xml b/packages/SystemUI/res-keyguard/values-ca/strings.xml index 421ddb6d31e6..2e3b4af4a68f 100644 --- a/packages/SystemUI/res-keyguard/values-ca/strings.xml +++ b/packages/SystemUI/res-keyguard/values-ca/strings.xml @@ -38,7 +38,7 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • S\'està carregant"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • S\'està carregant ràpidament"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • S\'està carregant lentament"</string> - <string name="keyguard_plugged_in_charging_limited" msgid="1158086783302116604">"<xliff:g id="PERCENTAGE">%s</xliff:g> • S\'està optimitzant per mantenir el bon estat de la bateria"</string> + <string name="keyguard_plugged_in_charging_limited" msgid="1158086783302116604">"<xliff:g id="PERCENTAGE">%s</xliff:g> • S\'està optimitzant per preservar l\'estat de la bateria"</string> <string name="keyguard_low_battery" msgid="1868012396800230904">"Connecta el carregador."</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Prem Menú per desbloquejar."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"La xarxa està bloquejada"</string> diff --git a/packages/SystemUI/res-keyguard/values-eu/strings.xml b/packages/SystemUI/res-keyguard/values-eu/strings.xml index ce8ef202a038..93fe516a8aa6 100644 --- a/packages/SystemUI/res-keyguard/values-eu/strings.xml +++ b/packages/SystemUI/res-keyguard/values-eu/strings.xml @@ -38,7 +38,7 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Kargatzen"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Bizkor kargatzen"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Mantso kargatzen"</string> - <string name="keyguard_plugged_in_charging_limited" msgid="1158086783302116604">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Optimizatzen bateria egoera onean mantentzeko"</string> + <string name="keyguard_plugged_in_charging_limited" msgid="1158086783302116604">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Optimizatzen, bateria egoera onean mantentzeko"</string> <string name="keyguard_low_battery" msgid="1868012396800230904">"Konektatu kargagailua."</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Desblokeatzeko, sakatu Menua."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Sarea blokeatuta dago"</string> diff --git a/packages/SystemUI/res-keyguard/values-fr-rCA/strings.xml b/packages/SystemUI/res-keyguard/values-fr-rCA/strings.xml index f2156252e241..d18690103c96 100644 --- a/packages/SystemUI/res-keyguard/values-fr-rCA/strings.xml +++ b/packages/SystemUI/res-keyguard/values-fr-rCA/strings.xml @@ -38,7 +38,7 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"En recharge : <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"En recharge rapide : <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"En recharge lente : <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> - <string name="keyguard_plugged_in_charging_limited" msgid="1158086783302116604">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Optimisation en fonction de la santé de la pile"</string> + <string name="keyguard_plugged_in_charging_limited" msgid="1158086783302116604">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Optimisation pour préserver la pile"</string> <string name="keyguard_low_battery" msgid="1868012396800230904">"Branchez votre chargeur."</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Appuyez sur la touche Menu pour déverrouiller l\'appareil."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Réseau verrouillé"</string> diff --git a/packages/SystemUI/res-keyguard/values-gl/strings.xml b/packages/SystemUI/res-keyguard/values-gl/strings.xml index 01a0fac58016..f8c5ebac9517 100644 --- a/packages/SystemUI/res-keyguard/values-gl/strings.xml +++ b/packages/SystemUI/res-keyguard/values-gl/strings.xml @@ -38,7 +38,7 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Cargando"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Cargando rapidamente"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Cargando lentamente"</string> - <string name="keyguard_plugged_in_charging_limited" msgid="1158086783302116604">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Optimizando para manter a batería en bo estado"</string> + <string name="keyguard_plugged_in_charging_limited" msgid="1158086783302116604">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Optimizando a preservación da batería"</string> <string name="keyguard_low_battery" msgid="1868012396800230904">"Conecta o cargador."</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Preme Menú para desbloquear."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Bloqueada pola rede"</string> diff --git a/packages/SystemUI/res-keyguard/values-hy/strings.xml b/packages/SystemUI/res-keyguard/values-hy/strings.xml index c8ed16d10327..ab07ba46c14d 100644 --- a/packages/SystemUI/res-keyguard/values-hy/strings.xml +++ b/packages/SystemUI/res-keyguard/values-hy/strings.xml @@ -38,7 +38,7 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Լիցքավորում"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Արագ լիցքավորում"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Դանդաղ լիցքավորում"</string> - <string name="keyguard_plugged_in_charging_limited" msgid="1158086783302116604">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Օպտիմալացվում է մարտկոցի աշխատանքային հզորության համար"</string> + <string name="keyguard_plugged_in_charging_limited" msgid="1158086783302116604">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Օպտիմալացվում է մարտկոցի պահպանման համար"</string> <string name="keyguard_low_battery" msgid="1868012396800230904">"Միացրեք լիցքավորիչը:"</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Ապակողպելու համար սեղմեք Ընտրացանկը:"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Ցանցը կողպված է"</string> diff --git a/packages/SystemUI/res-keyguard/values-is/strings.xml b/packages/SystemUI/res-keyguard/values-is/strings.xml index 3f3bec9b8c8c..bb8a908d20f7 100644 --- a/packages/SystemUI/res-keyguard/values-is/strings.xml +++ b/packages/SystemUI/res-keyguard/values-is/strings.xml @@ -38,7 +38,7 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Í hleðslu"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Hröð hleðsla"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Hæg hleðsla"</string> - <string name="keyguard_plugged_in_charging_limited" msgid="1158086783302116604">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Fínstillir til að bæta rafhlöðuendingu"</string> + <string name="keyguard_plugged_in_charging_limited" msgid="1158086783302116604">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Fínstillir fyrir rafhlöðuendingu"</string> <string name="keyguard_low_battery" msgid="1868012396800230904">"Tengdu hleðslutækið."</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Ýttu á valmyndarhnappinn til að taka úr lás."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Net læst"</string> diff --git a/packages/SystemUI/res-keyguard/values-it/strings.xml b/packages/SystemUI/res-keyguard/values-it/strings.xml index 47d17ce57892..c1baadf47de0 100644 --- a/packages/SystemUI/res-keyguard/values-it/strings.xml +++ b/packages/SystemUI/res-keyguard/values-it/strings.xml @@ -38,7 +38,7 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • In carica"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Ricarica veloce"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Ricarica lenta"</string> - <string name="keyguard_plugged_in_charging_limited" msgid="1158086783302116604">"<xliff:g id="PERCENTAGE">%s</xliff:g> • È in corso l\'ottimizzazione per l\'integrità della batteria"</string> + <string name="keyguard_plugged_in_charging_limited" msgid="1158086783302116604">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Ottimizzazione per integrità batteria"</string> <string name="keyguard_low_battery" msgid="1868012396800230904">"Collega il caricabatterie."</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Premi Menu per sbloccare."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Rete bloccata"</string> diff --git a/packages/SystemUI/res-keyguard/values-kk/strings.xml b/packages/SystemUI/res-keyguard/values-kk/strings.xml index b20cb05ea86f..8e701fe86430 100644 --- a/packages/SystemUI/res-keyguard/values-kk/strings.xml +++ b/packages/SystemUI/res-keyguard/values-kk/strings.xml @@ -38,7 +38,7 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Зарядталуда"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Жылдам зарядталуда"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Баяу зарядталуда"</string> - <string name="keyguard_plugged_in_charging_limited" msgid="1158086783302116604">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Батареяның жұмыс істеу қабілеті оңтайландырылуда"</string> + <string name="keyguard_plugged_in_charging_limited" msgid="1158086783302116604">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Батарея жұмысын оңтайландыру"</string> <string name="keyguard_low_battery" msgid="1868012396800230904">"Зарядтағышты қосыңыз."</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Ашу үшін \"Мәзір\" пернесін басыңыз."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Желі құлыптаулы"</string> diff --git a/packages/SystemUI/res-keyguard/values-kn/strings.xml b/packages/SystemUI/res-keyguard/values-kn/strings.xml index a52712379776..96865419cbf7 100644 --- a/packages/SystemUI/res-keyguard/values-kn/strings.xml +++ b/packages/SystemUI/res-keyguard/values-kn/strings.xml @@ -38,7 +38,7 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ಚಾರ್ಜ್ ಮಾಡಲಾಗುತ್ತಿದೆ"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ವೇಗವಾಗಿ ಚಾರ್ಜ್ಮಾಡಲಾಗುತ್ತಿದೆ"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ನಿಧಾನವಾಗಿ ಚಾರ್ಜ್ ಮಾಡಲಾಗುತ್ತಿದೆ"</string> - <string name="keyguard_plugged_in_charging_limited" msgid="1158086783302116604">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ಬ್ಯಾಟರಿಯ ಆರೋಗ್ಯಕ್ಕಾಗಿ ಆಪ್ಟಿಮೈಸ್ ಮಾಡಲಾಗುತ್ತಿದೆ"</string> + <string name="keyguard_plugged_in_charging_limited" msgid="1158086783302116604">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ಬ್ಯಾಟರಿಯ ಸುಸ್ಥಿತಿಗಾಗಿ ಆಪ್ಟಿಮೈಸ್ ಮಾಡಲಾಗುತ್ತಿದೆ"</string> <string name="keyguard_low_battery" msgid="1868012396800230904">"ನಿಮ್ಮ ಚಾರ್ಜರ್ ಸಂಪರ್ಕಗೊಳಿಸಿ."</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"ಅನ್ಲಾಕ್ ಮಾಡಲು ಮೆನು ಒತ್ತಿರಿ."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"ನೆಟ್ವರ್ಕ್ ಲಾಕ್ ಆಗಿದೆ"</string> diff --git a/packages/SystemUI/res-keyguard/values-mn/strings.xml b/packages/SystemUI/res-keyguard/values-mn/strings.xml index 2ff4a22d6737..13c152f8f38b 100644 --- a/packages/SystemUI/res-keyguard/values-mn/strings.xml +++ b/packages/SystemUI/res-keyguard/values-mn/strings.xml @@ -38,7 +38,7 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Цэнэглэж байна"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Хурдан цэнэглэж байна"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Удаан цэнэглэж байна"</string> - <string name="keyguard_plugged_in_charging_limited" msgid="1158086783302116604">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Батарейн чанарыг оновчилж байна"</string> + <string name="keyguard_plugged_in_charging_limited" msgid="1158086783302116604">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Батарейн барилтыг оновчилж байна"</string> <string name="keyguard_low_battery" msgid="1868012396800230904">"Цэнэглэгчээ холбоно уу."</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Түгжээг тайлах бол цэсийг дарна уу."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Сүлжээ түгжигдсэн"</string> diff --git a/packages/SystemUI/res-keyguard/values-pa/strings.xml b/packages/SystemUI/res-keyguard/values-pa/strings.xml index 43d549c92e6b..04419e039b58 100644 --- a/packages/SystemUI/res-keyguard/values-pa/strings.xml +++ b/packages/SystemUI/res-keyguard/values-pa/strings.xml @@ -38,7 +38,7 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ਚਾਰਜ ਹੋ ਰਿਹਾ ਹੈ"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ਤੇਜ਼ੀ ਨਾਲ ਚਾਰਜ ਹੋ ਰਿਹਾ ਹੈ"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ਹੌਲੀ-ਹੌਲੀ ਚਾਰਜ ਹੋ ਰਿਹਾ ਹੈ"</string> - <string name="keyguard_plugged_in_charging_limited" msgid="1158086783302116604">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ਬੈਟਰੀ ਸਥਿਤੀ ਲਈ ਅਨੁਕੂਲ ਬਣਾਇਆ ਜਾ ਰਿਹਾ"</string> + <string name="keyguard_plugged_in_charging_limited" msgid="1158086783302116604">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ਬੈਟਰੀ ਦੀ ਸਥਿਤੀ ਲਈ ਅਨੁਕੂਲ ਬਣਾਇਆ ਜਾ ਰਿਹਾ ਹੈ"</string> <string name="keyguard_low_battery" msgid="1868012396800230904">"ਆਪਣਾ ਚਾਰਜਰ ਕਨੈਕਟ ਕਰੋ।"</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"ਅਣਲਾਕ ਕਰਨ ਲਈ \"ਮੀਨੂ\" ਦਬਾਓ।"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"ਨੈੱਟਵਰਕ ਲਾਕ ਕੀਤਾ ਗਿਆ"</string> diff --git a/packages/SystemUI/res-keyguard/values-pl/strings.xml b/packages/SystemUI/res-keyguard/values-pl/strings.xml index 76f755c2b96a..01427be041ad 100644 --- a/packages/SystemUI/res-keyguard/values-pl/strings.xml +++ b/packages/SystemUI/res-keyguard/values-pl/strings.xml @@ -38,7 +38,7 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Ładowanie"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Szybkie ładowanie"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Wolne ładowanie"</string> - <string name="keyguard_plugged_in_charging_limited" msgid="1158086783302116604">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Optymalizuję, by utrzymać baterię w dobrym stanie"</string> + <string name="keyguard_plugged_in_charging_limited" msgid="1158086783302116604">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Optymalizuję, aby utrzymać baterię w dobrym stanie"</string> <string name="keyguard_low_battery" msgid="1868012396800230904">"Podłącz ładowarkę."</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Naciśnij Menu, aby odblokować."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Sieć zablokowana"</string> diff --git a/packages/SystemUI/res-keyguard/values-si/strings.xml b/packages/SystemUI/res-keyguard/values-si/strings.xml index 3a6078224be2..64609b302069 100644 --- a/packages/SystemUI/res-keyguard/values-si/strings.xml +++ b/packages/SystemUI/res-keyguard/values-si/strings.xml @@ -38,7 +38,7 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ආරෝපණය වෙමින්"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • වේගයෙන් ආරෝපණය වෙමින්"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • සෙමින් ආරෝපණය වෙමින්"</string> - <string name="keyguard_plugged_in_charging_limited" msgid="1158086783302116604">"<xliff:g id="PERCENTAGE">%s</xliff:g> • බැටරි සෞඛ්යය සඳහා ප්රශස්ත කරමින්"</string> + <string name="keyguard_plugged_in_charging_limited" msgid="1158086783302116604">"<xliff:g id="PERCENTAGE">%s</xliff:g> • බැටරි ආයු කාලය වැඩි දියුණු කරමින්"</string> <string name="keyguard_low_battery" msgid="1868012396800230904">"ඔබගේ ආරෝපකයට සම්බන්ධ කරන්න."</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"අගුලු හැරීමට මෙනුව ඔබන්න."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"ජාලය අගුළු දමා ඇත"</string> diff --git a/packages/SystemUI/res-keyguard/values-sr/strings.xml b/packages/SystemUI/res-keyguard/values-sr/strings.xml index 90b8a3e362fd..7ca04ce8198a 100644 --- a/packages/SystemUI/res-keyguard/values-sr/strings.xml +++ b/packages/SystemUI/res-keyguard/values-sr/strings.xml @@ -38,7 +38,7 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Пуни се"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Брзо се пуни"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Споро се пуни"</string> - <string name="keyguard_plugged_in_charging_limited" msgid="1158086783302116604">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Оптимизује се ради доброг стања батерије"</string> + <string name="keyguard_plugged_in_charging_limited" msgid="1158086783302116604">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Оптимизује се ради бољег стања батерије"</string> <string name="keyguard_low_battery" msgid="1868012396800230904">"Прикључите пуњач."</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Притисните Мени да бисте откључали."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Мрежа је закључана"</string> diff --git a/packages/SystemUI/res-keyguard/values-te/strings.xml b/packages/SystemUI/res-keyguard/values-te/strings.xml index c69a8f56b933..990253a7862f 100644 --- a/packages/SystemUI/res-keyguard/values-te/strings.xml +++ b/packages/SystemUI/res-keyguard/values-te/strings.xml @@ -38,7 +38,7 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ఛార్జ్ అవుతోంది"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • వేగంగా ఛార్జ్ అవుతోంది"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • నెమ్మదిగా ఛార్జ్ అవుతోంది"</string> - <string name="keyguard_plugged_in_charging_limited" msgid="1158086783302116604">"<xliff:g id="PERCENTAGE">%s</xliff:g> • బ్యాటరీ జీవితకాలాన్ని ఆప్టిమైజ్ చేయడం కోసం"</string> + <string name="keyguard_plugged_in_charging_limited" msgid="1158086783302116604">"<xliff:g id="PERCENTAGE">%s</xliff:g> • బ్యాటరీ జీవితకాలాన్ని పెంచడం కోసం ఆప్టిమైజ్ చేస్తోంది"</string> <string name="keyguard_low_battery" msgid="1868012396800230904">"మీ ఛార్జర్ను కనెక్ట్ చేయండి."</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"అన్లాక్ చేయడానికి మెనుని నొక్కండి."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"నెట్వర్క్ లాక్ చేయబడింది"</string> diff --git a/packages/SystemUI/res-keyguard/values-uk/strings.xml b/packages/SystemUI/res-keyguard/values-uk/strings.xml index a16576d37e9c..62950e29c70e 100644 --- a/packages/SystemUI/res-keyguard/values-uk/strings.xml +++ b/packages/SystemUI/res-keyguard/values-uk/strings.xml @@ -38,7 +38,7 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Заряджання"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Швидке заряджання"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Повільне заряджання"</string> - <string name="keyguard_plugged_in_charging_limited" msgid="1158086783302116604">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Стан акумулятора оптимізується"</string> + <string name="keyguard_plugged_in_charging_limited" msgid="1158086783302116604">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Оптимізація для збереження заряду акумулятора"</string> <string name="keyguard_low_battery" msgid="1868012396800230904">"Підключіть зарядний пристрій."</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Натисніть меню, щоб розблокувати."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Мережу заблоковано"</string> diff --git a/packages/SystemUI/res-keyguard/values-uz/strings.xml b/packages/SystemUI/res-keyguard/values-uz/strings.xml index 44e419180f6a..f2ca159bff0d 100644 --- a/packages/SystemUI/res-keyguard/values-uz/strings.xml +++ b/packages/SystemUI/res-keyguard/values-uz/strings.xml @@ -38,7 +38,7 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Quvvat olmoqda"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Tezkor quvvat olmoqda"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Sekin quvvat olmoqda"</string> - <string name="keyguard_plugged_in_charging_limited" msgid="1158086783302116604">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Batareya quvvati muvozanatlanmoqda"</string> + <string name="keyguard_plugged_in_charging_limited" msgid="1158086783302116604">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Batareya uchun optimizatsiya"</string> <string name="keyguard_low_battery" msgid="1868012396800230904">"Quvvatlash moslamasini ulang."</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Qulfdan chiqarish uchun Menyu tugmasini bosing."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Tarmoq qulflangan"</string> diff --git a/packages/SystemUI/res-keyguard/values-zh-rHK/strings.xml b/packages/SystemUI/res-keyguard/values-zh-rHK/strings.xml index 1054e36fc048..b4006e8b7341 100644 --- a/packages/SystemUI/res-keyguard/values-zh-rHK/strings.xml +++ b/packages/SystemUI/res-keyguard/values-zh-rHK/strings.xml @@ -38,7 +38,7 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 正在充電"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 正在快速充電"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> •正在慢速充電"</string> - <string name="keyguard_plugged_in_charging_limited" msgid="1158086783302116604">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 正在優化電池狀態"</string> + <string name="keyguard_plugged_in_charging_limited" msgid="1158086783302116604">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 優化電池效能"</string> <string name="keyguard_low_battery" msgid="1868012396800230904">"請連接充電器。"</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"按下 [選單] 即可解鎖。"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"網絡已鎖定"</string> diff --git a/packages/SystemUI/res-keyguard/values-zh-rTW/strings.xml b/packages/SystemUI/res-keyguard/values-zh-rTW/strings.xml index 7efa2f934d95..757bf56f3050 100644 --- a/packages/SystemUI/res-keyguard/values-zh-rTW/strings.xml +++ b/packages/SystemUI/res-keyguard/values-zh-rTW/strings.xml @@ -38,7 +38,7 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 充電中"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 快速充電中"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 慢速充電中"</string> - <string name="keyguard_plugged_in_charging_limited" msgid="1158086783302116604">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 針對電池狀態進行最佳化調整"</string> + <string name="keyguard_plugged_in_charging_limited" msgid="1158086783302116604">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 最佳化調整電池狀態"</string> <string name="keyguard_low_battery" msgid="1868012396800230904">"請連接充電器。"</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"按選單鍵解鎖。"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"網路已鎖定"</string> diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml index fa4eea5a805f..fe4ba63fe6c3 100644 --- a/packages/SystemUI/res/values-af/strings.xml +++ b/packages/SystemUI/res/values-af/strings.xml @@ -353,6 +353,8 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"Gebruiker"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Nuwe gebruiker"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> + <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> + <skip /> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Nie gekoppel nie"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Geen netwerk nie"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi af"</string> @@ -454,9 +456,11 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Wys profiel"</string> <string name="user_add_user" msgid="4336657383006913022">"Voeg gebruiker by"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Nuwe gebruiker"</string> - <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Verwyder gas?"</string> + <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Alle programme en data in hierdie sessie sal uitgevee word."</string> - <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Verwyder"</string> + <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Welkom terug, gas!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Wiil jy jou sessie voortsit?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Begin van voor af"</string> diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml index 5a22d919e912..9149aa636180 100644 --- a/packages/SystemUI/res/values-am/strings.xml +++ b/packages/SystemUI/res/values-am/strings.xml @@ -353,6 +353,8 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"ተጠቃሚ"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"አዲስ ተጠቃሚ"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> + <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> + <skip /> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"አልተገናኘም"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"ምንም አውታረ መረብ የለም"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi ጠፍቷል"</string> @@ -454,9 +456,11 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"መገለጫ አሳይ"</string> <string name="user_add_user" msgid="4336657383006913022">"ተጠቃሚ አክል"</string> <string name="user_new_user_name" msgid="2019166282704195789">"አዲስ ተጠቃሚ"</string> - <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"እንግዳ ይወገድ?"</string> + <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"በዚህ ክፍለ-ጊዜ ውስጥ ያሉ ሁሉም መተግበሪያዎች እና ውሂብ ይሰረዛሉ።"</string> - <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"አስወግድ"</string> + <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"እንኳን በደህና ተመለሱ እንግዳ!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"ክፍለ-ጊዜዎን መቀጠል ይፈልጋሉ?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"እንደገና ጀምር"</string> diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml index 39afa8362861..0146fcddf4fd 100644 --- a/packages/SystemUI/res/values-ar/strings.xml +++ b/packages/SystemUI/res/values-ar/strings.xml @@ -357,6 +357,8 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"المستخدم"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"مستخدم جديد"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> + <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> + <skip /> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"ليست متصلة"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"لا تتوفر شبكة"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"إيقاف Wi-Fi"</string> @@ -462,9 +464,11 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"عرض الملف الشخصي"</string> <string name="user_add_user" msgid="4336657383006913022">"إضافة مستخدم"</string> <string name="user_new_user_name" msgid="2019166282704195789">"مستخدم جديد"</string> - <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"هل تريد إزالة جلسة الضيف؟"</string> + <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"سيتم حذف كل التطبيقات والبيانات في هذه الجلسة."</string> - <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"إزالة"</string> + <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"مرحبًا بك مجددًا في جلسة الضيف"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"هل تريد متابعة جلستك؟"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"البدء من جديد"</string> diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml index cfb300941c74..3c206f2949a3 100644 --- a/packages/SystemUI/res/values-as/strings.xml +++ b/packages/SystemUI/res/values-as/strings.xml @@ -353,6 +353,8 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"ব্যৱহাৰকাৰী"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"নতুন ব্যৱহাৰকাৰী"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"ৱাই-ফাই"</string> + <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> + <skip /> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"সংযোগ হৈ থকা নাই"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"নেটৱৰ্ক নাই"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"ৱাই-ফাই অফ"</string> @@ -454,9 +456,11 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"প্ৰ\'ফাইল দেখুৱাওক"</string> <string name="user_add_user" msgid="4336657383006913022">"ব্যৱহাৰকাৰী যোগ কৰক"</string> <string name="user_new_user_name" msgid="2019166282704195789">"নতুন ব্যৱহাৰকাৰী"</string> - <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"অতিথি আঁতৰাবনে?"</string> + <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"এই ছেশ্বনৰ সকলো এপ্ আৰু ডেটা মচা হ\'ব।"</string> - <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"আঁতৰাওক"</string> + <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"আপোনাক পুনৰাই স্বাগতম জনাইছোঁ!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"আপুনি আপোনাৰ ছেশ্বন অব্যাহত ৰাখিব বিচাৰেনে?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"আকৌ আৰম্ভ কৰক"</string> diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml index fef70c3ff17b..67e1718a03fb 100644 --- a/packages/SystemUI/res/values-az/strings.xml +++ b/packages/SystemUI/res/values-az/strings.xml @@ -353,6 +353,8 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"İstifadəçi"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Yeni istifadəçi"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> + <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> + <skip /> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Bağlantı yoxdur"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Şəbəkə yoxdur"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi sönülüdür"</string> @@ -454,9 +456,11 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Show profile"</string> <string name="user_add_user" msgid="4336657383006913022">"İstifadəçi əlavə edin"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Yeni istifadəçi"</string> - <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Qonaq silinsin?"</string> + <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Bu sessiyada bütün tətbiqlər və data silinəcək."</string> - <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Yığışdır"</string> + <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Xoş gəlmisiniz!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Sessiya davam etsin?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Yenidən başlayın"</string> diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml index 978b46d13c38..ae8ffbfbe304 100644 --- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml +++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml @@ -354,6 +354,8 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"Korisnik"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Novi korisnik"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"WiFi"</string> + <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> + <skip /> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Veza nije uspostavljena"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Nema mreže"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"WiFi je isključen"</string> @@ -456,9 +458,11 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Prikaži profil"</string> <string name="user_add_user" msgid="4336657383006913022">"Dodaj korisnika"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Novi korisnik"</string> - <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Želite li da uklonite gosta?"</string> + <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Sve aplikacije i podaci u ovoj sesiji će biti izbrisani."</string> - <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Ukloni"</string> + <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Dobro došli nazad, goste!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Želite li da nastavite sesiju?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Počni iz početka"</string> diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml index 641134eb7230..b90d57e7ff5d 100644 --- a/packages/SystemUI/res/values-be/strings.xml +++ b/packages/SystemUI/res/values-be/strings.xml @@ -355,6 +355,8 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"Карыстальнік"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Новы карыстальнік"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> + <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> + <skip /> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Няма падключэння"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Няма сеткi"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi адключаны"</string> @@ -458,9 +460,11 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Паказаць профіль"</string> <string name="user_add_user" msgid="4336657383006913022">"Дадаць карыстальніка"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Новы карыстальнік"</string> - <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Выдаліць госця?"</string> + <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Усе праграмы і даныя гэтага сеанса будуць выдалены."</string> - <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Выдаліць"</string> + <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"З вяртаннем, госць!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Хочаце працягнуць сеанс?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Пачаць зноў"</string> diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml index 0848383d7785..bfc8ef1d1151 100644 --- a/packages/SystemUI/res/values-bg/strings.xml +++ b/packages/SystemUI/res/values-bg/strings.xml @@ -353,6 +353,8 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"Потребител"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Нов потребител"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> + <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> + <skip /> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Няма връзка"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Няма мрежа"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi е изключен"</string> @@ -454,9 +456,11 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Показване на потребителския профил"</string> <string name="user_add_user" msgid="4336657383006913022">"Добавяне на потребител"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Нов потребител"</string> - <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Да се премахне ли гостът?"</string> + <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Всички приложения и данни в тази сесия ще бъдат изтрити."</string> - <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Премахване"</string> + <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Добре дошли отново в сесията като гост!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Искате ли да продължите сесията си?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Започване отначало"</string> diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml index 109990189e1e..c41a43404628 100644 --- a/packages/SystemUI/res/values-bn/strings.xml +++ b/packages/SystemUI/res/values-bn/strings.xml @@ -85,8 +85,7 @@ <string name="screenshot_failed_title" msgid="3259148215671936891">"স্ক্রিনশট সেভ করা যায়নি"</string> <string name="screenshot_failed_to_save_user_locked_text" msgid="6156607948256936920">"স্ক্রিনশট সেভ করার আগে ডিভাইসটি অবশ্যই আনলক করতে হবে"</string> <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"আবার স্ক্রিনশট নেওয়ার চেষ্টা করুন"</string> - <!-- no translation found for screenshot_failed_to_save_text (7232739948999195960) --> - <skip /> + <string name="screenshot_failed_to_save_text" msgid="7232739948999195960">"স্ক্রিনশট সেভ করা যায়নি"</string> <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"এই অ্যাপ বা আপনার প্রতিষ্ঠান স্ক্রিনশট নেওয়ার অনুমতি দেয়নি"</string> <string name="screenshot_edit_label" msgid="8754981973544133050">"এডিট করুন"</string> <string name="screenshot_edit_description" msgid="3333092254706788906">"স্ক্রিনশট এডিট করুন"</string> @@ -354,6 +353,8 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"ব্যবহারকারী"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"নতুন ব্যবহারকারী"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"ওয়াই-ফাই"</string> + <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> + <skip /> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"সংযুক্ত নয়"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"কোনো নেটওয়ার্ক নেই"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"ওয়াই-ফাই বন্ধ"</string> @@ -455,9 +456,11 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"প্রোফাইল দেখান"</string> <string name="user_add_user" msgid="4336657383006913022">"ব্যবহারকারী জুড়ুন"</string> <string name="user_new_user_name" msgid="2019166282704195789">"নতুন ব্যবহারকারী"</string> - <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"অতিথি সরাবেন?"</string> + <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"এই সেশনের সব অ্যাপ্লিকেশান ও ডেটা মুছে ফেলা হবে।"</string> - <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"সরান"</string> + <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"অতিথি, আপনি ফিরে আসায় আপনাকে স্বাগত!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"আপনি কি আপনার সেশনটি অবিরত রাখতে চান?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"আবার শুরু করুন"</string> diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml index 3c81a4b1deb9..9a2877351c51 100644 --- a/packages/SystemUI/res/values-bs/strings.xml +++ b/packages/SystemUI/res/values-bs/strings.xml @@ -354,6 +354,8 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"Korisnik"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Novi korisnik"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"WiFi"</string> + <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> + <skip /> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Nije povezano"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Nema mreže"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"WiFi je isključen"</string> @@ -456,9 +458,11 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Pokaži profil"</string> <string name="user_add_user" msgid="4336657383006913022">"Dodaj korisnika"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Novi korisnik"</string> - <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Želite li ukloniti gosta?"</string> + <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Sve aplikacije i svi podaci iz ove sesije bit će izbrisani."</string> - <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Ukloni"</string> + <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Zdravo! Lijepo je opet vidjeti goste."</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Želite li nastaviti sesiju?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Počni ispočetka"</string> diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml index ef419ab2bd79..426005af098a 100644 --- a/packages/SystemUI/res/values-ca/strings.xml +++ b/packages/SystemUI/res/values-ca/strings.xml @@ -353,6 +353,8 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"Usuari"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Usuari nou"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> + <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> + <skip /> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Desconnectat"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"No hi ha cap xarxa"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi desconnectada"</string> @@ -454,9 +456,11 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Mostra el perfil"</string> <string name="user_add_user" msgid="4336657383006913022">"Afegeix un usuari"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Usuari nou"</string> - <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Vols suprimir el convidat?"</string> + <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Totes les aplicacions i les dades d\'aquesta sessió se suprimiran."</string> - <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Suprimeix"</string> + <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Benvingut de nou, convidat."</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Vols continuar amb la sessió?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Torna a començar"</string> diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml index 972232fd8768..3e015ec7c720 100644 --- a/packages/SystemUI/res/values-cs/strings.xml +++ b/packages/SystemUI/res/values-cs/strings.xml @@ -355,6 +355,8 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"Uživatel"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Nový uživatel"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> + <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> + <skip /> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Nepřipojeno"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Žádná síť"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi vypnuta"</string> @@ -458,9 +460,11 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Zobrazit profil"</string> <string name="user_add_user" msgid="4336657383006913022">"Přidat uživatele"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Nový uživatel"</string> - <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Odstranit hosta?"</string> + <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Veškeré aplikace a data v této relaci budou vymazána."</string> - <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Odstranit"</string> + <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Vítejte zpět v relaci hosta!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Chcete v relaci pokračovat?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Začít znovu"</string> diff --git a/packages/SystemUI/res/values-cs/strings_tv.xml b/packages/SystemUI/res/values-cs/strings_tv.xml index 0a9850c7b7a7..eeab0d9d95a2 100644 --- a/packages/SystemUI/res/values-cs/strings_tv.xml +++ b/packages/SystemUI/res/values-cs/strings_tv.xml @@ -23,7 +23,7 @@ <string name="app_accessed_mic" msgid="2754428675130470196">"Aplikace %1$s použila mikrofon"</string> <string name="notification_vpn_connected" msgid="3891023882833274730">"Síť VPN je připojena"</string> <string name="notification_vpn_disconnected" msgid="7150747626448044843">"Síť VPN je odpojena"</string> - <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"Prostřednictvím aplikace <xliff:g id="VPN_APP">%1$s</xliff:g>"</string> + <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"Přes <xliff:g id="VPN_APP">%1$s</xliff:g>"</string> <string name="tv_notification_panel_title" msgid="5311050946506276154">"Oznámení"</string> <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Žádná oznámení"</string> </resources> diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml index eb5f8caa2b78..8fd48b1bdd02 100644 --- a/packages/SystemUI/res/values-da/strings.xml +++ b/packages/SystemUI/res/values-da/strings.xml @@ -353,6 +353,8 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"Bruger"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Ny bruger"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> + <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> + <skip /> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Ikke forbundet"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Intet netværk"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi slået fra"</string> @@ -454,9 +456,11 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Vis profil"</string> <string name="user_add_user" msgid="4336657383006913022">"Tilføj bruger"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Ny bruger"</string> - <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Vil du fjerne gæsten?"</string> + <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Alle apps og data i denne session slettes."</string> - <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Fjern"</string> + <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Velkommen tilbage, gæst!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Vil du fortsætte din session?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Start forfra"</string> diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml index 66fb7eb480fe..84b63ba2db37 100644 --- a/packages/SystemUI/res/values-de/strings.xml +++ b/packages/SystemUI/res/values-de/strings.xml @@ -353,6 +353,8 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"Nutzer"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Neuer Nutzer"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"WLAN"</string> + <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> + <skip /> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Nicht verbunden"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Kein Netz"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"WLAN aus"</string> @@ -454,9 +456,11 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Profil öffnen"</string> <string name="user_add_user" msgid="4336657383006913022">"Nutzer hinzufügen"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Neuer Nutzer"</string> - <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Gast entfernen?"</string> + <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Alle Apps und Daten in dieser Sitzung werden gelöscht."</string> - <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Entfernen"</string> + <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Willkommen zurück im Gastmodus"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Möchtest du deine Sitzung fortsetzen?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Neu starten"</string> diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml index 40e229426d2b..bec78c0e57d6 100644 --- a/packages/SystemUI/res/values-el/strings.xml +++ b/packages/SystemUI/res/values-el/strings.xml @@ -353,6 +353,8 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"Χρήστης"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Νέος χρήστης"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> + <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> + <skip /> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Μη συνδεδεμένο"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Κανένα δίκτυο"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi ανενεργό"</string> @@ -454,9 +456,11 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Εμφάνιση προφίλ"</string> <string name="user_add_user" msgid="4336657383006913022">"Προσθήκη χρήστη"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Νέος χρήστης"</string> - <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Κατάργηση επισκέπτη;"</string> + <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Όλες οι εφαρμογές και τα δεδομένα αυτής της περιόδου σύνδεσης θα διαγραφούν."</string> - <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Κατάργηση"</string> + <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Επισκέπτη , καλώς όρισες ξανά!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Θέλετε να συνεχίσετε την περίοδο σύνδεσής σας;"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Έναρξη από την αρχή"</string> diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml index ca81ed6e7e17..21cac18abeb5 100644 --- a/packages/SystemUI/res/values-en-rAU/strings.xml +++ b/packages/SystemUI/res/values-en-rAU/strings.xml @@ -353,6 +353,8 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"User"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"New user"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> + <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> + <skip /> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Not Connected"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"No Network"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi Off"</string> @@ -454,9 +456,9 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Show profile"</string> <string name="user_add_user" msgid="4336657383006913022">"Add user"</string> <string name="user_new_user_name" msgid="2019166282704195789">"New user"</string> - <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Remove guest?"</string> + <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"End Guest session?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"All apps and data in this session will be deleted."</string> - <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Remove"</string> + <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"End session"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Welcome back, guest!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Do you want to continue your session?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Start again"</string> diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml index 89537e905720..7ac4c0df0ca1 100644 --- a/packages/SystemUI/res/values-en-rCA/strings.xml +++ b/packages/SystemUI/res/values-en-rCA/strings.xml @@ -353,6 +353,8 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"User"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"New user"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> + <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> + <skip /> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Not Connected"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"No Network"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi Off"</string> @@ -454,9 +456,9 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Show profile"</string> <string name="user_add_user" msgid="4336657383006913022">"Add user"</string> <string name="user_new_user_name" msgid="2019166282704195789">"New user"</string> - <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Remove guest?"</string> + <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"End Guest session?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"All apps and data in this session will be deleted."</string> - <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Remove"</string> + <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"End session"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Welcome back, guest!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Do you want to continue your session?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Start again"</string> diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml index ca81ed6e7e17..21cac18abeb5 100644 --- a/packages/SystemUI/res/values-en-rGB/strings.xml +++ b/packages/SystemUI/res/values-en-rGB/strings.xml @@ -353,6 +353,8 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"User"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"New user"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> + <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> + <skip /> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Not Connected"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"No Network"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi Off"</string> @@ -454,9 +456,9 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Show profile"</string> <string name="user_add_user" msgid="4336657383006913022">"Add user"</string> <string name="user_new_user_name" msgid="2019166282704195789">"New user"</string> - <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Remove guest?"</string> + <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"End Guest session?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"All apps and data in this session will be deleted."</string> - <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Remove"</string> + <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"End session"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Welcome back, guest!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Do you want to continue your session?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Start again"</string> diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml index ca81ed6e7e17..21cac18abeb5 100644 --- a/packages/SystemUI/res/values-en-rIN/strings.xml +++ b/packages/SystemUI/res/values-en-rIN/strings.xml @@ -353,6 +353,8 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"User"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"New user"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> + <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> + <skip /> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Not Connected"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"No Network"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi Off"</string> @@ -454,9 +456,9 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Show profile"</string> <string name="user_add_user" msgid="4336657383006913022">"Add user"</string> <string name="user_new_user_name" msgid="2019166282704195789">"New user"</string> - <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Remove guest?"</string> + <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"End Guest session?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"All apps and data in this session will be deleted."</string> - <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Remove"</string> + <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"End session"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Welcome back, guest!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Do you want to continue your session?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Start again"</string> diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml index 6fa2171b9041..2f30c412a159 100644 --- a/packages/SystemUI/res/values-en-rXC/strings.xml +++ b/packages/SystemUI/res/values-en-rXC/strings.xml @@ -353,6 +353,8 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"User"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"New user"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> + <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> + <skip /> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Not Connected"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"No Network"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi Off"</string> @@ -454,9 +456,9 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Show profile"</string> <string name="user_add_user" msgid="4336657383006913022">"Add user"</string> <string name="user_new_user_name" msgid="2019166282704195789">"New user"</string> - <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Remove guest?"</string> + <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"End guest session?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"All apps and data in this session will be deleted."</string> - <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Remove"</string> + <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"End session"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Welcome back, guest!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Do you want to continue your session?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Start over"</string> diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml index e9a27854622a..7ef9cdb96df7 100644 --- a/packages/SystemUI/res/values-es-rUS/strings.xml +++ b/packages/SystemUI/res/values-es-rUS/strings.xml @@ -353,6 +353,8 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"Usuario"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Usuario nuevo"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> + <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> + <skip /> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Sin conexión"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Sin red"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi desactivada"</string> @@ -454,9 +456,9 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Mostrar perfil"</string> <string name="user_add_user" msgid="4336657383006913022">"Agregar usuario"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Usuario nuevo"</string> - <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"¿Eliminar invitado?"</string> + <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"¿Quieres finalizar la sesión de invitado?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Se eliminarán las aplicaciones y los datos de esta sesión."</string> - <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Eliminar"</string> + <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Finalizar sesión"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Bienvenido nuevamente, invitado."</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"¿Quieres retomar la sesión?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Volver a empezar"</string> diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml index 11ff9af1f7e7..6c6b511e2fe4 100644 --- a/packages/SystemUI/res/values-es/strings.xml +++ b/packages/SystemUI/res/values-es/strings.xml @@ -85,8 +85,7 @@ <string name="screenshot_failed_title" msgid="3259148215671936891">"No se ha podido guardar la captura de pantalla"</string> <string name="screenshot_failed_to_save_user_locked_text" msgid="6156607948256936920">"El dispositivo debe desbloquearse para que se pueda guardar la captura de pantalla"</string> <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Vuelve a intentar hacer la captura de pantalla"</string> - <!-- no translation found for screenshot_failed_to_save_text (7232739948999195960) --> - <skip /> + <string name="screenshot_failed_to_save_text" msgid="7232739948999195960">"No se puede guardar la captura de pantalla"</string> <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"La aplicación o tu organización no permiten realizar capturas de pantalla"</string> <string name="screenshot_edit_label" msgid="8754981973544133050">"Editar"</string> <string name="screenshot_edit_description" msgid="3333092254706788906">"Editar captura de pantalla"</string> @@ -354,6 +353,8 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"Usuario"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Nuevo usuario"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> + <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> + <skip /> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"No conectado"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"No hay red."</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi desactivado"</string> @@ -455,9 +456,9 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Mostrar perfil"</string> <string name="user_add_user" msgid="4336657383006913022">"Añadir usuario"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Nuevo usuario"</string> - <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"¿Quitar invitado?"</string> + <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"¿Finalizar sesión de invitado?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Se eliminarán todas las aplicaciones y datos de esta sesión."</string> - <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Quitar"</string> + <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Finalizar sesión"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Hola de nuevo, invitado"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"¿Quieres continuar con la sesión?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Volver a empezar"</string> diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml index 27a9df054c4f..c64a07e3b61b 100644 --- a/packages/SystemUI/res/values-et/strings.xml +++ b/packages/SystemUI/res/values-et/strings.xml @@ -353,6 +353,8 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"Kasutaja"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Uus kasutaja"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"WiFi"</string> + <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> + <skip /> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Ühendus puudub"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Võrku pole"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"WiFi-ühendus on väljas"</string> @@ -454,9 +456,11 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Kuva profiil"</string> <string name="user_add_user" msgid="4336657383006913022">"Lisa kasutaja"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Uus kasutaja"</string> - <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Kas eemaldada külaline?"</string> + <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Seansi kõik rakendused ja andmed kustutatakse."</string> - <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Eemalda"</string> + <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Tere tulemast tagasi, külaline!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Kas soovite seansiga jätkata?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Alusta uuesti"</string> diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml index b2dc3d6361d2..5047cf17fefd 100644 --- a/packages/SystemUI/res/values-eu/strings.xml +++ b/packages/SystemUI/res/values-eu/strings.xml @@ -353,6 +353,8 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"Erabiltzailea"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Erabiltzaile berria"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wifia"</string> + <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> + <skip /> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Konektatu gabe"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Ez dago sarerik"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi konexioa desaktibatuta"</string> @@ -454,9 +456,9 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Erakutsi profila"</string> <string name="user_add_user" msgid="4336657383006913022">"Gehitu erabiltzailea"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Erabiltzaile berria"</string> - <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Gonbidatua kendu nahi duzu?"</string> + <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Gonbidatuentzako saioa amaitu nahi duzu?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Saioko aplikazio eta datu guztiak ezabatuko dira."</string> - <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Kendu"</string> + <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Amaitu saioa"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Ongi etorri berriro, gonbidatu hori!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Saioarekin jarraitu nahi duzu?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Hasi berriro"</string> diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml index 214f5c2c616c..34c6bf66f64c 100644 --- a/packages/SystemUI/res/values-fa/strings.xml +++ b/packages/SystemUI/res/values-fa/strings.xml @@ -353,6 +353,8 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"کاربر"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"کاربر جدید"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> + <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> + <skip /> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"متصل نیست"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"شبکهای موجود نیست"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi خاموش است"</string> @@ -454,9 +456,11 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"نمایش نمایه"</string> <string name="user_add_user" msgid="4336657383006913022">"افزودن کاربر"</string> <string name="user_new_user_name" msgid="2019166282704195789">"کاربر جدید"</string> - <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"مهمان حذف شود؟"</string> + <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"همه برنامهها و دادههای این جلسه حذف خواهد شد."</string> - <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"حذف"</string> + <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"مهمان گرامی، بازگشتتان را خوش آمد میگوییم!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"آیا میخواهید جلسهتان را ادامه دهید؟"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"شروع مجدد"</string> diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml index cb60ff4c4ebe..5486e39133d8 100644 --- a/packages/SystemUI/res/values-fi/strings.xml +++ b/packages/SystemUI/res/values-fi/strings.xml @@ -353,6 +353,8 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"Käyttäjä"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Uusi käyttäjä"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> + <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> + <skip /> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Ei yhteyttä"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Ei verkkoa"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi-yhteys pois käytöstä"</string> @@ -454,9 +456,11 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Näytä profiili"</string> <string name="user_add_user" msgid="4336657383006913022">"Lisää käyttäjä"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Uusi käyttäjä"</string> - <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Poistetaaanko vieras?"</string> + <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Kaikki sovellukset ja tämän istunnon tiedot poistetaan."</string> - <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Poista"</string> + <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Tervetuloa takaisin!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Haluatko jatkaa istuntoa?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Aloita alusta"</string> diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml index 17752c5d2c8a..63f65e08e189 100644 --- a/packages/SystemUI/res/values-fr-rCA/strings.xml +++ b/packages/SystemUI/res/values-fr-rCA/strings.xml @@ -353,6 +353,8 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"Utilisateur"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Nouvel utilisateur"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> + <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> + <skip /> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Non connecté"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Aucun réseau"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi désactivé"</string> @@ -454,9 +456,11 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Afficher le profil"</string> <string name="user_add_user" msgid="4336657383006913022">"Ajouter un utilisateur"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Nouvel utilisateur"</string> - <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Supprimer l\'invité?"</string> + <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Toutes les applications et les données de cette session seront supprimées."</string> - <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Supprimer"</string> + <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Bienvenue à nouveau dans la session Invité"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Voulez-vous poursuivre la session?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Recommencer"</string> diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml index 39d82ef44c6a..ebf199bfae90 100644 --- a/packages/SystemUI/res/values-fr/strings.xml +++ b/packages/SystemUI/res/values-fr/strings.xml @@ -353,6 +353,8 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"Utilisateur"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Nouvel utilisateur"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> + <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> + <skip /> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Non connecté"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Aucun réseau"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi désactivé"</string> @@ -454,9 +456,11 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Afficher le profil"</string> <string name="user_add_user" msgid="4336657383006913022">"Ajouter un utilisateur"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Nouvel utilisateur"</string> - <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Supprimer l\'invité ?"</string> + <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Toutes les applications et les données de cette session seront supprimées."</string> - <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Supprimer"</string> + <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Bienvenue à nouveau dans la session Invité"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Voulez-vous poursuivre la dernière session ?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Non, nouvelle session"</string> diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml index 6494e0afc5e8..36f7f4cd9fb7 100644 --- a/packages/SystemUI/res/values-gl/strings.xml +++ b/packages/SystemUI/res/values-gl/strings.xml @@ -353,6 +353,8 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"Usuario"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Novo usuario"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wifi"</string> + <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> + <skip /> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Non conectada"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Non hai rede"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wifi desactivada"</string> @@ -454,9 +456,11 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Mostrar perfil"</string> <string name="user_add_user" msgid="4336657383006913022">"Engadir usuario"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Novo usuario"</string> - <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Queres eliminar o invitado?"</string> + <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Eliminaranse todas as aplicacións e datos desta sesión."</string> - <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Eliminar"</string> + <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Benvido de novo, convidado."</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Queres continuar coa túa sesión?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Comezar de novo"</string> diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml index 369fd1d51f14..66423e46c4e5 100644 --- a/packages/SystemUI/res/values-gu/strings.xml +++ b/packages/SystemUI/res/values-gu/strings.xml @@ -353,6 +353,8 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"વપરાશકર્તા"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"નવો વપરાશકર્તા"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"વાઇ-ફાઇ"</string> + <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> + <skip /> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"કનેક્ટ થયેલ નથી"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"કોઈ નેટવર્ક નથી"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"વાઇ-ફાઇ બંધ"</string> @@ -454,9 +456,11 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"પ્રોફાઇલ બતાવો"</string> <string name="user_add_user" msgid="4336657383006913022">"વપરાશકર્તા ઉમેરો"</string> <string name="user_new_user_name" msgid="2019166282704195789">"નવો વપરાશકર્તા"</string> - <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"અતિથિ દૂર કરીએ?"</string> + <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"આ સત્રમાંની તમામ ઍપ્લિકેશનો અને ડેટા કાઢી નાખવામાં આવશે."</string> - <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"દૂર કરો"</string> + <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"ફરી સ્વાગત છે, અતિથિ!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"શું તમે તમારું સત્ર ચાલુ કરવા માંગો છો?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"શરૂ કરો"</string> diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml index 0b16f527e46a..65cba2f9dc09 100644 --- a/packages/SystemUI/res/values-hi/strings.xml +++ b/packages/SystemUI/res/values-hi/strings.xml @@ -85,7 +85,7 @@ <string name="screenshot_failed_title" msgid="3259148215671936891">"स्क्रीनशॉट सेव नहीं किया जा सका"</string> <string name="screenshot_failed_to_save_user_locked_text" msgid="6156607948256936920">"स्क्रीनशॉट सेव करने के लिए डिवाइस का अनलॉक होना ज़रूरी है"</string> <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"स्क्रीनशॉट दोबारा लेने की कोशिश करें"</string> - <string name="screenshot_failed_to_save_text" msgid="7232739948999195960">"स्क्रीनशॉट को सेव नहीं किया जा सका"</string> + <string name="screenshot_failed_to_save_text" msgid="7232739948999195960">"स्क्रीनशॉट को सेव नहीं किया जा सकता"</string> <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"ऐप्लिकेशन या आपका संगठन स्क्रीनशॉट लेने की अनुमति नहीं देता"</string> <string name="screenshot_edit_label" msgid="8754981973544133050">"बदलाव करें"</string> <string name="screenshot_edit_description" msgid="3333092254706788906">"स्क्रीनशॉट में बदलाव करें"</string> @@ -355,6 +355,8 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"उपयोगकर्ता"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"नया उपयोगकर्ता"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"वाई-फ़ाई"</string> + <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> + <skip /> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"कनेक्ट नहीं है"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"कोई नेटवर्क नहीं"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"वाई-फ़ाई बंद"</string> @@ -456,9 +458,11 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"प्रोफ़ाइल दिखाएं"</string> <string name="user_add_user" msgid="4336657383006913022">"उपयोगकर्ता जोड़ें"</string> <string name="user_new_user_name" msgid="2019166282704195789">"नया उपयोगकर्ता"</string> - <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"अतिथि को निकालें?"</string> + <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"इस सत्र के सभी ऐप्स और डेटा को हटा दिया जाएगा."</string> - <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"निकालें"</string> + <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"अतिथि, आपका फिर से स्वागत है!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"क्या आप अपना सत्र जारी रखना चाहते हैं?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"फिर से शुरू करें"</string> diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml index 2f13421590ae..7d60619e2f02 100644 --- a/packages/SystemUI/res/values-hr/strings.xml +++ b/packages/SystemUI/res/values-hr/strings.xml @@ -354,6 +354,8 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"Korisnik"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Novi korisnik"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> + <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> + <skip /> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Nije povezano"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Nema mreže"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi isključen"</string> @@ -456,9 +458,11 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Prikaz profila"</string> <string name="user_add_user" msgid="4336657383006913022">"Dodavanje korisnika"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Novi korisnik"</string> - <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Ukloniti gosta?"</string> + <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Sve aplikacije i podaci u ovoj sesiji bit će izbrisani."</string> - <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Ukloni"</string> + <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Dobro došli natrag, gostu!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Želite li nastaviti sesiju?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Počni ispočetka"</string> diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml index a7b3cc315980..2ee8913f00d1 100644 --- a/packages/SystemUI/res/values-hu/strings.xml +++ b/packages/SystemUI/res/values-hu/strings.xml @@ -353,6 +353,8 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"Felhasználó"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Új felhasználó"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> + <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> + <skip /> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Nincs kapcsolat"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Nincs hálózat"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi kikapcsolva"</string> @@ -454,9 +456,11 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Profil megjelenítése"</string> <string name="user_add_user" msgid="4336657383006913022">"Felhasználó hozzáadása"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Új felhasználó"</string> - <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Eltávolítja a vendég munkamenetet?"</string> + <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"A munkamenetben található összes alkalmazás és adat törlődni fog."</string> - <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Eltávolítás"</string> + <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Örülünk, hogy visszatért, vendég!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Folytatja a munkamenetet?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Újrakezdés"</string> diff --git a/packages/SystemUI/res/values-hu/strings_tv.xml b/packages/SystemUI/res/values-hu/strings_tv.xml index 78d6099b74a0..91183af142a5 100644 --- a/packages/SystemUI/res/values-hu/strings_tv.xml +++ b/packages/SystemUI/res/values-hu/strings_tv.xml @@ -23,7 +23,7 @@ <string name="app_accessed_mic" msgid="2754428675130470196">"A(z) %1$s hozzáfért a mikrofonhoz"</string> <string name="notification_vpn_connected" msgid="3891023882833274730">"A VPN-kapcsolat létrejött"</string> <string name="notification_vpn_disconnected" msgid="7150747626448044843">"A VPN-kapcsolat megszakadt"</string> - <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"A következő szolgáltatás használatával: <xliff:g id="VPN_APP">%1$s</xliff:g>"</string> + <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"Ezzel: <xliff:g id="VPN_APP">%1$s</xliff:g>"</string> <string name="tv_notification_panel_title" msgid="5311050946506276154">"Értesítések"</string> <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Nincs értesítés"</string> </resources> diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml index fed73da0f120..18af1eaf17e3 100644 --- a/packages/SystemUI/res/values-hy/strings.xml +++ b/packages/SystemUI/res/values-hy/strings.xml @@ -353,6 +353,8 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"Օգտատեր"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Նոր օգտատեր"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> + <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> + <skip /> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Միացված չէ"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Ցանց չկա"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi-ը անջատված է"</string> @@ -454,9 +456,9 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Ցույց տալ դիտարկումը"</string> <string name="user_add_user" msgid="4336657383006913022">"Ավելացնել օգտատեր"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Նոր օգտատեր"</string> - <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Հեռացնե՞լ հյուրին:"</string> + <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Ավարտե՞լ հյուրի աշխատաշրջանը"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Այս աշխատաշրջանի բոլոր ծրագրերն ու տվյալները կջնջվեն:"</string> - <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Հեռացնել"</string> + <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Ավարտել աշխատաշրջանը"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Բարի վերադարձ, հյուր:"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Դուք ցանկանու՞մ եք շարունակել ձեր գործողությունը:"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Սկսել"</string> diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml index 7ae3e5d7edb4..4a048bb638ef 100644 --- a/packages/SystemUI/res/values-in/strings.xml +++ b/packages/SystemUI/res/values-in/strings.xml @@ -353,6 +353,8 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"Pengguna"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Pengguna baru"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> + <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> + <skip /> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Tidak Terhubung"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Tidak Ada Jaringan"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi Mati"</string> @@ -454,9 +456,11 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Tampilkan profil"</string> <string name="user_add_user" msgid="4336657383006913022">"Tambahkan pengguna"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Pengguna baru"</string> - <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Hapus tamu?"</string> + <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Semua aplikasi dan data di sesi ini akan dihapus."</string> - <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Hapus"</string> + <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Selamat datang kembali, tamu!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Lanjutkan sesi Anda?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Mulai ulang"</string> diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml index 0361ba4db718..10e11fc64d7b 100644 --- a/packages/SystemUI/res/values-is/strings.xml +++ b/packages/SystemUI/res/values-is/strings.xml @@ -353,6 +353,8 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"Notandi"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Nýr notandi"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> + <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> + <skip /> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Engin tenging"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Ekkert net"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Slökkt á Wi-Fi"</string> @@ -454,9 +456,11 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Sýna snið"</string> <string name="user_add_user" msgid="4336657383006913022">"Bæta notanda við"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Nýr notandi"</string> - <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Fjarlægja gest?"</string> + <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Öllum forritum og gögnum í þessari lotu verður eytt."</string> - <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Fjarlægja"</string> + <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Velkominn aftur, gestur!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Viltu halda áfram með lotuna?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Byrja upp á nýtt"</string> diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml index 948faf1ccf72..24e3f474f1fe 100644 --- a/packages/SystemUI/res/values-it/strings.xml +++ b/packages/SystemUI/res/values-it/strings.xml @@ -353,6 +353,8 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"Utente"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Nuovo utente"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> + <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> + <skip /> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Non connessa"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Nessuna rete"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi disattivato"</string> @@ -454,9 +456,11 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Mostra profilo"</string> <string name="user_add_user" msgid="4336657383006913022">"Aggiungi utente"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Nuovo utente"</string> - <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Rimuovere l\'ospite?"</string> + <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Tutte le app e i dati di questa sessione verranno eliminati."</string> - <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Rimuovi"</string> + <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Bentornato, ospite."</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Vuoi continuare la sessione?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Ricomincia"</string> diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml index d20623e0efa1..76dbdf99ca50 100644 --- a/packages/SystemUI/res/values-iw/strings.xml +++ b/packages/SystemUI/res/values-iw/strings.xml @@ -35,7 +35,7 @@ <string name="battery_low_why" msgid="2056750982959359863">"הגדרות"</string> <string name="battery_saver_confirmation_title" msgid="1234998463717398453">"להפעיל את תכונת החיסכון בסוללה?"</string> <string name="battery_saver_confirmation_title_generic" msgid="2299231884234959849">"מידע על מצב החיסכון בסוללה"</string> - <string name="battery_saver_confirmation_ok" msgid="5042136476802816494">"הפעל"</string> + <string name="battery_saver_confirmation_ok" msgid="5042136476802816494">"הפעלה"</string> <string name="battery_saver_start_action" msgid="4553256017945469937">"הפעלת תכונת החיסכון בסוללה"</string> <string name="status_bar_settings_settings_button" msgid="534331565185171556">"הגדרות"</string> <string name="status_bar_settings_wifi_button" msgid="7243072479837270946">"Wi-Fi"</string> @@ -278,8 +278,8 @@ <string name="accessibility_quick_settings_flashlight_changed_on" msgid="4747870681508334200">"הפנס הופעל."</string> <string name="accessibility_quick_settings_color_inversion_changed_off" msgid="7548045840282925393">"היפוך צבעים כבוי."</string> <string name="accessibility_quick_settings_color_inversion_changed_on" msgid="4711141858364404084">"היפוך צבעים מופעל."</string> - <string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"נקודה לשיתוף אינטרנט בנייד כבויה."</string> - <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"נקודה לשיתוף אינטרנט בנייד מופעלת."</string> + <string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"נקודת האינטרנט (hotspot) כבויה."</string> + <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"נקודת האינטרנט (hotspot) מופעלת."</string> <string name="accessibility_casting_turned_off" msgid="1387906158563374962">"העברת המסך הופסקה."</string> <string name="accessibility_quick_settings_work_mode_off" msgid="562749867895549696">"מצב עבודה כבוי."</string> <string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"מצב עבודה מופעל."</string> @@ -355,6 +355,8 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"משתמש"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"משתמש חדש"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> + <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> + <skip /> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"אין חיבור"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"אין רשת"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi כבוי"</string> @@ -377,7 +379,7 @@ <string name="quick_settings_connected_battery_level" msgid="1322075669498906959">"מחובר, הסוללה ב-<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string> <string name="quick_settings_connecting" msgid="2381969772953268809">"מתחבר..."</string> <string name="quick_settings_tethering_label" msgid="5257299852322475780">"שיתוף אינטרנט בין ניידים"</string> - <string name="quick_settings_hotspot_label" msgid="1199196300038363424">"נקודה לשיתוף אינטרנט"</string> + <string name="quick_settings_hotspot_label" msgid="1199196300038363424">"נקודת אינטרנט (hotspot)"</string> <string name="quick_settings_hotspot_secondary_label_transient" msgid="7585604088079160564">"ההפעלה מתבצעת…"</string> <string name="quick_settings_hotspot_secondary_label_data_saver_enabled" msgid="1280433136266439372">"חוסך הנתונים פועל"</string> <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="3142308865165871976"> @@ -458,9 +460,11 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"הצג פרופיל"</string> <string name="user_add_user" msgid="4336657383006913022">"הוספת משתמש"</string> <string name="user_new_user_name" msgid="2019166282704195789">"משתמש חדש"</string> - <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"להסיר אורח?"</string> + <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"כל האפליקציות והנתונים בפעילות זו באתר יימחקו."</string> - <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"הסר"</string> + <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"שמחים לראותך שוב!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"האם ברצונך להמשיך בפעילות באתר?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"ברצוני להתחיל מחדש"</string> @@ -649,7 +653,7 @@ <string name="alarm_template" msgid="2234991538018805736">"בשעה <xliff:g id="WHEN">%1$s</xliff:g>"</string> <string name="alarm_template_far" msgid="3561752195856839456">"ב-<xliff:g id="WHEN">%1$s</xliff:g>"</string> <string name="accessibility_quick_settings_detail" msgid="544463655956179791">"הגדרות מהירות, <xliff:g id="TITLE">%s</xliff:g>."</string> - <string name="accessibility_status_bar_hotspot" msgid="2888479317489131669">"נקודה לשיתוף אינטרנט"</string> + <string name="accessibility_status_bar_hotspot" msgid="2888479317489131669">"נקודת אינטרנט (hotspot)"</string> <string name="accessibility_managed_profile" msgid="4703836746209377356">"פרופיל עבודה"</string> <string name="tuner_warning_title" msgid="7721976098452135267">"מהנה בשביל חלק מהאנשים, אבל לא בשביל כולם"</string> <string name="tuner_warning" msgid="1861736288458481650">"System UI Tuner מספק לך דרכים נוספות להתאים אישית את ממשק המשתמש של Android. התכונות הניסיוניות האלה עשויות להשתנות, להתקלקל או להיעלם בגרסאות עתידיות. המשך בזהירות."</string> @@ -666,7 +670,7 @@ <string name="experimental" msgid="3549865454812314826">"ניסיוני"</string> <string name="enable_bluetooth_title" msgid="866883307336662596">"האם להפעיל את ה-Bluetooth?"</string> <string name="enable_bluetooth_message" msgid="6740938333772779717">"כדי לחבר את המקלדת לטאבלט, תחילה עליך להפעיל את ה-Bluetooth."</string> - <string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"הפעל"</string> + <string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"הפעלה"</string> <string name="show_silently" msgid="5629369640872236299">"הצגת התראות בלי להשמיע צליל"</string> <string name="block" msgid="188483833983476566">"חסימת כל ההתראות"</string> <string name="do_not_silence" msgid="4982217934250511227">"לא להשתיק"</string> diff --git a/packages/SystemUI/res/values-iw/strings_tv.xml b/packages/SystemUI/res/values-iw/strings_tv.xml index 7483116177f6..1258f0d42aa0 100644 --- a/packages/SystemUI/res/values-iw/strings_tv.xml +++ b/packages/SystemUI/res/values-iw/strings_tv.xml @@ -21,8 +21,8 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="mic_active" msgid="5766614241012047024">"המיקרופון פעיל"</string> <string name="app_accessed_mic" msgid="2754428675130470196">"%1$s קיבלה גישה למיקרופון שלך"</string> - <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN מחובר"</string> - <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN מנותק"</string> + <string name="notification_vpn_connected" msgid="3891023882833274730">"ה-VPN מחובר"</string> + <string name="notification_vpn_disconnected" msgid="7150747626448044843">"ה-VPN מנותק"</string> <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"באמצעות <xliff:g id="VPN_APP">%1$s</xliff:g>"</string> <string name="tv_notification_panel_title" msgid="5311050946506276154">"התראות"</string> <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"אין התראות"</string> diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml index 3bb841d21694..b4edfe5c38dc 100644 --- a/packages/SystemUI/res/values-ja/strings.xml +++ b/packages/SystemUI/res/values-ja/strings.xml @@ -353,6 +353,8 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"ユーザー"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"新しいユーザー"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> + <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> + <skip /> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"接続されていません"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"ネットワークなし"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi OFF"</string> @@ -454,9 +456,11 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"プロファイルを表示"</string> <string name="user_add_user" msgid="4336657383006913022">"ユーザーを追加"</string> <string name="user_new_user_name" msgid="2019166282704195789">"新しいユーザー"</string> - <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"ゲストを削除しますか?"</string> + <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"このセッションでのアプリとデータはすべて削除されます。"</string> - <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"削除"</string> + <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"おかえりなさい、ゲストさん"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"セッションを続行しますか?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"最初から開始"</string> diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml index 3e3405e23706..26bde65e0ba3 100644 --- a/packages/SystemUI/res/values-ka/strings.xml +++ b/packages/SystemUI/res/values-ka/strings.xml @@ -353,6 +353,8 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"მომხმარებელი"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"ახალი მომხმარებელი"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> + <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> + <skip /> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"არ არის დაკავშირებული."</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"ქსელი არ არის"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi გამორთულია"</string> @@ -454,9 +456,11 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"პროფილის ჩვენება"</string> <string name="user_add_user" msgid="4336657383006913022">"მომხმარებლის დამატება"</string> <string name="user_new_user_name" msgid="2019166282704195789">"ახალი მომხმარებელი"</string> - <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"სტუმრის ამოშლა?"</string> + <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ამ სესიის ყველა აპი და მონაცემი წაიშლება."</string> - <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"ამოშლა"</string> + <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"სტუმარო, გვიხარია, რომ დაბრუნდით!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"გსურთ, თქვენი სესიის გაგრძელება?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"ხელახლა დაწყება"</string> diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml index 7cbb40b1ad34..9ce0a29649dc 100644 --- a/packages/SystemUI/res/values-kk/strings.xml +++ b/packages/SystemUI/res/values-kk/strings.xml @@ -353,6 +353,8 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"Пайдаланушы"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Жаңа пайдаланушы"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> + <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> + <skip /> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Жалғанбаған"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Желі жоқ"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi өшірулі"</string> @@ -454,9 +456,11 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Профильді көрсету"</string> <string name="user_add_user" msgid="4336657383006913022">"Пайдаланушы қосу"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Жаңа пайдаланушы"</string> - <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Қонақты жою керек пе?"</string> + <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Осы сеанстағы барлық қолданбалар мен деректер жойылады."</string> - <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Алып тастау"</string> + <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Қош келдіңіз, қонақ"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Сеансты жалғастыру керек пе?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Қайта бастау"</string> diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml index e15cc9218269..1e6c7fa6ebea 100644 --- a/packages/SystemUI/res/values-km/strings.xml +++ b/packages/SystemUI/res/values-km/strings.xml @@ -353,6 +353,8 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"អ្នកប្រើ"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"អ្នកប្រើថ្មី"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> + <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> + <skip /> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"មិនបានតភ្ជាប់"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"គ្មានបណ្ដាញ"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"វ៉ាយហ្វាយបានបិទ"</string> @@ -454,9 +456,11 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"បង្ហាញប្រវត្តិរូប"</string> <string name="user_add_user" msgid="4336657383006913022">"បន្ថែមអ្នកប្រើ"</string> <string name="user_new_user_name" msgid="2019166282704195789">"អ្នកប្រើថ្មី"</string> - <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"លុបភ្ញៀវ?"</string> + <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ទិន្នន័យ និងកម្មវិធីទាំងអស់ក្នុងសម័យនេះនឹងត្រូវបានលុប។"</string> - <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"លុបចេញ"</string> + <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"សូមស្វាគមន៍ការត្រឡប់មកវិញ, ភ្ញៀវ!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"តើអ្នកចង់បន្តសម័យរបស់អ្នក?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"ចាប់ផ្ដើម"</string> diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml index 177db59697f5..2958b2947de0 100644 --- a/packages/SystemUI/res/values-kn/strings.xml +++ b/packages/SystemUI/res/values-kn/strings.xml @@ -353,6 +353,8 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"ಬಳಕೆದಾರ"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"ಹೊಸ ಬಳಕೆದಾರರು"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"ವೈ-ಫೈ"</string> + <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> + <skip /> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"ಸಂಪರ್ಕಗೊಂಡಿಲ್ಲ"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"ನೆಟ್ವರ್ಕ್ ಇಲ್ಲ"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"ವೈ-ಫೈ ಆಫ್"</string> @@ -454,9 +456,11 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"ಪ್ರೊಫೈಲ್ ತೋರಿಸು"</string> <string name="user_add_user" msgid="4336657383006913022">"ಬಳಕೆದಾರರನ್ನು ಸೇರಿಸಿ"</string> <string name="user_new_user_name" msgid="2019166282704195789">"ಹೊಸ ಬಳಕೆದಾರರು"</string> - <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"ಅತಿಥಿಯನ್ನು ತೆಗೆದುಹಾಕುವುದೇ?"</string> + <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ಈ ಸೆಷನ್ನಲ್ಲಿನ ಎಲ್ಲ ಅಪ್ಲಿಕೇಶನ್ಗಳು ಮತ್ತು ಡೇಟಾವನ್ನು ಅಳಿಸಲಾಗುತ್ತದೆ."</string> - <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"ತೆಗೆದುಹಾಕಿ"</string> + <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"ಮತ್ತೆ ಸುಸ್ವಾಗತ, ಅತಿಥಿ!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"ನಿಮ್ಮ ಸೆಷನ್ ಮುಂದುವರಿಸಲು ಇಚ್ಚಿಸುವಿರಾ?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"ಪ್ರಾರಂಭಿಸಿ"</string> diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml index eb81396257a6..cd613b647e38 100644 --- a/packages/SystemUI/res/values-ko/strings.xml +++ b/packages/SystemUI/res/values-ko/strings.xml @@ -353,6 +353,8 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"사용자"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"신규 사용자"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> + <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> + <skip /> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"연결되어 있지 않음"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"네트워크가 연결되지 않음"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi 꺼짐"</string> @@ -454,9 +456,11 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"프로필 표시"</string> <string name="user_add_user" msgid="4336657383006913022">"사용자 추가"</string> <string name="user_new_user_name" msgid="2019166282704195789">"신규 사용자"</string> - <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"게스트를 삭제하시겠습니까?"</string> + <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"이 세션에 있는 모든 앱과 데이터가 삭제됩니다."</string> - <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"삭제"</string> + <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"게스트 세션 다시 시작"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"세션을 계속 진행하시겠습니까?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"다시 시작"</string> diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml index caa2b31647c1..e6053411b74a 100644 --- a/packages/SystemUI/res/values-ky/strings.xml +++ b/packages/SystemUI/res/values-ky/strings.xml @@ -355,6 +355,8 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"Колдонуучу"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Жаңы колдонуучу"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> + <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> + <skip /> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Байланышкан жок"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Желе жок"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi өчүк"</string> @@ -456,9 +458,11 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Профилди көрсөтүү"</string> <string name="user_add_user" msgid="4336657383006913022">"Колдонуучу кошуу"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Жаңы колдонуучу"</string> - <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Конокту алып саласызбы?"</string> + <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Бул сеанстагы бардык колдонмолор жана дайындар өчүрүлөт."</string> - <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Алып салуу"</string> + <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Кайтып келишиңиз менен, конок!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Сеансыңызды улантасызбы?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Кайра баштоо"</string> diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml index 5ed1d4b0416f..b8cc3ded31e8 100644 --- a/packages/SystemUI/res/values-lo/strings.xml +++ b/packages/SystemUI/res/values-lo/strings.xml @@ -353,6 +353,8 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"ຜູ້ໃຊ້"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"ຜູ່ໃຊ້ໃໝ່"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> + <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> + <skip /> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"ບໍ່ໄດ້ເຊື່ອມຕໍ່"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"ບໍ່ມີເຄືອຂ່າຍ"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi ປິດ"</string> @@ -454,9 +456,11 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"ສະແດງໂປຣໄຟລ໌"</string> <string name="user_add_user" msgid="4336657383006913022">"ເພີ່ມຜູ້ໃຊ້"</string> <string name="user_new_user_name" msgid="2019166282704195789">"ຜູ່ໃຊ້ໃໝ່"</string> - <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"ລຶບແຂກບໍ?"</string> + <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ແອັບຯແລະຂໍ້ມູນທັງໝົດໃນເຊດຊັນນີ້ຈະຖືກລຶບອອກ."</string> - <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"ລຶບ"</string> + <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"ຍິນດີຕ້ອນຮັບກັບມາ, ຜູ່ຢ້ຽມຢາມ!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"ທ່ານຕ້ອງການສືບຕໍ່ເຊດຊັນຂອງທ່ານບໍ່?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"ເລີ່ມຕົ້ນໃຫມ່"</string> diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml index 17d517cda3de..8a32436f2c0c 100644 --- a/packages/SystemUI/res/values-lt/strings.xml +++ b/packages/SystemUI/res/values-lt/strings.xml @@ -355,6 +355,8 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"Naudotojas"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Naujas naudotojas"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> + <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> + <skip /> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Neprisijungta"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Tinklo nėra"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"„Wi-Fi“ išjungta"</string> @@ -458,9 +460,11 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Rodyti profilį"</string> <string name="user_add_user" msgid="4336657383006913022">"Pridėti naudotoją"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Naujas naudotojas"</string> - <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Pašalinti svečią?"</string> + <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Bus ištrintos visos šios sesijos programos ir duomenys."</string> - <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Pašalinti"</string> + <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Sveiki sugrįžę, svety!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Ar norite tęsti sesiją?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Pradėti iš naujo"</string> diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml index 6fd32b59f175..56ce376dd147 100644 --- a/packages/SystemUI/res/values-lv/strings.xml +++ b/packages/SystemUI/res/values-lv/strings.xml @@ -354,6 +354,8 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"Lietotājs"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Jauns lietotājs"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> + <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> + <skip /> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Nav izveidots savienojums"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Nav tīkla"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi ir izslēgts"</string> @@ -456,9 +458,11 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Parādīt profilu"</string> <string name="user_add_user" msgid="4336657383006913022">"Lietotāja pievienošana"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Jauns lietotājs"</string> - <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Vai noņemt viesi?"</string> + <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Tiks dzēstas visas šīs sesijas lietotnes un dati."</string> - <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Noņemt"</string> + <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Laipni lūdzam atpakaļ, viesi!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Vai vēlaties turpināt savu sesiju?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Sākt no sākuma"</string> diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml index 51f9b8c4d45a..fc83bb7ee46d 100644 --- a/packages/SystemUI/res/values-mk/strings.xml +++ b/packages/SystemUI/res/values-mk/strings.xml @@ -353,6 +353,8 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"Корисник"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Нов корисник"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> + <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> + <skip /> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Не е поврзано"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Нема мрежа"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi е исклучено"</string> @@ -454,9 +456,11 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Прикажи го профилот"</string> <string name="user_add_user" msgid="4336657383006913022">"Додај корисник"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Нов корисник"</string> - <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Да се отстрани гостинот?"</string> + <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Сите апликации и податоци во сесијата ќе се избришат."</string> - <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Отстрани"</string> + <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Добре дојде пак, гостине!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Дали сакате да продолжите со сесијата?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Почни одново"</string> diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml index 534faa719334..9cd041236c36 100644 --- a/packages/SystemUI/res/values-ml/strings.xml +++ b/packages/SystemUI/res/values-ml/strings.xml @@ -353,6 +353,8 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"ഉപയോക്താവ്"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"പുതിയ ഉപയോക്താവ്"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"വൈഫൈ"</string> + <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> + <skip /> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"കണക്റ്റ് ചെയ്തിട്ടില്ല"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"നെറ്റ്വർക്ക് ഒന്നുമില്ല"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"വൈഫൈ ഓഫുചെയ്യുക"</string> @@ -454,9 +456,11 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"പ്രൊഫൈൽ കാണിക്കുക"</string> <string name="user_add_user" msgid="4336657383006913022">"ഉപയോക്താവിനെ ചേര്ക്കുക"</string> <string name="user_new_user_name" msgid="2019166282704195789">"പുതിയ ഉപയോക്താവ്"</string> - <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"അതിഥിയെ നീക്കംചെയ്യണോ?"</string> + <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ഈ സെഷനിലെ എല്ലാ അപ്ലിക്കേഷനുകളും ഡാറ്റയും ഇല്ലാതാക്കും."</string> - <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"നീക്കംചെയ്യുക"</string> + <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"അതിഥിയ്ക്ക് വീണ്ടും സ്വാഗതം!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"നിങ്ങളുടെ സെഷൻ തുടരണോ?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"പുനരാംരംഭിക്കുക"</string> diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml index 190e3e503798..b0159b027611 100644 --- a/packages/SystemUI/res/values-mn/strings.xml +++ b/packages/SystemUI/res/values-mn/strings.xml @@ -353,6 +353,8 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"Хэрэглэгч"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Шинэ хэрэглэгч"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> + <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> + <skip /> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Холбогдоогүй"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Сүлжээгүй"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi унтарсан"</string> @@ -454,9 +456,11 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Профайлыг харуулах"</string> <string name="user_add_user" msgid="4336657383006913022">"Хэрэглэгч нэмэх"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Шинэ хэрэглэгч"</string> - <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Зочныг хасах уу?"</string> + <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Энэ сешний бүх апп болон дата устах болно."</string> - <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Хасах"</string> + <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Тавтай морилно уу!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Та үргэлжлүүлэхийг хүсэж байна уу?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Дахин эхлүүлэх"</string> diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml index 1f2e82e1c429..965b8958b458 100644 --- a/packages/SystemUI/res/values-mr/strings.xml +++ b/packages/SystemUI/res/values-mr/strings.xml @@ -85,8 +85,7 @@ <string name="screenshot_failed_title" msgid="3259148215671936891">"स्क्रीनशॉट सेव्ह करू शकलो नाही"</string> <string name="screenshot_failed_to_save_user_locked_text" msgid="6156607948256936920">"स्क्रीनशॉट सेव्ह करण्याआधी डिव्हाइस अनलॉक करणे आवश्यक आहे"</string> <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"स्क्रीनशॉट पुन्हा घेण्याचा प्रयत्न करा"</string> - <!-- no translation found for screenshot_failed_to_save_text (7232739948999195960) --> - <skip /> + <string name="screenshot_failed_to_save_text" msgid="7232739948999195960">"स्क्रीनशॉट सेव्ह करू शकत नाही"</string> <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"अॅप किंवा आपल्या संस्थेद्वारे स्क्रीनशॉट घेण्याची अनुमती नाही"</string> <string name="screenshot_edit_label" msgid="8754981973544133050">"संपादित करा"</string> <string name="screenshot_edit_description" msgid="3333092254706788906">"स्क्रीनशॉट संपादित करा"</string> @@ -354,6 +353,8 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"वापरकर्ता"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"नवीन वापरकर्ता"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"वाय-फाय"</string> + <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> + <skip /> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"कनेक्ट केले नाही"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"नेटवर्क नाही"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"वाय-फाय बंद"</string> @@ -455,9 +456,11 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"प्रोफाईल दर्शवा"</string> <string name="user_add_user" msgid="4336657383006913022">"वापरकर्ता जोडा"</string> <string name="user_new_user_name" msgid="2019166282704195789">"नवीन वापरकर्ता"</string> - <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"अतिथी काढायचे?"</string> + <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"या सत्रातील सर्व अॅप्स आणि डेटा हटवला जाईल."</string> - <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"काढा"</string> + <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"अतिथी, तुमचे पुन्हा स्वागत आहे!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"तुम्ही तुमचे सत्र सुरू ठेवू इच्छिता?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"येथून सुरू करा"</string> diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml index 17fe32d3b2d5..b9b525ead63c 100644 --- a/packages/SystemUI/res/values-ms/strings.xml +++ b/packages/SystemUI/res/values-ms/strings.xml @@ -353,6 +353,8 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"Pengguna"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Pengguna baharu"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> + <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> + <skip /> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Tidak Disambungkan"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Tiada Rangkaian"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi Dimatikan"</string> @@ -454,9 +456,11 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Tunjuk profil"</string> <string name="user_add_user" msgid="4336657383006913022">"Tambah pengguna"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Pengguna baharu"</string> - <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Alih keluar tetamu?"</string> + <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Semua apl dan data dalam sesi ini akan dipadam."</string> - <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Alih keluar"</string> + <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Selamat kembali, tetamu!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Adakah anda ingin meneruskan sesi anda?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Mulakan semula"</string> diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml index 610105eb6b4d..9801ec60c89e 100644 --- a/packages/SystemUI/res/values-my/strings.xml +++ b/packages/SystemUI/res/values-my/strings.xml @@ -353,6 +353,8 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"အသုံးပြုသူ"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"အသုံးပြုသူ အသစ်"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> + <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> + <skip /> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"ချိတ်ဆက်မထားပါ"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"ကွန်ရက်မရှိပါ"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"ဝိုင်ဖိုင်ပိတ်ရန်"</string> @@ -454,9 +456,11 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"ပရိုဖိုင်ကို ပြရန်"</string> <string name="user_add_user" msgid="4336657383006913022">"အသုံးပြုသူ ထည့်ရန်"</string> <string name="user_new_user_name" msgid="2019166282704195789">"အသုံးပြုသူ အသစ်"</string> - <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"ဧည့်သည်ကို ဖယ်ထုတ်လိုက်ရမလား?"</string> + <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ဒီချိတ်ဆက်မှု ထဲက အက်ပ်များ အားလုံး နှင့် ဒေတာကို ဖျက်ပစ်မည်။"</string> - <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"ဖယ်ထုတ်ပါ"</string> + <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"ပြန်လာတာ ကြိုဆိုပါသည်၊ ဧည့်သည်!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"သင်သည် သင်၏ ချိတ်ဆက်မှုကို ဆက်ပြုလုပ် လိုပါသလား?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"အစမှ ပြန်စပါ"</string> diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml index b16f1e78af00..67242865b649 100644 --- a/packages/SystemUI/res/values-nb/strings.xml +++ b/packages/SystemUI/res/values-nb/strings.xml @@ -353,6 +353,8 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"Bruker"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Ny bruker"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> + <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> + <skip /> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Ikke tilkoblet"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Ingen nettverk"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi er av"</string> @@ -454,9 +456,11 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Vis profil"</string> <string name="user_add_user" msgid="4336657383006913022">"Legg til brukere"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Ny bruker"</string> - <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Vil du fjerne gjesten?"</string> + <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Alle appene og all informasjon i denne økten slettes."</string> - <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Fjern"</string> + <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Velkommen tilbake, gjest!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Vil du fortsette økten?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Start på nytt"</string> diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml index fb4dca094a5c..c104ffdf0dcd 100644 --- a/packages/SystemUI/res/values-ne/strings.xml +++ b/packages/SystemUI/res/values-ne/strings.xml @@ -85,8 +85,7 @@ <string name="screenshot_failed_title" msgid="3259148215671936891">"स्क्रिनसट सुरक्षित गर्न सकिएन"</string> <string name="screenshot_failed_to_save_user_locked_text" msgid="6156607948256936920">"यन्त्र अनलक गरेपछि मात्र स्क्रिनसट सुरक्षित गर्न सकिन्छ"</string> <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"स्क्रिनसट फेरि लिएर हेर्नुहोस्"</string> - <!-- no translation found for screenshot_failed_to_save_text (7232739948999195960) --> - <skip /> + <string name="screenshot_failed_to_save_text" msgid="7232739948999195960">"स्क्रिनसट सुरक्षित गर्न सकिएन"</string> <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"उक्त एप वा तपाईंको संगठनले स्क्रिनसटहरू लिन दिँदैन"</string> <string name="screenshot_edit_label" msgid="8754981973544133050">"सम्पादन गर्नुहोस्"</string> <string name="screenshot_edit_description" msgid="3333092254706788906">"स्क्रिनसट सम्पादन गर्नुहोस्"</string> @@ -354,6 +353,8 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"प्रयोगकर्ता"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"नयाँ प्रयोगकर्ता"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> + <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> + <skip /> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"जोडिएको छैन"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"नेटवर्क छैन"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi बन्द"</string> @@ -455,9 +456,11 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"प्रोफाइल देखाउनुहोस्"</string> <string name="user_add_user" msgid="4336657383006913022">"प्रयोगकर्ता थप्नुहोस्"</string> <string name="user_new_user_name" msgid="2019166282704195789">"नयाँ प्रयोगकर्ता"</string> - <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"अतिथि हटाउने हो?"</string> + <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"यस सत्रमा सबै एपहरू र डेटा मेटाइनेछ।"</string> - <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"हटाउनुहोस्"</string> + <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"पुनः स्वागत, अतिथि!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"तपाईं आफ्नो सत्र जारी गर्न चाहनुहुन्छ?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"सुरु गर्नुहोस्"</string> diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml index a858a1d79e0e..7d878170283f 100644 --- a/packages/SystemUI/res/values-nl/strings.xml +++ b/packages/SystemUI/res/values-nl/strings.xml @@ -353,6 +353,8 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"Gebruiker"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Nieuwe gebruiker"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wifi"</string> + <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> + <skip /> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Niet verbonden"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Geen netwerk"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wifi uit"</string> @@ -454,9 +456,11 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Profiel weergeven"</string> <string name="user_add_user" msgid="4336657383006913022">"Gebruiker toevoegen"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Nieuwe gebruiker"</string> - <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Gast verwijderen?"</string> + <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Alle apps en gegevens in deze sessie worden verwijderd."</string> - <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Verwijderen"</string> + <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Welkom terug, gast!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Wil je doorgaan met je sessie?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Opnieuw starten"</string> diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml index 4931b770cada..7d7b84f785f1 100644 --- a/packages/SystemUI/res/values-or/strings.xml +++ b/packages/SystemUI/res/values-or/strings.xml @@ -353,6 +353,8 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"ୟୁଜର୍"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"ନୂଆ ଉପଯୋଗକର୍ତ୍ତା"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"ୱାଇ-ଫାଇ"</string> + <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> + <skip /> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"ସଂଯୁକ୍ତ ହୋଇନାହିଁ"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"ନେଟ୍ୱର୍କ ନାହିଁ"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"ୱାଇ-ଫାଇ ଅଫ୍"</string> @@ -454,9 +456,11 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"ପ୍ରୋଫାଇଲ୍ ଦେଖାନ୍ତୁ"</string> <string name="user_add_user" msgid="4336657383006913022">"ଉପଯୋଗକର୍ତ୍ତାଙ୍କୁ ଯୋଗ କରନ୍ତୁ"</string> <string name="user_new_user_name" msgid="2019166282704195789">"ନୂଆ ଉପଯୋଗକର୍ତ୍ତା"</string> - <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"ଅତିଥିଙ୍କୁ କାଢ଼ିଦେବେ?"</string> + <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ଏହି ଅବଧିର ସମସ୍ତ ଆପ୍ ଓ ଡାଟା ଡିଲିଟ୍ ହୋଇଯିବ।"</string> - <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"କାଢ଼ିଦିଅନ୍ତୁ"</string> + <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"ପୁଣି ସ୍ୱାଗତ, ଅତିଥି!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"ଆପଣ ନିଜର ଅବଧି ଜାରି ରଖିବାକୁ ଚାହାନ୍ତି କି?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"ଆରମ୍ଭ କରନ୍ତୁ"</string> diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml index a2ff2b2932db..7d5bc96830a5 100644 --- a/packages/SystemUI/res/values-pa/strings.xml +++ b/packages/SystemUI/res/values-pa/strings.xml @@ -85,8 +85,7 @@ <string name="screenshot_failed_title" msgid="3259148215671936891">"ਸਕ੍ਰੀਨਸ਼ਾਟ ਰੱਖਿਅਤ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਿਆ"</string> <string name="screenshot_failed_to_save_user_locked_text" msgid="6156607948256936920">"ਸਕ੍ਰੀਨਸ਼ਾਟ ਨੂੰ ਰੱਖਿਅਤ ਕੀਤੇ ਜਾਣ ਤੋਂ ਪਹਿਲਾਂ ਡੀਵਾਈਸ ਨੂੰ ਅਣਲਾਕ ਕੀਤਾ ਹੋਣਾ ਲਾਜ਼ਮੀ ਹੈ"</string> <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"ਸਕ੍ਰੀਨਸ਼ਾਟ ਦੁਬਾਰਾ ਲੈ ਕੇ ਦੇਖੋ"</string> - <!-- no translation found for screenshot_failed_to_save_text (7232739948999195960) --> - <skip /> + <string name="screenshot_failed_to_save_text" msgid="7232739948999195960">"ਸਕ੍ਰੀਨਸ਼ਾਟ ਨੂੰ ਰੱਖਿਅਤ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਦਾ"</string> <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"ਐਪ ਜਾਂ ਤੁਹਾਡੀ ਸੰਸਥਾ ਵੱਲੋਂ ਸਕ੍ਰੀਨਸ਼ਾਟ ਲੈਣ ਦੀ ਇਜਾਜ਼ਤ ਨਹੀਂ ਦਿੱਤੀ ਗਈ ਹੈ"</string> <string name="screenshot_edit_label" msgid="8754981973544133050">"ਸੰਪਾਦਨ ਕਰੋ"</string> <string name="screenshot_edit_description" msgid="3333092254706788906">"ਸਕ੍ਰੀਨਸ਼ਾਟ ਦਾ ਸੰਪਾਦਨ ਕਰੋ"</string> @@ -354,6 +353,8 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"ਵਰਤੋਂਕਾਰ"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"ਨਵਾਂ ਵਰਤੋਂਕਾਰ"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"ਵਾਈ-ਫਾਈ"</string> + <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> + <skip /> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"ਕਨੈਕਟ ਨਹੀਂ ਕੀਤਾ"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"ਕੋਈ ਨੈੱਟਵਰਕ ਨਹੀਂ"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"ਵਾਈ-ਫਾਈ ਬੰਦ"</string> @@ -455,9 +456,11 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"ਪ੍ਰੋਫਾਈਲ ਦਿਖਾਓ"</string> <string name="user_add_user" msgid="4336657383006913022">"ਵਰਤੋਂਕਾਰ ਸ਼ਾਮਲ ਕਰੋ"</string> <string name="user_new_user_name" msgid="2019166282704195789">"ਨਵਾਂ ਵਰਤੋਂਕਾਰ"</string> - <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"ਕੀ ਮਹਿਮਾਨ ਹਟਾਉਣਾ ਹੈ?"</string> + <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ਇਸ ਸੈਸ਼ਨ ਵਿੱਚ ਸਾਰੀਆਂ ਐਪਾਂ ਅਤੇ ਡਾਟਾ ਨੂੰ ਮਿਟਾ ਦਿੱਤਾ ਜਾਏਗਾ।"</string> - <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"ਹਟਾਓ"</string> + <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"ਮਹਿਮਾਨ, ਫਿਰ ਤੁਹਾਡਾ ਸੁਆਗਤ ਹੈ!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"ਕੀ ਤੁਸੀਂ ਆਪਣਾ ਸੈਸ਼ਨ ਜਾਰੀ ਰੱਖਣਾ ਚਾਹੁੰਦੇ ਹੋ?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"ਸ਼ੁਰੂ ਕਰੋ"</string> diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml index 99552857a17b..8a19e285463e 100644 --- a/packages/SystemUI/res/values-pl/strings.xml +++ b/packages/SystemUI/res/values-pl/strings.xml @@ -355,6 +355,8 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"Użytkownik"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Nowy użytkownik"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> + <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> + <skip /> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Brak połączenia"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Brak sieci"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi wyłączone"</string> @@ -458,9 +460,11 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Pokaż profil"</string> <string name="user_add_user" msgid="4336657383006913022">"Dodaj użytkownika"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Nowy użytkownik"</string> - <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Usunąć gościa?"</string> + <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Wszystkie aplikacje i dane w tej sesji zostaną usunięte."</string> - <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Usuń"</string> + <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Witaj ponownie, gościu!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Chcesz kontynuować sesję?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Rozpocznij nową"</string> diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml index 9b7220fb3c52..57b924d4e6f8 100644 --- a/packages/SystemUI/res/values-pt-rBR/strings.xml +++ b/packages/SystemUI/res/values-pt-rBR/strings.xml @@ -353,6 +353,8 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"Usuário"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Novo usuário"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> + <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> + <skip /> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Não conectado"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Sem rede"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi desligado"</string> @@ -454,9 +456,11 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Mostrar perfil"</string> <string name="user_add_user" msgid="4336657383006913022">"Adicionar usuário"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Novo usuário"</string> - <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Remover convidado?"</string> + <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Todos os apps e dados nesta sessão serão excluídos."</string> - <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Remover"</string> + <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Bem-vindo, convidado."</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Quer continuar a sessão?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Recomeçar"</string> @@ -836,7 +840,7 @@ <item msgid="7453955063378349599">"Inclinação à esquerda"</item> <item msgid="5874146774389433072">"Inclinação à direita"</item> </string-array> - <string name="menu_ime" msgid="5677467548258017952">"Alternador de teclado"</string> + <string name="menu_ime" msgid="5677467548258017952">"Seletor de teclado"</string> <string name="save" msgid="3392754183673848006">"Salvar"</string> <string name="reset" msgid="8715144064608810383">"Redefinir"</string> <string name="adjust_button_width" msgid="8313444823666482197">"Ajustar largura do botão"</string> diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml index 9b5267781ee9..e39f757ce416 100644 --- a/packages/SystemUI/res/values-pt-rPT/strings.xml +++ b/packages/SystemUI/res/values-pt-rPT/strings.xml @@ -353,6 +353,8 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"Utilizador"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Novo utilizador"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> + <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> + <skip /> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Não Ligado"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Sem Rede"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi Desligado"</string> @@ -454,9 +456,11 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Mostrar perfil"</string> <string name="user_add_user" msgid="4336657383006913022">"Adicionar utilizador"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Novo utilizador"</string> - <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Remover o convidado?"</string> + <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Todas as aplicações e dados desta sessão serão eliminados."</string> - <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Remover"</string> + <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Bem-vindo de volta, caro(a) convidado(a)!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Pretende continuar a sessão?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Recomeçar"</string> diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml index 9b7220fb3c52..57b924d4e6f8 100644 --- a/packages/SystemUI/res/values-pt/strings.xml +++ b/packages/SystemUI/res/values-pt/strings.xml @@ -353,6 +353,8 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"Usuário"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Novo usuário"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> + <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> + <skip /> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Não conectado"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Sem rede"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi desligado"</string> @@ -454,9 +456,11 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Mostrar perfil"</string> <string name="user_add_user" msgid="4336657383006913022">"Adicionar usuário"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Novo usuário"</string> - <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Remover convidado?"</string> + <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Todos os apps e dados nesta sessão serão excluídos."</string> - <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Remover"</string> + <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Bem-vindo, convidado."</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Quer continuar a sessão?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Recomeçar"</string> @@ -836,7 +840,7 @@ <item msgid="7453955063378349599">"Inclinação à esquerda"</item> <item msgid="5874146774389433072">"Inclinação à direita"</item> </string-array> - <string name="menu_ime" msgid="5677467548258017952">"Alternador de teclado"</string> + <string name="menu_ime" msgid="5677467548258017952">"Seletor de teclado"</string> <string name="save" msgid="3392754183673848006">"Salvar"</string> <string name="reset" msgid="8715144064608810383">"Redefinir"</string> <string name="adjust_button_width" msgid="8313444823666482197">"Ajustar largura do botão"</string> diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml index d0d01036a041..acbe80140c2f 100644 --- a/packages/SystemUI/res/values-ro/strings.xml +++ b/packages/SystemUI/res/values-ro/strings.xml @@ -354,6 +354,8 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"Utilizator"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Utilizator nou"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> + <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> + <skip /> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Neconectată"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Nicio rețea"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi deconectat"</string> @@ -456,9 +458,11 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Afișați profilul"</string> <string name="user_add_user" msgid="4336657383006913022">"Adăugați un utilizator"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Utilizator nou"</string> - <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Ștergeți invitatul?"</string> + <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Toate aplicațiile și datele din această sesiune vor fi șterse."</string> - <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Ștergeți"</string> + <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Bine ați revenit în sesiunea pentru invitați!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Vreți să continuați sesiunea?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Începeți din nou"</string> diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml index 1b1aa29db67c..c59bd2ae727d 100644 --- a/packages/SystemUI/res/values-ru/strings.xml +++ b/packages/SystemUI/res/values-ru/strings.xml @@ -355,6 +355,8 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"Пользователь"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Новый пользователь"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> + <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> + <skip /> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Нет соединения"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Нет сети"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi выкл."</string> @@ -458,9 +460,11 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Показать профиль."</string> <string name="user_add_user" msgid="4336657383006913022">"Добавить пользователя"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Новый пользователь"</string> - <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Удалить аккаунт гостя?"</string> + <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Все приложения и данные этого профиля будут удалены."</string> - <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Удалить"</string> + <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Рады видеть вас снова!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Продолжить сеанс?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Начать заново"</string> diff --git a/packages/SystemUI/res/values-ru/strings_tv.xml b/packages/SystemUI/res/values-ru/strings_tv.xml index e08ade3bc065..8ce0dc203923 100644 --- a/packages/SystemUI/res/values-ru/strings_tv.xml +++ b/packages/SystemUI/res/values-ru/strings_tv.xml @@ -23,7 +23,7 @@ <string name="app_accessed_mic" msgid="2754428675130470196">"Приложение \"%1$s\" использовало доступ к микрофону."</string> <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN-подключение установлено"</string> <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN-подключение отключено"</string> - <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"Отправлено через <xliff:g id="VPN_APP">%1$s</xliff:g>"</string> + <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"Через приложение <xliff:g id="VPN_APP">%1$s</xliff:g>"</string> <string name="tv_notification_panel_title" msgid="5311050946506276154">"Уведомления"</string> <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Уведомлений нет."</string> </resources> diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml index 998161496f0c..a49ca863b3fa 100644 --- a/packages/SystemUI/res/values-si/strings.xml +++ b/packages/SystemUI/res/values-si/strings.xml @@ -353,6 +353,8 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"පරිශීලක"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"නව පරිශීලකයා"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> + <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> + <skip /> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"සම්බන්ධ වී නොමැත"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"ජාලයක් නැත"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi අක්රියයි"</string> @@ -454,9 +456,11 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"පැතිකඩ පෙන්වන්න"</string> <string name="user_add_user" msgid="4336657383006913022">"පරිශීලකයෙක් එක් කරන්න"</string> <string name="user_new_user_name" msgid="2019166282704195789">"නව පරිශීලකයා"</string> - <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"අමුත්තාන් ඉවත් කරන්නද?"</string> + <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"මෙම සැසියේ සියළුම යෙදුම් සහ දත්ත මකාවී."</string> - <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"ඉවත් කරන්න"</string> + <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"නැවත සාදරයෙන් පිළිගනිමු, අමුත්තා!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"ඔබගේ සැසිය දිගටම කරගෙන යෑමට ඔබට අවශ්යද?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"යළි මුල සිට අරඹන්න"</string> diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml index 470b02154b55..fe63b70f1c46 100644 --- a/packages/SystemUI/res/values-sk/strings.xml +++ b/packages/SystemUI/res/values-sk/strings.xml @@ -355,6 +355,8 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"Používateľ"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Nový používateľ"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi‑Fi"</string> + <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> + <skip /> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Nepripojené"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Žiadna sieť"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Sieť Wi‑Fi je vypnutá"</string> @@ -458,9 +460,11 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Zobraziť profil"</string> <string name="user_add_user" msgid="4336657383006913022">"Pridať používateľa"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Nový používateľ"</string> - <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Odstrániť hosťa?"</string> + <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Všetky aplikácie a údaje v tejto relácii budú odstránené."</string> - <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Odstrániť"</string> + <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Hosť, vitajte späť!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Chcete v relácii pokračovať?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Začať odznova"</string> diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml index 08fdb5895d90..85956d6d0c72 100644 --- a/packages/SystemUI/res/values-sl/strings.xml +++ b/packages/SystemUI/res/values-sl/strings.xml @@ -355,6 +355,8 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"Uporabnik"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Nov uporabnik"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> + <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> + <skip /> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Povezava ni vzpostavljena"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Ni omrežja"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi izklopljen"</string> @@ -458,9 +460,11 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Prikaz profila"</string> <string name="user_add_user" msgid="4336657383006913022">"Dodajanje uporabnika"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Nov uporabnik"</string> - <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Želite odstraniti gosta?"</string> + <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Vse aplikacije in podatki v tej seji bodo izbrisani."</string> - <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Odstrani"</string> + <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Znova pozdravljeni, gost!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Želite nadaljevati sejo?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Začni znova"</string> diff --git a/packages/SystemUI/res/values-sl/strings_tv.xml b/packages/SystemUI/res/values-sl/strings_tv.xml index 57d70c0c3ecb..1f66138c175a 100644 --- a/packages/SystemUI/res/values-sl/strings_tv.xml +++ b/packages/SystemUI/res/values-sl/strings_tv.xml @@ -21,8 +21,8 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="mic_active" msgid="5766614241012047024">"Mikrofon je aktiven"</string> <string name="app_accessed_mic" msgid="2754428675130470196">"Aplikacija %1$s je dostopala do mikrofona"</string> - <string name="notification_vpn_connected" msgid="3891023882833274730">"Povezava z navideznim zasebnim omrežjem je vzpostavljena"</string> - <string name="notification_vpn_disconnected" msgid="7150747626448044843">"Povezava z navideznim zasebnim omrežjem je prekinjena"</string> + <string name="notification_vpn_connected" msgid="3891023882833274730">"Povezava z omrežjem VPN je vzpostavljena"</string> + <string name="notification_vpn_disconnected" msgid="7150747626448044843">"Povezava z omrežjem VPN je prekinjena"</string> <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"Prek storitve <xliff:g id="VPN_APP">%1$s</xliff:g>"</string> <string name="tv_notification_panel_title" msgid="5311050946506276154">"Obvestila"</string> <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Ni obvestil"</string> diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml index aaee22511860..733db0e018a8 100644 --- a/packages/SystemUI/res/values-sq/strings.xml +++ b/packages/SystemUI/res/values-sq/strings.xml @@ -57,15 +57,15 @@ <string name="label_view" msgid="6815442985276363364">"Pamje"</string> <string name="always_use_device" msgid="210535878779644679">"Hap gjithmonë <xliff:g id="APPLICATION">%1$s</xliff:g> kur lidhet <xliff:g id="USB_DEVICE">%2$s</xliff:g>"</string> <string name="always_use_accessory" msgid="1977225429341838444">"Hap gjithmonë <xliff:g id="APPLICATION">%1$s</xliff:g> kur lidhet <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>"</string> - <string name="usb_debugging_title" msgid="8274884945238642726">"Të lejohet korrigjimi i USB-së?"</string> + <string name="usb_debugging_title" msgid="8274884945238642726">"Të lejohet korrigjimi përmes USB-së?"</string> <string name="usb_debugging_message" msgid="5794616114463921773">"Gjurma e gishtit të tastit \"RSA\" së kompjuterit është:\n<xliff:g id="FINGERPRINT">%1$s</xliff:g>"</string> <string name="usb_debugging_always" msgid="4003121804294739548">"Lejo gjithmonë nga ky kompjuter"</string> <string name="usb_debugging_allow" msgid="1722643858015321328">"Lejo"</string> - <string name="usb_debugging_secondary_user_title" msgid="7843050591380107998">"Korrigjimi i USB-së nuk lejohet"</string> - <string name="usb_debugging_secondary_user_message" msgid="3740347841470403244">"Përdoruesi i identifikuar aktualisht në këtë pajisje nuk mund ta aktivizojë korrigjimin e USB-së. Për ta përdorur këtë funksion, kalo te përdoruesi parësor."</string> + <string name="usb_debugging_secondary_user_title" msgid="7843050591380107998">"Korrigjimi përmes USB-së nuk lejohet"</string> + <string name="usb_debugging_secondary_user_message" msgid="3740347841470403244">"Përdoruesi i identifikuar aktualisht në këtë pajisje nuk mund ta aktivizojë korrigjimin përmes USB-së. Për ta përdorur këtë veçori, kalo te përdoruesi parësor."</string> <string name="wifi_debugging_title" msgid="7300007687492186076">"Do ta lejosh korrigjimin përmes Wi-Fi në këtë rrjet?"</string> <string name="wifi_debugging_message" msgid="5461204211731802995">"Emri i rrjetit (SSID)\n<xliff:g id="SSID_0">%1$s</xliff:g>\n\nAdresa Wi‑Fi (BSSID)\n<xliff:g id="BSSID_1">%2$s</xliff:g>"</string> - <string name="wifi_debugging_always" msgid="2968383799517975155">"Shfaq gjithmonë në këtë rrjet"</string> + <string name="wifi_debugging_always" msgid="2968383799517975155">"Lejo gjithmonë në këtë rrjet"</string> <string name="wifi_debugging_allow" msgid="4573224609684957886">"Lejo"</string> <string name="wifi_debugging_secondary_user_title" msgid="2493201475880517725">"Korrigjimi përmes Wi-Fi nuk lejohet"</string> <string name="wifi_debugging_secondary_user_message" msgid="4492383073970079751">"Përdoruesi i identifikuar aktualisht në këtë pajisje nuk mund ta aktivizojë korrigjimin përmes Wi-Fi. Për ta përdorur këtë veçori, kalo te përdoruesi parësor."</string> @@ -353,6 +353,8 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"Përdoruesi"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Përdorues i ri"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> + <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> + <skip /> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Nuk është i lidhur"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Nuk ka rrjet"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi është i çaktivizuar"</string> @@ -454,9 +456,9 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Shfaq profilin"</string> <string name="user_add_user" msgid="4336657383006913022">"Shto përdorues"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Përdorues i ri"</string> - <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Të hiqet i ftuari?"</string> + <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Dëshiron t\'i japësh fund sesionit të vizitorit?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Të gjitha aplikacionet dhe të dhënat në këtë sesion do të fshihen."</string> - <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Hiq"</string> + <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Jepi fund sesionit"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Mirë se erdhe, i ftuar!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Dëshiron ta vazhdosh sesionin tënd?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Fillo nga e para"</string> diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml index d7645be711ed..c5414a4e3f80 100644 --- a/packages/SystemUI/res/values-sr/strings.xml +++ b/packages/SystemUI/res/values-sr/strings.xml @@ -354,6 +354,8 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"Корисник"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Нови корисник"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"WiFi"</string> + <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> + <skip /> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Веза није успостављена"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Нема мреже"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"WiFi је искључен"</string> @@ -456,9 +458,11 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Прикажи профил"</string> <string name="user_add_user" msgid="4336657383006913022">"Додај корисника"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Нови корисник"</string> - <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Желите ли да уклоните госта?"</string> + <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Све апликације и подаци у овој сесији ће бити избрисани."</string> - <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Уклони"</string> + <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Добро дошли назад, госте!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Желите ли да наставите сесију?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Почни из почетка"</string> diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml index 8e5e79e809f5..d2c993a0985a 100644 --- a/packages/SystemUI/res/values-sv/strings.xml +++ b/packages/SystemUI/res/values-sv/strings.xml @@ -353,6 +353,8 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"Användare"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Ny användare"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> + <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> + <skip /> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Ej ansluten"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Inget nätverk"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi av"</string> @@ -454,9 +456,11 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Visa profil"</string> <string name="user_add_user" msgid="4336657383006913022">"Lägg till användare"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Ny användare"</string> - <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Vill du ta bort gästen?"</string> + <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Alla appar och data i denna session kommer att raderas."</string> - <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Ta bort"</string> + <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Välkommen tillbaka gäst!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Vill du fortsätta sessionen?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Börja om"</string> diff --git a/packages/SystemUI/res/values-sv/strings_tv.xml b/packages/SystemUI/res/values-sv/strings_tv.xml index d7261e6b1445..fd8fa4b1bb8e 100644 --- a/packages/SystemUI/res/values-sv/strings_tv.xml +++ b/packages/SystemUI/res/values-sv/strings_tv.xml @@ -23,7 +23,7 @@ <string name="app_accessed_mic" msgid="2754428675130470196">"%1$s har fått åtkomst till mikrofonen"</string> <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN är anslutet"</string> <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN är frånkopplat"</string> - <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"Via <xliff:g id="VPN_APP">%1$s</xliff:g>"</string> + <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"via <xliff:g id="VPN_APP">%1$s</xliff:g>"</string> <string name="tv_notification_panel_title" msgid="5311050946506276154">"Aviseringar"</string> <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Inga aviseringar"</string> </resources> diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml index 768fd8549d48..29045f3d69ae 100644 --- a/packages/SystemUI/res/values-sw/strings.xml +++ b/packages/SystemUI/res/values-sw/strings.xml @@ -353,6 +353,8 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"Mtumiaji"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Mtumiaji mpya"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> + <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> + <skip /> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Haijaunganishwa"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Hakuna Mtandao"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi Imezimwa"</string> @@ -454,9 +456,11 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Onyesha wasifu"</string> <string name="user_add_user" msgid="4336657383006913022">"Ongeza mtumiaji"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Mtumiaji mpya"</string> - <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Ungependa kumwondoa mgeni?"</string> + <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Data na programu zote katika kipindi hiki zitafutwa."</string> - <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Ondoa"</string> + <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Karibu tena, mwalikwa!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Je, unataka kuendelea na kipindi chako?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Anza tena"</string> diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml index efdace48ff8e..2ca67201d387 100644 --- a/packages/SystemUI/res/values-ta/strings.xml +++ b/packages/SystemUI/res/values-ta/strings.xml @@ -353,6 +353,8 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"பயனர்"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"புதியவர்"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"வைஃபை"</string> + <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> + <skip /> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"இணைக்கப்படவில்லை"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"நெட்வொர்க் இல்லை"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"வைஃபையை முடக்கு"</string> @@ -454,9 +456,11 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"சுயவிவரத்தைக் காட்டு"</string> <string name="user_add_user" msgid="4336657383006913022">"பயனரைச் சேர்"</string> <string name="user_new_user_name" msgid="2019166282704195789">"புதியவர்"</string> - <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"கெஸ்ட்டை அகற்றவா?"</string> + <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"இந்த அமர்வின் எல்லா பயன்பாடுகளும், தரவும் நீக்கப்படும்."</string> - <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"அகற்று"</string> + <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"நல்வரவு!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"உங்கள் அமர்வைத் தொடர விருப்பமா?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"மீண்டும் தொடங்கு"</string> diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml index 36a5de721451..9ce5fc745580 100644 --- a/packages/SystemUI/res/values-te/strings.xml +++ b/packages/SystemUI/res/values-te/strings.xml @@ -353,6 +353,8 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"వినియోగదారు"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"కొత్త వినియోగదారు"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> + <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> + <skip /> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"కనెక్ట్ చేయబడలేదు"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"నెట్వర్క్ లేదు"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi ఆఫ్లో ఉంది"</string> @@ -454,9 +456,11 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"ప్రొఫైల్ని చూపు"</string> <string name="user_add_user" msgid="4336657383006913022">"వినియోగదారుని జోడించండి"</string> <string name="user_new_user_name" msgid="2019166282704195789">"కొత్త వినియోగదారు"</string> - <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"అతిథిని తీసివేయాలా?"</string> + <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ఈ సెషన్లోని అన్ని అనువర్తనాలు మరియు డేటా తొలగించబడతాయి."</string> - <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"తీసివేయి"</string> + <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"పునఃస్వాగతం, అతిథి!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"మీరు మీ సెషన్ని కొనసాగించాలనుకుంటున్నారా?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"మొదటి నుండి ప్రారంభించు"</string> diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml index 5dd4400e9ece..900012eb267d 100644 --- a/packages/SystemUI/res/values-th/strings.xml +++ b/packages/SystemUI/res/values-th/strings.xml @@ -93,7 +93,7 @@ <string name="screenshot_scroll_description" msgid="7855773867093272175">"เลื่อนจับภาพหน้าจอ"</string> <string name="screenshot_dismiss_description" msgid="4702341245899508786">"ปิดภาพหน้าจอ"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"ตัวอย่างภาพหน้าจอ"</string> - <string name="screenrecord_name" msgid="2596401223859996572">"โปรแกรมอัดหน้าจอ"</string> + <string name="screenrecord_name" msgid="2596401223859996572">"โปรแกรมบันทึกหน้าจอ"</string> <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"กำลังประมวลผลการอัดหน้าจอ"</string> <string name="screenrecord_channel_description" msgid="4147077128486138351">"การแจ้งเตือนต่อเนื่องสำหรับเซสชันการบันทึกหน้าจอ"</string> <string name="screenrecord_start_label" msgid="1750350278888217473">"เริ่มบันทึกเลยไหม"</string> @@ -353,6 +353,8 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"ผู้ใช้"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"ผู้ใช้ใหม่"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> + <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> + <skip /> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"ไม่ได้เชื่อมต่อ"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"ไม่มีเครือข่าย"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"ปิด WiFi"</string> @@ -454,9 +456,11 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"แสดงโปรไฟล์"</string> <string name="user_add_user" msgid="4336657383006913022">"เพิ่มผู้ใช้"</string> <string name="user_new_user_name" msgid="2019166282704195789">"ผู้ใช้ใหม่"</string> - <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"ต้องการนำผู้เข้าร่วมออกไหม"</string> + <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ระบบจะลบแอปและข้อมูลทั้งหมดในเซสชันนี้"</string> - <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"นำออก"</string> + <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"ยินดีต้อนรับท่านผู้เยี่ยมชมกลับมาอีกครั้ง!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"คุณต้องการอยู่ในเซสชันต่อไปไหม"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"เริ่มต้นใหม่"</string> @@ -744,7 +748,7 @@ <string name="notification_conversation_home_screen" msgid="8347136037958438935">"เพิ่มลงในหน้าจอหลัก"</string> <string name="notification_menu_accessibility" msgid="8984166825879886773">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string> <string name="notification_menu_gear_description" msgid="6429668976593634862">"ส่วนควบคุมการแจ้งเตือน"</string> - <string name="notification_menu_snooze_description" msgid="4740133348901973244">"ตัวเลือกการปิดเสียงแจ้งเตือนชั่วคราว"</string> + <string name="notification_menu_snooze_description" msgid="4740133348901973244">"ตัวเลือกการเลื่อนการแจ้งเตือน"</string> <string name="notification_menu_snooze_action" msgid="5415729610393475019">"เตือนฉัน"</string> <string name="notification_menu_settings_action" msgid="7085494017202764285">"การตั้งค่า"</string> <string name="snooze_undo" msgid="60890935148417175">"เลิกทำ"</string> diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml index 0ade0ed7416d..f531de5a89de 100644 --- a/packages/SystemUI/res/values-tl/strings.xml +++ b/packages/SystemUI/res/values-tl/strings.xml @@ -353,6 +353,8 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"User"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Bagong user"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> + <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> + <skip /> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Hindi Nakakonekta"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Walang Network"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Naka-off ang Wi-Fi"</string> @@ -454,9 +456,11 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Ipakita ang profile"</string> <string name="user_add_user" msgid="4336657383006913022">"Magdagdag ng user"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Bagong user"</string> - <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Alisin ang bisita?"</string> + <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Ide-delete ang lahat ng app at data sa session na ito."</string> - <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Alisin"</string> + <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Maligayang pagbabalik, bisita!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Gusto mo bang ipagpatuloy ang iyong session?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Magsimulang muli"</string> diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml index 2608931f296e..ba70531a89f3 100644 --- a/packages/SystemUI/res/values-tr/strings.xml +++ b/packages/SystemUI/res/values-tr/strings.xml @@ -353,6 +353,8 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"Kullanıcı"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Yeni kullanıcı"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Kablosuz"</string> + <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> + <skip /> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Bağlı Değil"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Ağ yok"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Kablosuz Kapalı"</string> @@ -454,9 +456,11 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Profili göster"</string> <string name="user_add_user" msgid="4336657383006913022">"Kullanıcı ekle"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Yeni kullanıcı"</string> - <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Misafir oturumu kaldırılsın mı?"</string> + <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Bu oturumdaki tüm uygulamalar ve veriler silinecek."</string> - <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Kaldır"</string> + <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Tekrar hoş geldiniz sayın misafir!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Oturumunuza devam etmek istiyor musunuz?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Baştan başla"</string> diff --git a/packages/SystemUI/res/values-tr/strings_tv.xml b/packages/SystemUI/res/values-tr/strings_tv.xml index babd460a1375..49e76af004ab 100644 --- a/packages/SystemUI/res/values-tr/strings_tv.xml +++ b/packages/SystemUI/res/values-tr/strings_tv.xml @@ -23,7 +23,7 @@ <string name="app_accessed_mic" msgid="2754428675130470196">"%1$s mikrofonunuza erişti"</string> <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN bağlandı"</string> <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN bağlantısı kesildi"</string> - <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"<xliff:g id="VPN_APP">%1$s</xliff:g> yoluyla"</string> + <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"<xliff:g id="VPN_APP">%1$s</xliff:g> üzerinden"</string> <string name="tv_notification_panel_title" msgid="5311050946506276154">"Bildirimler"</string> <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Bildirim Yok"</string> </resources> diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml index ce00b92093ef..b2ae569cf26c 100644 --- a/packages/SystemUI/res/values-uk/strings.xml +++ b/packages/SystemUI/res/values-uk/strings.xml @@ -355,6 +355,8 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"Користувач"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Новий користувач"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> + <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> + <skip /> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Не під’єднано."</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Немає мережі"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi вимкнено"</string> @@ -458,9 +460,11 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Показати профіль"</string> <string name="user_add_user" msgid="4336657383006913022">"Додати користувача"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Новий користувач"</string> - <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Вийти з режиму гостя?"</string> + <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Усі додатки й дані з цього сеансу буде видалено."</string> - <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Вийти"</string> + <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"З поверненням!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Продовжити сеанс?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Почати знову"</string> diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml index 0032c6bd7c74..06a448b25579 100644 --- a/packages/SystemUI/res/values-ur/strings.xml +++ b/packages/SystemUI/res/values-ur/strings.xml @@ -353,6 +353,8 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"صارف"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"نیا صارف"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> + <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> + <skip /> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"مربوط نہیں ہے"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"کوئی نیٹ ورک نہیں ہے"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi آف ہے"</string> @@ -454,9 +456,11 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"پروفائل دکھائیں"</string> <string name="user_add_user" msgid="4336657383006913022">"صارف کو شامل کریں"</string> <string name="user_new_user_name" msgid="2019166282704195789">"نیا صارف"</string> - <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"مہمان کو ہٹائیں؟"</string> + <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"اس سیشن میں موجود سبھی ایپس اور ڈیٹا کو حذف کر دیا جائے گا۔"</string> - <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"ہٹائیں"</string> + <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"مہمان، پھر سے خوش آمدید!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"کیا آپ اپنا سیشن جاری رکھنا چاہتے ہیں؟"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"دوبارہ شروع کریں"</string> diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml index 532fa4021131..e1add6503412 100644 --- a/packages/SystemUI/res/values-uz/strings.xml +++ b/packages/SystemUI/res/values-uz/strings.xml @@ -353,6 +353,8 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"Foydalanuvchi"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Yangi foydalanuvchi"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> + <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> + <skip /> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Ulanmagan"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Tarmoq mavjud emas"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi o‘chiq"</string> @@ -454,9 +456,9 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Profilni ko‘rsatish"</string> <string name="user_add_user" msgid="4336657383006913022">"Foydalanuvchi"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Yangi foydalanuvchi"</string> - <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Mehmon hisobi o‘chirib tashlansinmi?"</string> + <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Mehmon seansi yakunlansinmi?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Ushbu seansdagi barcha ilovalar va ma’lumotlar o‘chirib tashlanadi."</string> - <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Olib tashlash"</string> + <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Seansni yakunlash"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Xush kelibsiz, mehmon!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Seansni davom ettirmoqchimisiz?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Boshidan boshlansin"</string> @@ -493,7 +495,7 @@ <string name="notification_section_header_alerting" msgid="5581175033680477651">"Bildirishnomalar"</string> <string name="notification_section_header_conversations" msgid="821834744538345661">"Suhbatlar"</string> <string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Barcha sokin bildirishnomalarni tozalash"</string> - <string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Bezovta qilinmasin rejimida bildirishnomalar pauza qilingan"</string> + <string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Bezovta qilinmasin rejimida bildirishnomalar pauza qilinadi"</string> <string name="media_projection_action_text" msgid="3634906766918186440">"Boshlash"</string> <string name="empty_shade_text" msgid="8935967157319717412">"Bildirishnomalar yo‘q"</string> <string name="profile_owned_footer" msgid="2756770645766113964">"Profil kuzatilishi mumkin"</string> diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml index 0e9987732d26..021044d29b34 100644 --- a/packages/SystemUI/res/values-vi/strings.xml +++ b/packages/SystemUI/res/values-vi/strings.xml @@ -353,6 +353,8 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"Người dùng"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Người dùng mới"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> + <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> + <skip /> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Chưa được kết nối"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Không có mạng nào"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Tắt Wi-Fi"</string> @@ -454,9 +456,11 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Hiển thị hồ sơ"</string> <string name="user_add_user" msgid="4336657383006913022">"Thêm người dùng"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Người dùng mới"</string> - <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Xóa phiên khách?"</string> + <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Tất cả ứng dụng và dữ liệu trong phiên này sẽ bị xóa."</string> - <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Xóa"</string> + <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Chào mừng bạn trở lại!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Bạn có muốn tiếp tục phiên của mình không?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Bắt đầu lại"</string> diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml index 07232ff1119e..60acb839b33a 100644 --- a/packages/SystemUI/res/values-zh-rCN/strings.xml +++ b/packages/SystemUI/res/values-zh-rCN/strings.xml @@ -353,6 +353,8 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"用户"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"新用户"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"WLAN"</string> + <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> + <skip /> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"未连接"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"无网络"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"WLAN:关闭"</string> @@ -454,9 +456,11 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"显示个人资料"</string> <string name="user_add_user" msgid="4336657383006913022">"添加用户"</string> <string name="user_new_user_name" msgid="2019166282704195789">"新用户"</string> - <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"要移除访客吗?"</string> + <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"此会话中的所有应用和数据都将被删除。"</string> - <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"移除"</string> + <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"访客,欢迎回来!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"要继续您的会话吗?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"重新开始"</string> diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml index 7a07af104f07..db55580872f4 100644 --- a/packages/SystemUI/res/values-zh-rHK/strings.xml +++ b/packages/SystemUI/res/values-zh-rHK/strings.xml @@ -353,6 +353,8 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"使用者"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"新使用者"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> + <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> + <skip /> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"未連線"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"沒有網絡"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi 關閉"</string> @@ -454,9 +456,11 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"顯示個人檔案"</string> <string name="user_add_user" msgid="4336657383006913022">"加入使用者"</string> <string name="user_new_user_name" msgid="2019166282704195789">"新使用者"</string> - <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"移除訪客?"</string> + <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"這個工作階段中的所有應用程式和資料都會被刪除。"</string> - <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"移除"</string> + <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"訪客您好,歡迎回來!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"您要繼續您的工作階段嗎?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"重新開始"</string> diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml index 0369d6318760..1487ad33d12f 100644 --- a/packages/SystemUI/res/values-zh-rTW/strings.xml +++ b/packages/SystemUI/res/values-zh-rTW/strings.xml @@ -353,6 +353,8 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"使用者"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"新使用者"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> + <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> + <skip /> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"未連線"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"沒有網路"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi 已關閉"</string> @@ -454,9 +456,11 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"顯示設定檔"</string> <string name="user_add_user" msgid="4336657383006913022">"新增使用者"</string> <string name="user_new_user_name" msgid="2019166282704195789">"新使用者"</string> - <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"移除訪客?"</string> + <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"這個工作階段中的所有應用程式和資料都會遭到刪除。"</string> - <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"移除"</string> + <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"訪客你好,歡迎回來!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"你要繼續這個工作階段嗎?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"重新開始"</string> diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml index 068e2e53f8a1..b0f084b7f83d 100644 --- a/packages/SystemUI/res/values-zu/strings.xml +++ b/packages/SystemUI/res/values-zu/strings.xml @@ -353,6 +353,8 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"Umsebenzisi"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Umsebenzisi omusha"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"I-Wi-Fi"</string> + <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> + <skip /> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Akuxhunyiwe"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Ayikho inethiwekhi"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"I-Wi-Fi icimile"</string> @@ -454,9 +456,11 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Bonisa iphrofayela"</string> <string name="user_add_user" msgid="4336657383006913022">"Engeza umsebenzisi"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Umsebenzisi omusha"</string> - <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Susa isivakashi?"</string> + <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> + <skip /> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Zonke izinhlelo zokusebenza nedatha kulesi sikhathi zizosuswa."</string> - <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Susa"</string> + <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> + <skip /> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Siyakwamukela futhi, sivakashi!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Ingabe ufuna ukuqhubeka ngesikhathi sakho?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Qala phansi"</string> diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java index 323449af0850..6cc863a448c7 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java @@ -1,6 +1,7 @@ package com.android.keyguard; import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.animation.ValueAnimator; @@ -107,7 +108,10 @@ public class KeyguardClockSwitch extends RelativeLayout { /** * Boolean value indicating if notifications are visible on lock screen. */ - private boolean mHasVisibleNotifications; + private boolean mHasVisibleNotifications = true; + + private AnimatorSet mClockInAnim = null; + private AnimatorSet mClockOutAnim = null; /** * If the Keyguard Slice has a header (big center-aligned text.) @@ -318,12 +322,15 @@ public class KeyguardClockSwitch extends RelativeLayout { private void animateClockChange(boolean useLargeClock) { if (mLockScreenMode != KeyguardUpdateMonitor.LOCK_SCREEN_MODE_LAYOUT_1) return; + if (mClockInAnim != null) mClockInAnim.cancel(); + if (mClockOutAnim != null) mClockOutAnim.cancel(); + View in, out; int direction = 1; if (useLargeClock) { out = mNewLockscreenClockFrame; in = mNewLockscreenLargeClockFrame; - addView(in); + if (indexOfChild(in) == -1) addView(in); direction = -1; } else { in = mNewLockscreenClockFrame; @@ -333,25 +340,35 @@ public class KeyguardClockSwitch extends RelativeLayout { removeView(out); } - AnimatorSet outAnim = new AnimatorSet(); - outAnim.setDuration(CLOCK_OUT_MILLIS); - outAnim.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN); - outAnim.playTogether( + mClockOutAnim = new AnimatorSet(); + mClockOutAnim.setDuration(CLOCK_OUT_MILLIS); + mClockOutAnim.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN); + mClockOutAnim.playTogether( ObjectAnimator.ofFloat(out, View.ALPHA, 0f), ObjectAnimator.ofFloat(out, View.TRANSLATION_Y, 0, direction * -mClockSwitchYAmount)); + mClockOutAnim.addListener(new AnimatorListenerAdapter() { + public void onAnimationEnd(Animator animation) { + mClockOutAnim = null; + } + }); in.setAlpha(0); in.setVisibility(View.VISIBLE); - AnimatorSet inAnim = new AnimatorSet(); - inAnim.setDuration(CLOCK_IN_MILLIS); - inAnim.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN); - inAnim.playTogether(ObjectAnimator.ofFloat(in, View.ALPHA, 1f), + mClockInAnim = new AnimatorSet(); + mClockInAnim.setDuration(CLOCK_IN_MILLIS); + mClockInAnim.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN); + mClockInAnim.playTogether(ObjectAnimator.ofFloat(in, View.ALPHA, 1f), ObjectAnimator.ofFloat(in, View.TRANSLATION_Y, direction * mClockSwitchYAmount, 0)); - inAnim.setStartDelay(CLOCK_OUT_MILLIS / 2); + mClockInAnim.setStartDelay(CLOCK_OUT_MILLIS / 2); + mClockInAnim.addListener(new AnimatorListenerAdapter() { + public void onAnimationEnd(Animator animation) { + mClockInAnim = null; + } + }); - inAnim.start(); - outAnim.start(); + mClockInAnim.start(); + mClockOutAnim.start(); } /** diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java index 926062b1623f..f9505dec78fd 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java @@ -28,9 +28,9 @@ import com.android.wm.shell.ShellCommandHandler; import com.android.wm.shell.apppairs.AppPairs; import com.android.wm.shell.bubbles.Bubbles; import com.android.wm.shell.hidedisplaycutout.HideDisplayCutout; +import com.android.wm.shell.legacysplitscreen.LegacySplitScreen; import com.android.wm.shell.onehanded.OneHanded; import com.android.wm.shell.pip.Pip; -import com.android.wm.shell.legacysplitscreen.LegacySplitScreen; import java.util.Optional; diff --git a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java index a1bde589d5c1..943a54e61c03 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java @@ -22,9 +22,9 @@ import com.android.wm.shell.ShellInit; import com.android.wm.shell.apppairs.AppPairs; import com.android.wm.shell.bubbles.Bubbles; import com.android.wm.shell.hidedisplaycutout.HideDisplayCutout; +import com.android.wm.shell.legacysplitscreen.LegacySplitScreen; import com.android.wm.shell.onehanded.OneHanded; import com.android.wm.shell.pip.Pip; -import com.android.wm.shell.legacysplitscreen.LegacySplitScreen; import java.util.Optional; diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt b/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt index 5c1c60c5b07e..80d13715ca07 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt @@ -29,11 +29,15 @@ import android.provider.Settings import android.service.media.MediaBrowserService import android.util.Log import com.android.internal.annotations.VisibleForTesting +import com.android.systemui.Dumpable import com.android.systemui.broadcast.BroadcastDispatcher import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.dump.DumpManager import com.android.systemui.tuner.TunerService import com.android.systemui.util.Utils +import java.io.FileDescriptor +import java.io.PrintWriter import java.util.concurrent.ConcurrentLinkedQueue import java.util.concurrent.Executor import javax.inject.Inject @@ -49,8 +53,9 @@ class MediaResumeListener @Inject constructor( private val broadcastDispatcher: BroadcastDispatcher, @Background private val backgroundExecutor: Executor, private val tunerService: TunerService, - private val mediaBrowserFactory: ResumeMediaBrowserFactory -) : MediaDataManager.Listener { + private val mediaBrowserFactory: ResumeMediaBrowserFactory, + dumpManager: DumpManager +) : MediaDataManager.Listener, Dumpable { private var useMediaResumption: Boolean = Utils.useMediaResumption(context) private val resumeComponents: ConcurrentLinkedQueue<ComponentName> = ConcurrentLinkedQueue() @@ -99,6 +104,7 @@ class MediaResumeListener @Inject constructor( init { if (useMediaResumption) { + dumpManager.registerDumpable(TAG, this) val unlockFilter = IntentFilter() unlockFilter.addAction(Intent.ACTION_USER_UNLOCKED) unlockFilter.addAction(Intent.ACTION_USER_SWITCHED) @@ -283,4 +289,10 @@ class MediaResumeListener @Inject constructor( mediaBrowser?.restart() } } + + override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) { + pw.apply { + println("resumeComponents: $resumeComponents") + } + } }
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java index 6d9d587bc729..54e30af675ab 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java +++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java @@ -91,11 +91,11 @@ import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.phone.StatusBarWindowCallback; import com.android.systemui.statusbar.policy.CallbackController; +import com.android.wm.shell.legacysplitscreen.LegacySplitScreen; import com.android.wm.shell.onehanded.OneHanded; import com.android.wm.shell.onehanded.OneHandedEvents; import com.android.wm.shell.pip.Pip; import com.android.wm.shell.pip.PipAnimationController; -import com.android.wm.shell.legacysplitscreen.LegacySplitScreen; import java.io.FileDescriptor; import java.io.PrintWriter; diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ImageTile.java b/packages/SystemUI/src/com/android/systemui/screenshot/ImageTile.java new file mode 100644 index 000000000000..143121af9f2c --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ImageTile.java @@ -0,0 +1,113 @@ +/* + * 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.screenshot; + +import static android.graphics.ColorSpace.Named.SRGB; + +import static java.util.Objects.requireNonNull; + +import android.graphics.Bitmap; +import android.graphics.ColorSpace; +import android.graphics.RecordingCanvas; +import android.graphics.Rect; +import android.graphics.RenderNode; +import android.media.Image; + +/** + * Holds a hardware image, coordinates and render node to draw the tile. The tile manages clipping + * and dimensions. The tile must be drawn translated to the correct target position: + * <pre> + * ImageTile tile = getTile(); + * canvas.save(); + * canvas.translate(tile.getLeft(), tile.getTop()); + * canvas.drawRenderNode(tile.getDisplayList()); + * canvas.restore(); + * </pre> + */ +class ImageTile implements AutoCloseable { + private final Image mImage; + private final Rect mLocation; + private RenderNode mNode; + + private static final ColorSpace COLOR_SPACE = ColorSpace.get(SRGB); + + /** + * Create an image tile from the given image. + * + * @param image an image containing a hardware buffer + * @param location the captured area represented by image tile (virtual coordinates) + */ + ImageTile(Image image, Rect location) { + mImage = requireNonNull(image, "image"); + mLocation = location; + + requireNonNull(mImage.getHardwareBuffer(), "image must be a hardware image"); + } + + RenderNode getDisplayList() { + if (mNode == null) { + mNode = new RenderNode("Tile{" + Integer.toHexString(mImage.hashCode()) + "}"); + } + if (mNode.hasDisplayList()) { + return mNode; + } + final int w = Math.min(mImage.getWidth(), mLocation.width()); + final int h = Math.min(mImage.getHeight(), mLocation.height()); + mNode.setPosition(0, 0, w, h); + + RecordingCanvas canvas = mNode.beginRecording(w, h); + Rect rect = new Rect(0, 0, w, h); + canvas.save(); + canvas.clipRect(0, 0, mLocation.right, mLocation.bottom); + canvas.drawBitmap(Bitmap.wrapHardwareBuffer(mImage.getHardwareBuffer(), COLOR_SPACE), + 0, 0, null); + canvas.restore(); + mNode.endRecording(); + return mNode; + } + + Rect getLocation() { + return mLocation; + } + + int getLeft() { + return mLocation.left; + } + + int getTop() { + return mLocation.top; + } + + int getRight() { + return mLocation.right; + } + + int getBottom() { + return mLocation.bottom; + } + + @Override + public void close() { + mImage.close(); + mNode.discardDisplayList(); + } + + @Override + public String toString() { + return "{location=" + mLocation + ", source=" + mImage + + ", buffer=" + mImage.getHardwareBuffer() + "}"; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ImageTileSet.java b/packages/SystemUI/src/com/android/systemui/screenshot/ImageTileSet.java new file mode 100644 index 000000000000..8ff66f548172 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ImageTileSet.java @@ -0,0 +1,163 @@ +/* + * 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.screenshot; + +import android.graphics.Bitmap; +import android.graphics.HardwareRenderer; +import android.graphics.RecordingCanvas; +import android.graphics.Rect; +import android.graphics.RenderNode; +import android.graphics.drawable.Drawable; + +import androidx.annotation.UiThread; + +import java.util.ArrayList; +import java.util.List; + +/** + * Owns a series of partial screen captures (tiles). + * <p> + * To display on-screen, use {@link #getDrawable()}. + */ +@UiThread +class ImageTileSet { + + private static final String TAG = "ImageTileSet"; + + interface OnBoundsChangedListener { + /** + * Reports an update to the bounding box that contains all active tiles. These are virtual + * (capture) coordinates which can be either negative or positive. + */ + void onBoundsChanged(int left, int top, int right, int bottom); + } + + interface OnContentChangedListener { + /** + * Mark as dirty and rebuild display list. + */ + void onContentChanged(); + } + + private final List<ImageTile> mTiles = new ArrayList<>(); + private final Rect mBounds = new Rect(); + + private OnContentChangedListener mOnContentChangedListener; + private OnBoundsChangedListener mOnBoundsChangedListener; + + void setOnBoundsChangedListener(OnBoundsChangedListener listener) { + mOnBoundsChangedListener = listener; + } + + void setOnContentChangedListener(OnContentChangedListener listener) { + mOnContentChangedListener = listener; + } + + void addTile(ImageTile tile) { + final Rect newBounds = new Rect(mBounds); + final Rect newRect = tile.getLocation(); + mTiles.add(tile); + newBounds.union(newRect); + if (!newBounds.equals(mBounds)) { + mBounds.set(newBounds); + if (mOnBoundsChangedListener != null) { + mOnBoundsChangedListener.onBoundsChanged( + newBounds.left, newBounds.top, newBounds.right, newBounds.bottom); + } + } + if (mOnContentChangedListener != null) { + mOnContentChangedListener.onContentChanged(); + } + } + + /** + * Returns a drawable to paint the combined contents of the tiles. Drawable dimensions are + * zero-based and map directly to {@link #getLeft()}, {@link #getTop()}, {@link #getRight()}, + * and {@link #getBottom()} which are dimensions relative to the capture start position + * (positive or negative). + * + * @return a drawable to display the image content + */ + Drawable getDrawable() { + return new TiledImageDrawable(this); + } + + boolean isEmpty() { + return mTiles.isEmpty(); + } + + int size() { + return mTiles.size(); + } + + ImageTile get(int i) { + return mTiles.get(i); + } + + Bitmap toBitmap() { + if (mTiles.isEmpty()) { + return null; + } + final RenderNode output = new RenderNode("Bitmap Export"); + output.setPosition(0, 0, getWidth(), getHeight()); + RecordingCanvas canvas = output.beginRecording(); + canvas.translate(-getLeft(), -getTop()); + for (ImageTile tile : mTiles) { + canvas.save(); + canvas.translate(tile.getLeft(), tile.getTop()); + canvas.drawRenderNode(tile.getDisplayList()); + canvas.restore(); + } + output.endRecording(); + return HardwareRenderer.createHardwareBitmap(output, getWidth(), getHeight()); + } + + int getLeft() { + return mBounds.left; + } + + int getTop() { + return mBounds.top; + } + + int getRight() { + return mBounds.right; + } + + int getBottom() { + return mBounds.bottom; + } + + int getWidth() { + return mBounds.width(); + } + + int getHeight() { + return mBounds.height(); + } + + void clear() { + mBounds.set(0, 0, 0, 0); + mTiles.forEach(ImageTile::close); + mTiles.clear(); + if (mOnBoundsChangedListener != null) { + mOnBoundsChangedListener.onBoundsChanged(0, 0, 0, 0); + } + if (mOnContentChangedListener != null) { + mOnContentChangedListener.onContentChanged(); + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureClient.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureClient.java index e159992bc9a5..bb07012f2355 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureClient.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureClient.java @@ -18,6 +18,7 @@ package com.android.systemui.screenshot; import static com.android.systemui.screenshot.LogConfig.DEBUG_SCROLL; +import static java.lang.Math.min; import static java.util.Objects.requireNonNull; import android.annotation.UiContext; @@ -44,15 +45,20 @@ import java.util.function.Consumer; import javax.inject.Inject; /** - * High level interface to scroll capture API. + * High(er) level interface to scroll capture API. */ public class ScrollCaptureClient { + private static final int TILE_SIZE_PX_MAX = 4 * (1024 * 1024); + private static final int TILES_PER_PAGE = 2; // increase once b/174571735 is addressed + private static final int MAX_PAGES = 5; + private static final int MAX_IMAGE_COUNT = MAX_PAGES * TILES_PER_PAGE; @VisibleForTesting static final int MATCH_ANY_TASK = ActivityTaskManager.INVALID_TASK_ID; private static final String TAG = LogConfig.logTag(ScrollCaptureClient.class); + /** * A connection to a remote window. Starts a capture session. */ @@ -60,13 +66,10 @@ public class ScrollCaptureClient { /** * Session start should be deferred until UI is active because of resource allocation and * potential visible side effects in the target window. - * - * @param maxBuffers the maximum number of buffers (tiles) that may be in use at one - * time, tiles are not cached anywhere so set this to a large enough - * number to retain offscreen content until it is no longer needed + * @param sessionConsumer listener to receive the session once active */ - void start(int maxBuffers, Consumer<Session> sessionConsumer); + void start(Consumer<Session> sessionConsumer); /** * Close the connection. @@ -100,26 +103,33 @@ public class ScrollCaptureClient { */ interface Session { /** - * Request the given horizontal strip. Values are y-coordinates in captured space, relative - * to start position. + * Request an image tile at the given position, from top, to top + {@link #getTileHeight()}, + * and from left 0, to {@link #getPageWidth()} * - * @param contentRect the area to capture, in content rect space, relative to scroll-bounds + * @param top the top (y) position of the tile to capture, in content rect space * @param consumer listener to be informed of the result */ - void requestTile(Rect contentRect, Consumer<CaptureResult> consumer); + void requestTile(int top, Consumer<CaptureResult> consumer); /** - * End the capture session, return the target app to original state. The returned - * stage must be waited for to complete to allow the target app a chance to restore to - * original state before becoming visible. + * Returns the maximum number of tiles which may be requested and retained without + * being {@link Image#close() closed}. * - * @return a stage presenting the session shutdown + * @return the maximum number of open tiles allowed */ - void end(Runnable listener); + int getMaxTiles(); + + int getTileHeight(); + + int getPageHeight(); - int getMaxTileHeight(); + int getPageWidth(); - int getMaxTileWidth(); + /** + * End the capture session, return the target app to original state. The listener + * will be called when the target app is ready to before visible and interactive. + */ + void end(Runnable listener); } private final IWindowManager mWindowManagerService; @@ -131,6 +141,12 @@ public class ScrollCaptureClient { mWindowManagerService = windowManagerService; } + /** + * Set the window token for the screenshot window/ This is required to avoid targeting our + * window or any above it. + * + * @param token the windowToken of the screenshot window + */ public void setHostWindowToken(IBinder token) { mHostWindowToken = token; } @@ -176,6 +192,8 @@ public class ScrollCaptureClient { private ImageReader mReader; private Rect mScrollBounds; + private int mTileHeight; + private int mTileWidth; private Rect mRequestRect; private boolean mStarted; @@ -197,6 +215,15 @@ public class ScrollCaptureClient { mScrollBounds = scrollBounds; mConnectionConsumer.accept(this); mConnectionConsumer = null; + + int pxPerPage = mScrollBounds.width() * mScrollBounds.height(); + int pxPerTile = min(TILE_SIZE_PX_MAX, (pxPerPage / TILES_PER_PAGE)); + mTileWidth = mScrollBounds.width(); + mTileHeight = pxPerTile / mScrollBounds.width(); + if (DEBUG_SCROLL) { + Log.d(TAG, "scrollBounds: " + mScrollBounds); + Log.d(TAG, "tile dimen: " + mTileWidth + "x" + mTileHeight); + } } @Override @@ -257,24 +284,19 @@ public class ScrollCaptureClient { // ScrollCaptureController.Connection - // -> Error handling: BiConsumer<Session, Throwable> ? @Override - public void start(int maxBufferCount, Consumer<Session> sessionConsumer) { + public void start(Consumer<Session> sessionConsumer) { if (DEBUG_SCROLL) { - Log.d(TAG, "start(maxBufferCount=" + maxBufferCount - + ", sessionConsumer=" + sessionConsumer + ")"); + Log.d(TAG, "start(sessionConsumer=" + sessionConsumer + ")"); } - mReader = ImageReader.newInstance(mScrollBounds.width(), mScrollBounds.height(), - PixelFormat.RGBA_8888, maxBufferCount, HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE); + mReader = ImageReader.newInstance(mTileWidth, mTileHeight, PixelFormat.RGBA_8888, + MAX_IMAGE_COUNT, HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE); mSessionConsumer = sessionConsumer; try { mConnection.startCapture(mReader.getSurface()); mStarted = true; } catch (RemoteException e) { - Log.w(TAG, "should not be happening :-("); - // ? - //mSessionListener.onError(e); - //mSessionListener = null; + Log.w(TAG, "Failed to start", e); } } @@ -307,27 +329,36 @@ public class ScrollCaptureClient { } @Override - public int getMaxTileHeight() { + public int getPageHeight() { return mScrollBounds.height(); } @Override - public int getMaxTileWidth() { + public int getPageWidth() { return mScrollBounds.width(); } @Override - public void requestTile(Rect contentRect, Consumer<CaptureResult> consumer) { + public int getTileHeight() { + return mTileHeight; + } + + @Override + public int getMaxTiles() { + return MAX_IMAGE_COUNT; + } + + @Override + public void requestTile(int top, Consumer<CaptureResult> consumer) { if (DEBUG_SCROLL) { - Log.d(TAG, "requestTile(contentRect=" + contentRect + "consumer=" + consumer + ")"); + Log.d(TAG, "requestTile(top=" + top + ", consumer=" + consumer + ")"); } - mRequestRect = new Rect(contentRect); + mRequestRect = new Rect(0, top, mTileWidth, top + mTileHeight); mResultConsumer = consumer; try { mConnection.requestImage(mRequestRect); } catch (RemoteException e) { Log.e(TAG, "Caught remote exception from requestImage", e); - // ? } } diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java index 27c74ac0938d..c75efbcc5f80 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java @@ -16,16 +16,9 @@ package com.android.systemui.screenshot; -import static android.graphics.ColorSpace.Named.SRGB; - import android.content.Context; import android.content.Intent; import android.graphics.Bitmap; -import android.graphics.Canvas; -import android.graphics.ColorSpace; -import android.graphics.Picture; -import android.graphics.Rect; -import android.media.Image; import android.net.Uri; import android.os.UserHandle; import android.util.Log; @@ -46,6 +39,8 @@ import java.util.function.Consumer; public class ScrollCaptureController { private static final String TAG = "ScrollCaptureController"; + private static final boolean USE_TILED_IMAGE = false; + public static final int MAX_PAGES = 5; public static final int MAX_HEIGHT = 12000; @@ -55,7 +50,7 @@ public class ScrollCaptureController { private final Executor mUiExecutor; private final Executor mBgExecutor; private final ImageExporter mImageExporter; - private Picture mPicture; + private final ImageTileSet mImageTileSet; public ScrollCaptureController(Context context, Connection connection, Executor uiExecutor, Executor bgExecutor, ImageExporter exporter) { @@ -64,6 +59,7 @@ public class ScrollCaptureController { mUiExecutor = uiExecutor; mBgExecutor = bgExecutor; mImageExporter = exporter; + mImageTileSet = new ImageTileSet(); } /** @@ -72,50 +68,50 @@ public class ScrollCaptureController { * @param after action to take after the flow is complete */ public void run(final Runnable after) { - mConnection.start(MAX_PAGES, (session) -> startCapture(session, after)); + mConnection.start((session) -> startCapture(session, after)); } private void startCapture(Session session, final Runnable onDismiss) { - Rect requestRect = new Rect(0, 0, - session.getMaxTileWidth(), session.getMaxTileHeight()); Consumer<ScrollCaptureClient.CaptureResult> consumer = new Consumer<ScrollCaptureClient.CaptureResult>() { int mFrameCount = 0; + int mTop = 0; @Override public void accept(ScrollCaptureClient.CaptureResult result) { mFrameCount++; + boolean emptyFrame = result.captured.height() == 0; if (!emptyFrame) { - mPicture = stackBelow(mPicture, result.image, result.captured.width(), - result.captured.height()); + mImageTileSet.addTile(new ImageTile(result.image, result.captured)); } + if (emptyFrame || mFrameCount >= MAX_PAGES - || requestRect.bottom > MAX_HEIGHT) { - if (mPicture != null) { - exportToFile(mPicture, session, onDismiss); + || mTop + session.getTileHeight() > MAX_HEIGHT) { + if (!mImageTileSet.isEmpty()) { + exportToFile(mImageTileSet.toBitmap(), session, onDismiss); + mImageTileSet.clear(); } else { session.end(onDismiss); } return; } - requestRect.offset(0, session.getMaxTileHeight()); - session.requestTile(requestRect, /* consumer */ this); + mTop += result.captured.height(); + session.requestTile(mTop, /* consumer */ this); } }; // fire it up! - session.requestTile(requestRect, consumer); + session.requestTile(0, consumer); }; - void exportToFile(Picture picture, Session session, Runnable afterEnd) { + void exportToFile(Bitmap bitmap, Session session, Runnable afterEnd) { mImageExporter.setFormat(Bitmap.CompressFormat.PNG); mImageExporter.setQuality(6); ListenableFuture<Uri> future = - mImageExporter.export(mBgExecutor, Bitmap.createBitmap(picture)); + mImageExporter.export(mBgExecutor, bitmap); future.addListener(() -> { - picture.close(); // release resources try { launchViewer(future.get()); } catch (InterruptedException | ExecutionException e) { @@ -126,41 +122,6 @@ public class ScrollCaptureController { }, mUiExecutor); } - /** - * Combine the top {@link Picture} with an {@link Image} by appending the image directly - * below, creating a result that is the combined height of both. - * <p> - * Note: no pixel data is transferred here, only a record of drawing commands. Backing - * hardware buffers must not be modified/recycled until the picture is - * {@link Picture#close closed}. - * - * @param top the existing picture - * @param below the image to append below - * @param cropWidth the width of the pixel data to use from the image - * @param cropHeight the height of the pixel data to use from the image - * - * @return a new Picture which draws the previous picture with the image below it - */ - private static Picture stackBelow(Picture top, Image below, int cropWidth, int cropHeight) { - int width = cropWidth; - int height = cropHeight; - if (top != null) { - height += top.getHeight(); - width = Math.max(width, top.getWidth()); - } - Picture combined = new Picture(); - Canvas canvas = combined.beginRecording(width, height); - int y = 0; - if (top != null) { - canvas.drawPicture(top, new Rect(0, 0, top.getWidth(), top.getHeight())); - y += top.getHeight(); - } - canvas.drawBitmap(Bitmap.wrapHardwareBuffer( - below.getHardwareBuffer(), ColorSpace.get(SRGB)), 0, y, null); - combined.endRecording(); - return combined; - } - void launchViewer(Uri uri) { Intent editIntent = new Intent(Intent.ACTION_VIEW); editIntent.setType("image/png"); diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TiledImageDrawable.java b/packages/SystemUI/src/com/android/systemui/screenshot/TiledImageDrawable.java new file mode 100644 index 000000000000..72f489bdd398 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/screenshot/TiledImageDrawable.java @@ -0,0 +1,117 @@ +/* + * 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.screenshot; + +import android.annotation.Nullable; +import android.graphics.Canvas; +import android.graphics.ColorFilter; +import android.graphics.PixelFormat; +import android.graphics.Rect; +import android.graphics.RenderNode; +import android.graphics.drawable.Drawable; +import android.util.Log; + +/** + * Draws a set of hardware image tiles from a display list. The tiles exist in virtual coordinate + * space that may extend into positive or negative values. The origin is the upper-left-most corner + * of bounding box, which is drawn at 0,0 to the drawable (output) bounds. + */ +public class TiledImageDrawable extends Drawable { + + private static final String TAG = "TiledImageDrawable"; + + private final ImageTileSet mTiles; + private RenderNode mNode; + + public TiledImageDrawable(ImageTileSet tiles) { + mTiles = tiles; + mTiles.setOnContentChangedListener(this::onContentChanged); + } + + private void onContentChanged() { + if (mNode != null && mNode.hasDisplayList()) { + mNode.discardDisplayList(); + } + invalidateSelf(); + } + + private void rebuildDisplayListIfNeeded() { + if (mNode != null && mNode.hasDisplayList()) { + return; + } + if (mNode == null) { + mNode = new RenderNode("TiledImageDrawable"); + } + mNode.setPosition(0, 0, mTiles.getWidth(), mTiles.getHeight()); + Canvas canvas = mNode.beginRecording(mTiles.getWidth(), mTiles.getHeight()); + // Align content (virtual) top/left with 0,0, within the render node + canvas.translate(-mTiles.getLeft(), -mTiles.getTop()); + for (int i = 0; i < mTiles.size(); i++) { + ImageTile tile = mTiles.get(i); + canvas.save(); + canvas.translate(tile.getLeft(), tile.getTop()); + canvas.drawRenderNode(tile.getDisplayList()); + canvas.restore(); + } + mNode.endRecording(); + } + + /** + * Draws the tiled image to the canvas, with the top/left (virtual) coordinate aligned to 0,0 + * placed at left/top of the drawable's bounds. + */ + @Override + public void draw(Canvas canvas) { + rebuildDisplayListIfNeeded(); + if (canvas.isHardwareAccelerated()) { + Rect bounds = getBounds(); + canvas.save(); + canvas.clipRect(bounds); + canvas.translate(bounds.left, bounds.top); + canvas.drawRenderNode(mNode); + canvas.restore(); + } else { + Log.d(TAG, "Canvas is not hardware accelerated. Skipping draw!"); + } + } + + @Override + public int getIntrinsicWidth() { + return mTiles.getWidth(); + } + + @Override + public int getIntrinsicHeight() { + return mTiles.getHeight(); + } + + @Override + public void setAlpha(int alpha) { + if (mNode.setAlpha(alpha / 255f)) { + invalidateSelf(); + } + } + + @Override + public void setColorFilter(@Nullable ColorFilter colorFilter) { + throw new IllegalArgumentException("not implemented"); + } + + @Override + public int getOpacity() { + return PixelFormat.OPAQUE; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGroupingUtil.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGroupingUtil.java index dbee0ee8f5d5..9ef304d7e83c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGroupingUtil.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGroupingUtil.java @@ -20,6 +20,8 @@ import android.app.Notification; import android.graphics.drawable.Drawable; import android.graphics.drawable.Icon; import android.text.TextUtils; +import android.util.DisplayMetrics; +import android.util.TypedValue; import android.view.NotificationHeaderView; import android.view.View; import android.view.ViewGroup; @@ -456,12 +458,14 @@ public class NotificationGroupingUtil { if (target == null) { return; } - Integer value = (Integer) target.getTag(iconVisible + final Integer data = (Integer) target.getTag(iconVisible ? com.android.internal.R.id.tag_margin_end_when_icon_visible : com.android.internal.R.id.tag_margin_end_when_icon_gone); - if (value == null) { + if (data == null) { return; } + final DisplayMetrics metrics = target.getResources().getDisplayMetrics(); + final int value = TypedValue.complexToDimensionPixelOffset(data, metrics); if (target instanceof NotificationHeaderView) { ((NotificationHeaderView) target).setTopLineExtraMarginEnd(value); } else { diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java index 125b5d4c7b8d..4d3af9c01153 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java @@ -24,6 +24,7 @@ import com.android.wm.shell.WindowManagerShellWrapper; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.SystemWindows; import com.android.wm.shell.common.TaskStackListenerImpl; +import com.android.wm.shell.legacysplitscreen.LegacySplitScreen; import com.android.wm.shell.pip.Pip; import com.android.wm.shell.pip.PipBoundsAlgorithm; import com.android.wm.shell.pip.PipBoundsState; @@ -32,11 +33,8 @@ import com.android.wm.shell.pip.PipSurfaceTransactionHelper; import com.android.wm.shell.pip.PipTaskOrganizer; import com.android.wm.shell.pip.PipUiEventLogger; import com.android.wm.shell.pip.tv.PipController; -import com.android.wm.shell.pip.tv.PipControlsView; -import com.android.wm.shell.pip.tv.PipControlsViewController; import com.android.wm.shell.pip.tv.PipNotification; import com.android.wm.shell.pip.tv.TvPipMenuController; -import com.android.wm.shell.legacysplitscreen.LegacySplitScreen; import java.util.Optional; @@ -75,19 +73,6 @@ public abstract class TvPipModule { @WMSingleton @Provides - static PipControlsViewController providePipControlsViewController( - PipControlsView pipControlsView, PipController pipController) { - return new PipControlsViewController(pipControlsView, pipController); - } - - @WMSingleton - @Provides - static PipControlsView providePipControlsView(Context context) { - return new PipControlsView(context, null); - } - - @WMSingleton - @Provides static PipNotification providePipNotification(Context context, PipMediaController pipMediaController) { return new PipNotification(context, pipMediaController); @@ -108,9 +93,12 @@ public abstract class TvPipModule { @WMSingleton @Provides - static TvPipMenuController providesPipTvMenuController(Context context, - PipBoundsState pipBoundsState, SystemWindows systemWindows) { - return new TvPipMenuController(context, pipBoundsState, systemWindows); + static TvPipMenuController providesPipTvMenuController( + Context context, + PipBoundsState pipBoundsState, + SystemWindows systemWindows, + PipMediaController pipMediaController) { + return new TvPipMenuController(context, pipBoundsState, systemWindows, pipMediaController); } @WMSingleton diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java index 765fd32f7cd2..1049548c382c 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java @@ -23,6 +23,7 @@ import android.view.IWindowManager; import com.android.systemui.dagger.WMSingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.wm.shell.ShellTaskOrganizer; +import com.android.wm.shell.Transitions; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.DisplayImeController; import com.android.wm.shell.common.SyncTransactionQueue; @@ -58,9 +59,10 @@ public class TvWMShellModule { DisplayController displayController, SystemWindows systemWindows, DisplayImeController displayImeController, @Main Handler handler, TransactionPool transactionPool, ShellTaskOrganizer shellTaskOrganizer, - SyncTransactionQueue syncQueue, TaskStackListenerImpl taskStackListener) { + SyncTransactionQueue syncQueue, TaskStackListenerImpl taskStackListener, + Transitions transitions) { return new LegacySplitScreenController(context, displayController, systemWindows, displayImeController, handler, transactionPool, shellTaskOrganizer, syncQueue, - taskStackListener); + taskStackListener, transitions); } } diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java index 3399a0bfc72e..9ec7657c5dd5 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java @@ -23,6 +23,7 @@ import android.view.IWindowManager; import com.android.systemui.dagger.WMSingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.wm.shell.ShellTaskOrganizer; +import com.android.wm.shell.Transitions; import com.android.wm.shell.WindowManagerShellWrapper; import com.android.wm.shell.apppairs.AppPairs; import com.android.wm.shell.apppairs.AppPairsController; @@ -35,6 +36,8 @@ import com.android.wm.shell.common.SystemWindows; import com.android.wm.shell.common.TaskStackListenerImpl; import com.android.wm.shell.common.TransactionPool; import com.android.wm.shell.common.annotations.ShellMainThread; +import com.android.wm.shell.legacysplitscreen.LegacySplitScreen; +import com.android.wm.shell.legacysplitscreen.LegacySplitScreenController; import com.android.wm.shell.pip.Pip; import com.android.wm.shell.pip.PipBoundsAlgorithm; import com.android.wm.shell.pip.PipBoundsState; @@ -46,8 +49,6 @@ import com.android.wm.shell.pip.phone.PhonePipMenuController; import com.android.wm.shell.pip.phone.PipAppOpsListener; import com.android.wm.shell.pip.phone.PipController; import com.android.wm.shell.pip.phone.PipTouchHandler; -import com.android.wm.shell.legacysplitscreen.LegacySplitScreen; -import com.android.wm.shell.legacysplitscreen.LegacySplitScreenController; import java.util.Optional; import java.util.concurrent.Executor; @@ -76,10 +77,11 @@ public class WMShellModule { DisplayController displayController, SystemWindows systemWindows, DisplayImeController displayImeController, @Main Handler handler, TransactionPool transactionPool, ShellTaskOrganizer shellTaskOrganizer, - SyncTransactionQueue syncQueue, TaskStackListenerImpl taskStackListener) { + SyncTransactionQueue syncQueue, TaskStackListenerImpl taskStackListener, + Transitions transitions) { return new LegacySplitScreenController(context, displayController, systemWindows, displayImeController, handler, transactionPool, shellTaskOrganizer, syncQueue, - taskStackListener); + taskStackListener, transitions); } @WMSingleton diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaResumeListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaResumeListenerTest.kt index 5d81de6bce00..59c2b176f12e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaResumeListenerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaResumeListenerTest.kt @@ -33,6 +33,7 @@ import android.testing.TestableLooper import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.broadcast.BroadcastDispatcher +import com.android.systemui.dump.DumpManager import com.android.systemui.tuner.TunerService import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.time.FakeSystemClock @@ -85,6 +86,7 @@ class MediaResumeListenerTest : SysuiTestCase() { @Mock private lateinit var sharedPrefsEditor: SharedPreferences.Editor @Mock private lateinit var mockContext: Context @Mock private lateinit var pendingIntent: PendingIntent + @Mock private lateinit var dumpManager: DumpManager @Captor lateinit var callbackCaptor: ArgumentCaptor<ResumeMediaBrowser.Callback> @@ -120,7 +122,7 @@ class MediaResumeListenerTest : SysuiTestCase() { executor = FakeExecutor(FakeSystemClock()) resumeListener = MediaResumeListener(mockContext, broadcastDispatcher, executor, - tunerService, resumeBrowserFactory) + tunerService, resumeBrowserFactory, dumpManager) resumeListener.setManager(mediaDataManager) mediaDataManager.addListener(resumeListener) @@ -159,7 +161,7 @@ class MediaResumeListenerTest : SysuiTestCase() { // When listener is created, we do NOT register a user change listener val listener = MediaResumeListener(context, broadcastDispatcher, executor, tunerService, - resumeBrowserFactory) + resumeBrowserFactory, dumpManager) listener.setManager(mediaDataManager) verify(broadcastDispatcher, never()).registerReceiver(eq(listener.userChangeReceiver), any(), any(), any()) diff --git a/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.java b/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.java index b3176ddeaf65..4d32a3b4077f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.java @@ -40,8 +40,8 @@ import com.android.systemui.shared.recents.IPinnedStackAnimationListener; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.phone.StatusBar; -import com.android.wm.shell.pip.Pip; import com.android.wm.shell.legacysplitscreen.LegacySplitScreen; +import com.android.wm.shell.pip.Pip; import org.junit.Before; import org.junit.Test; diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureClientTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureClientTest.java index 4aa730efa91e..c1c637129d85 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureClientTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureClientTest.java @@ -96,13 +96,13 @@ public class ScrollCaptureClientTest extends SysuiTestCase { Connection conn = mConnectionConsumer.getValue(); - conn.start(5, mSessionConsumer); + conn.start(mSessionConsumer); verify(mSessionConsumer, timeout(100)).accept(any(Session.class)); Session session = mSessionConsumer.getValue(); - Rect request = new Rect(0, 0, session.getMaxTileWidth(), session.getMaxTileHeight()); + Rect request = new Rect(0, 0, session.getPageWidth(), session.getTileHeight()); - session.requestTile(request, mResultConsumer); + session.requestTile(0, mResultConsumer); verify(mResultConsumer, timeout(100)).accept(any(CaptureResult.class)); CaptureResult result = mResultConsumer.getValue(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java index 73d87b0d4596..31bf7120900f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java @@ -35,12 +35,12 @@ import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.tracing.ProtoTracer; import com.android.wm.shell.ShellCommandHandler; import com.android.wm.shell.hidedisplaycutout.HideDisplayCutout; +import com.android.wm.shell.legacysplitscreen.LegacySplitScreen; import com.android.wm.shell.onehanded.OneHanded; import com.android.wm.shell.onehanded.OneHandedGestureHandler; import com.android.wm.shell.onehanded.OneHandedTransitionCallback; import com.android.wm.shell.pip.Pip; import com.android.wm.shell.pip.phone.PipTouchHandler; -import com.android.wm.shell.legacysplitscreen.LegacySplitScreen; import org.junit.Before; import org.junit.Test; diff --git a/services/core/java/com/android/server/IntentResolver.java b/services/core/java/com/android/server/IntentResolver.java index ea1ac0c3fddc..2906ceebca58 100644 --- a/services/core/java/com/android/server/IntentResolver.java +++ b/services/core/java/com/android/server/IntentResolver.java @@ -839,14 +839,22 @@ public abstract class IntentResolver<F, R extends Object> { } }; - // Make <this> a copy of <orig>. The presumption is that <this> is empty. - protected void doCopy(IntentResolver orig) { + // Make <this> a copy of <orig>. The presumption is that <this> is empty but all + // arrays are cleared out explicitly, just to be sure. + protected void copyFrom(IntentResolver orig) { + mFilters.clear(); mFilters.addAll(orig.mFilters); + mTypeToFilter.clear(); mTypeToFilter.putAll(orig.mTypeToFilter); + mBaseTypeToFilter.clear(); mBaseTypeToFilter.putAll(orig.mBaseTypeToFilter); + mWildTypeToFilter.clear(); mWildTypeToFilter.putAll(orig.mWildTypeToFilter); + mSchemeToFilter.clear(); mSchemeToFilter.putAll(orig.mSchemeToFilter); + mActionToFilter.clear(); mActionToFilter.putAll(orig.mActionToFilter); + mTypedActionToFilter.clear(); mTypedActionToFilter.putAll(orig.mTypedActionToFilter); } diff --git a/services/core/java/com/android/server/utils/WatchableIntentResolver.java b/services/core/java/com/android/server/WatchableIntentResolver.java index 767fc07d8cad..2ef94f17e5d9 100644 --- a/services/core/java/com/android/server/utils/WatchableIntentResolver.java +++ b/services/core/java/com/android/server/WatchableIntentResolver.java @@ -14,12 +14,14 @@ * limitations under the License. */ -package com.android.server.utils; +package com.android.server; import android.annotation.NonNull; import android.annotation.Nullable; -import com.android.server.IntentResolver; +import com.android.server.utils.Watchable; +import com.android.server.utils.WatchableImpl; +import com.android.server.utils.Watcher; import java.util.List; @@ -37,28 +39,45 @@ public abstract class WatchableIntentResolver<F, R extends Object> * Watchable machinery */ private final Watchable mWatchable = new WatchableImpl(); + /** * Register an observer to receive change notifications. * @param observer The observer to register. */ + @Override public void registerObserver(@NonNull Watcher observer) { mWatchable.registerObserver(observer); } + /** * Unregister the observer, which will no longer receive change notifications. * @param observer The observer to unregister. */ + @Override public void unregisterObserver(@NonNull Watcher observer) { mWatchable.unregisterObserver(observer); } + + /** + * Return true if the {@link Watcher) is a registered observer. + * @param observer A {@link Watcher} that might be registered + * @return true if the observer is registered with this {@link Watchable}. + */ + @Override + public boolean isRegisteredObserver(@NonNull Watcher observer) { + return mWatchable.isRegisteredObserver(observer); + } + /** * Notify listeners that the object has changd. The argument is a hint as to the * source of the change. * @param what The attribute or sub-object that changed, if not null. */ + @Override public void dispatchChange(@Nullable Watchable what) { mWatchable.dispatchChange(what); } + /** * Notify listeners that this object has changed. */ diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java index 15e31ba0f140..117098315ef7 100644 --- a/services/core/java/com/android/server/accounts/AccountManagerService.java +++ b/services/core/java/com/android/server/accounts/AccountManagerService.java @@ -4756,7 +4756,7 @@ public class AccountManagerService IAccountManagerResponse getResponseAndClose() { if (mResponse == null) { - // this session has already been closed + close(); return null; } IAccountManagerResponse response = mResponse; diff --git a/services/core/java/com/android/server/apphibernation/OWNERS b/services/core/java/com/android/server/apphibernation/OWNERS new file mode 100644 index 000000000000..4804fa3eb915 --- /dev/null +++ b/services/core/java/com/android/server/apphibernation/OWNERS @@ -0,0 +1,3 @@ +# TODO: Include /core/java/android/apphibernation/OWNERS. See b/177005153 +kevhan@google.com +rajekumar@google.com diff --git a/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java b/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java index 9898d7676178..493c68862567 100644 --- a/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java @@ -56,9 +56,9 @@ public abstract class AcquisitionClient<T> extends ClientMonitor<T> implements I public AcquisitionClient(@NonNull Context context, @NonNull LazyDaemon<T> lazyDaemon, @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int userId, @NonNull String owner, int cookie, int sensorId, int statsModality, - int statsAction, int statsClient) { + int statsAction, int statsClient, boolean shouldLogMetrics) { super(context, lazyDaemon, token, listener, userId, owner, cookie, sensorId, statsModality, - statsAction, statsClient); + statsAction, statsClient, shouldLogMetrics); mPowerManager = context.getSystemService(PowerManager.class); mSuccessVibrationEffect = VibrationEffect.get(VibrationEffect.EFFECT_CLICK); mErrorVibrationEffect = VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK); 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 0943c9ca17fe..52923283f22c 100644 --- a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java @@ -20,7 +20,6 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManager; import android.app.ActivityTaskManager; -import android.app.IActivityTaskManager; import android.app.TaskStackListener; import android.content.ComponentName; import android.content.Context; @@ -67,7 +66,8 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T> int statsModality, int statsClient, @Nullable TaskStackListener taskStackListener, @NonNull LockoutTracker lockoutTracker) { super(context, lazyDaemon, token, listener, targetUserId, owner, cookie, sensorId, - statsModality, BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient); + statsModality, BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient, + true /* shouldLogMetrics */); mIsStrongBiometric = isStrongBiometric; mOperationId = operationId; mRequireConfirmation = requireConfirmation; diff --git a/services/core/java/com/android/server/biometrics/sensors/ClientMonitor.java b/services/core/java/com/android/server/biometrics/sensors/ClientMonitor.java index bbd652357888..27c2dd069dce 100644 --- a/services/core/java/com/android/server/biometrics/sensors/ClientMonitor.java +++ b/services/core/java/com/android/server/biometrics/sensors/ClientMonitor.java @@ -105,8 +105,8 @@ public abstract class ClientMonitor<T> extends LoggableMonitor implements IBinde public ClientMonitor(@NonNull Context context, @NonNull LazyDaemon<T> lazyDaemon, @Nullable IBinder token, @Nullable ClientMonitorCallbackConverter listener, int userId, @NonNull String owner, int cookie, int sensorId, int statsModality, int statsAction, - int statsClient) { - super(statsModality, statsAction, statsClient); + int statsClient, boolean shouldLogMetrics) { + super(statsModality, statsAction, statsClient, shouldLogMetrics); mSequentialId = sCount++; mContext = context; mLazyDaemon = lazyDaemon; diff --git a/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java index 8bf9680d60cd..fbe3d8469d81 100644 --- a/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java @@ -49,10 +49,10 @@ public abstract class EnrollClient<T> extends AcquisitionClient<T> { @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int userId, @NonNull byte[] hardwareAuthToken, @NonNull String owner, @NonNull BiometricUtils utils, int timeoutSec, int statsModality, int sensorId, - boolean shouldVibrate) { + boolean shouldVibrate, boolean shouldLogMetrics) { super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId, statsModality, BiometricsProtoEnums.ACTION_ENROLL, - BiometricsProtoEnums.CLIENT_UNKNOWN); + BiometricsProtoEnums.CLIENT_UNKNOWN, shouldLogMetrics); mBiometricUtils = utils; mHardwareAuthToken = Arrays.copyOf(hardwareAuthToken, hardwareAuthToken.length); mTimeoutSec = timeoutSec; diff --git a/services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java index bac944fca1de..f6a1040f6a7b 100644 --- a/services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java @@ -32,7 +32,7 @@ public abstract class GenerateChallengeClient<T> extends ClientMonitor<T> { @NonNull String owner, int sensorId) { super(context, lazyDaemon, token, listener, 0 /* userId */, owner, 0 /* cookie */, sensorId, BiometricsProtoEnums.MODALITY_UNKNOWN, BiometricsProtoEnums.ACTION_UNKNOWN, - BiometricsProtoEnums.CLIENT_UNKNOWN); + BiometricsProtoEnums.CLIENT_UNKNOWN, true /* shouldLogMetrics */); } @Override diff --git a/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java b/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java index e738d17f5f17..e56e116f6e1d 100644 --- a/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java @@ -107,7 +107,8 @@ public abstract class InternalCleanupClient<S extends BiometricAuthenticator.Ide @NonNull Map<Integer, Long> authenticatorIds) { super(context, lazyDaemon, null /* token */, null /* ClientMonitorCallbackConverter */, userId, owner, 0 /* cookie */, sensorId, statsModality, - BiometricsProtoEnums.ACTION_ENUMERATE, BiometricsProtoEnums.CLIENT_UNKNOWN); + BiometricsProtoEnums.ACTION_ENUMERATE, BiometricsProtoEnums.CLIENT_UNKNOWN, + true /* shouldLogMetrics */); mBiometricUtils = utils; mAuthenticatorIds = authenticatorIds; mEnrolledList = enrolledList; diff --git a/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java b/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java index e07f71298d13..158b836c84cd 100644 --- a/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java @@ -51,7 +51,7 @@ public abstract class InternalEnumerateClient<T> extends ClientMonitor<T> // is all done internally. super(context, lazyDaemon, token, null /* ClientMonitorCallbackConverter */, userId, owner, 0 /* cookie */, sensorId, statsModality, BiometricsProtoEnums.ACTION_ENUMERATE, - BiometricsProtoEnums.CLIENT_UNKNOWN); + BiometricsProtoEnums.CLIENT_UNKNOWN, true /* shouldLogMetrics */); mEnrolledList = enrolledList; mUtils = utils; } diff --git a/services/core/java/com/android/server/biometrics/sensors/LoggableMonitor.java b/services/core/java/com/android/server/biometrics/sensors/LoggableMonitor.java index d85ab25cc812..59e40da47578 100644 --- a/services/core/java/com/android/server/biometrics/sensors/LoggableMonitor.java +++ b/services/core/java/com/android/server/biometrics/sensors/LoggableMonitor.java @@ -38,6 +38,7 @@ public abstract class LoggableMonitor { private final int mStatsAction; private final int mStatsClient; private long mFirstAcquireTimeMs; + private boolean mShouldLogMetrics; /** * Only valid for AuthenticationClient. @@ -51,11 +52,14 @@ public abstract class LoggableMonitor { * @param statsModality One of {@link BiometricsProtoEnums} MODALITY_* constants. * @param statsAction One of {@link BiometricsProtoEnums} ACTION_* constants. * @param statsClient One of {@link BiometricsProtoEnums} CLIENT_* constants. + * @param shouldLogMetrics If set to false, metrics will not be reported to statsd. */ - public LoggableMonitor(int statsModality, int statsAction, int statsClient) { + public LoggableMonitor(int statsModality, int statsAction, int statsClient, + boolean shouldLogMetrics) { mStatsModality = statsModality; mStatsAction = statsAction; mStatsClient = statsClient; + mShouldLogMetrics = shouldLogMetrics; } public int getStatsClient() { @@ -70,6 +74,9 @@ public abstract class LoggableMonitor { protected final void logOnAcquired(Context context, int acquiredInfo, int vendorCode, int targetUserId) { + if (!mShouldLogMetrics) { + return; + } final boolean isFace = mStatsModality == BiometricsProtoEnums.MODALITY_FACE; final boolean isFingerprint = mStatsModality == BiometricsProtoEnums.MODALITY_FINGERPRINT; @@ -110,6 +117,10 @@ public abstract class LoggableMonitor { protected final void logOnError(Context context, int error, int vendorCode, int targetUserId) { + if (!mShouldLogMetrics) { + return; + } + final long latency = mFirstAcquireTimeMs != 0 ? (System.currentTimeMillis() - mFirstAcquireTimeMs) : -1; @@ -144,6 +155,10 @@ public abstract class LoggableMonitor { protected final void logOnAuthenticated(Context context, boolean authenticated, boolean requireConfirmation, int targetUserId, boolean isBiometricPrompt) { + if (!mShouldLogMetrics) { + return; + } + int authState = FrameworkStatsLog.BIOMETRIC_AUTHENTICATED__STATE__UNKNOWN; if (!authenticated) { authState = FrameworkStatsLog.BIOMETRIC_AUTHENTICATED__STATE__REJECTED; @@ -189,6 +204,10 @@ public abstract class LoggableMonitor { } protected final void logOnEnrolled(int targetUserId, long latency, boolean enrollSuccessful) { + if (!mShouldLogMetrics) { + return; + } + if (DEBUG) { Slog.v(TAG, "Enrolled! Modality: " + mStatsModality + ", User: " + targetUserId diff --git a/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java b/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java index f79abd59dbb4..22b57170e494 100644 --- a/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java @@ -45,7 +45,7 @@ public abstract class RemovalClient<S extends BiometricAuthenticator.Identifier, int sensorId, @NonNull Map<Integer, Long> authenticatorIds, int statsModality) { super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId, statsModality, BiometricsProtoEnums.ACTION_REMOVE, - BiometricsProtoEnums.CLIENT_UNKNOWN); + BiometricsProtoEnums.CLIENT_UNKNOWN, true /* shouldLogMetrics */); mBiometricId = biometricId; mBiometricUtils = utils; mAuthenticatorIds = authenticatorIds; diff --git a/services/core/java/com/android/server/biometrics/sensors/RevokeChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/RevokeChallengeClient.java index 5deb8fa26639..dcbd4b5d7768 100644 --- a/services/core/java/com/android/server/biometrics/sensors/RevokeChallengeClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/RevokeChallengeClient.java @@ -27,7 +27,8 @@ public abstract class RevokeChallengeClient<T> extends ClientMonitor<T> { @NonNull IBinder token, @NonNull String owner, int sensorId) { super(context, lazyDaemon, token, null /* listener */, 0 /* userId */, owner, 0 /* cookie */, sensorId, BiometricsProtoEnums.MODALITY_UNKNOWN, - BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN); + BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN, + true /* shouldLogMetrics */); } @Override diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java index f09df1e1812e..99b89e365b99 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java @@ -62,7 +62,7 @@ public class FaceEnrollClient extends EnrollClient<ISession> { @Nullable NativeHandle previewSurface, int sensorId, int maxTemplatesPerUser) { super(context, lazyDaemon, token, listener, userId, hardwareAuthToken, opPackageName, utils, timeoutSec, BiometricsProtoEnums.MODALITY_FACE, sensorId, - false /* shouldVibrate */); + false /* shouldVibrate */, true /* shouldLogMetrics */); mEnrollIgnoreList = getContext().getResources() .getIntArray(R.array.config_face_acquire_enroll_ignorelist); mEnrollIgnoreListVendor = getContext().getResources() diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetAuthenticatorIdClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetAuthenticatorIdClient.java index c27b6e5a4b7d..ce0bb4566ea3 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetAuthenticatorIdClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetAuthenticatorIdClient.java @@ -38,7 +38,8 @@ class FaceGetAuthenticatorIdClient extends ClientMonitor<ISession> { Map<Integer, Long> authenticatorIds) { super(context, lazyDaemon, null /* token */, null /* listener */, userId, opPackageName, 0 /* cookie */, sensorId, BiometricsProtoEnums.MODALITY_FACE, - BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN); + BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN, + true /* shouldLogMetrics */); mAuthenticatorIds = authenticatorIds; } diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java index 5b1f5465ea0a..500099522d0b 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java @@ -50,7 +50,8 @@ public class FaceResetLockoutClient extends ClientMonitor<ISession> { @NonNull LockoutResetDispatcher lockoutResetDispatcher) { super(context, lazyDaemon, null /* token */, null /* listener */, userId, owner, 0 /* cookie */, sensorId, BiometricsProtoEnums.MODALITY_UNKNOWN, - BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN); + BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN, + true /* shouldLogMetrics */); mHardwareAuthToken = HardwareAuthTokenUtils.toHardwareAuthToken(hardwareAuthToken); mLockoutCache = lockoutTracker; mLockoutResetDispatcher = lockoutResetDispatcher; diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceEnrollClient.java index 1a7544fc7f01..bc1eace3c9f7 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceEnrollClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceEnrollClient.java @@ -59,7 +59,7 @@ public class FaceEnrollClient extends EnrollClient<IBiometricsFace> { @Nullable NativeHandle surfaceHandle, int sensorId) { super(context, lazyDaemon, token, listener, userId, hardwareAuthToken, owner, utils, timeoutSec, BiometricsProtoEnums.MODALITY_FACE, sensorId, - false /* shouldVibrate */); + false /* shouldVibrate */, true /* shouldLogMetrics */); mDisabledFeatures = Arrays.copyOf(disabledFeatures, disabledFeatures.length); mSurfaceHandle = surfaceHandle; mEnrollIgnoreList = getContext().getResources() diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceGetFeatureClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceGetFeatureClient.java index e25bb812caa6..3758774e8d13 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceGetFeatureClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceGetFeatureClient.java @@ -47,7 +47,7 @@ public class FaceGetFeatureClient extends ClientMonitor<IBiometricsFace> { @NonNull String owner, int sensorId, int feature, int faceId) { super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId, BiometricsProtoEnums.MODALITY_UNKNOWN, BiometricsProtoEnums.ACTION_UNKNOWN, - BiometricsProtoEnums.CLIENT_UNKNOWN); + BiometricsProtoEnums.CLIENT_UNKNOWN, true /* shouldLogMetrics */); mFeature = feature; mFaceId = faceId; diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceResetLockoutClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceResetLockoutClient.java index 8df9b9f305de..06808e0c30da 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceResetLockoutClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceResetLockoutClient.java @@ -42,7 +42,8 @@ public class FaceResetLockoutClient extends ClientMonitor<IBiometricsFace> { @NonNull byte[] hardwareAuthToken) { super(context, lazyDaemon, null /* token */, null /* listener */, userId, owner, 0 /* cookie */, sensorId, BiometricsProtoEnums.MODALITY_UNKNOWN, - BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN); + BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN, + true /* shouldLogMetrics */); mHardwareAuthToken = new ArrayList<>(); for (byte b : hardwareAuthToken) { diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceSetFeatureClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceSetFeatureClient.java index 0e2072823684..b2db6c7d24bc 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceSetFeatureClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceSetFeatureClient.java @@ -49,7 +49,7 @@ public class FaceSetFeatureClient extends ClientMonitor<IBiometricsFace> { byte[] hardwareAuthToken, int faceId) { super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId, BiometricsProtoEnums.MODALITY_UNKNOWN, BiometricsProtoEnums.ACTION_UNKNOWN, - BiometricsProtoEnums.CLIENT_UNKNOWN); + BiometricsProtoEnums.CLIENT_UNKNOWN, true /* shouldLogMetrics */); mFeature = feature; mEnabled = enabled; mFaceId = faceId; diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceUpdateActiveUserClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceUpdateActiveUserClient.java index 22275e5f9d32..c9d2cd378c3a 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceUpdateActiveUserClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceUpdateActiveUserClient.java @@ -43,7 +43,8 @@ public class FaceUpdateActiveUserClient extends ClientMonitor<IBiometricsFace> { @NonNull Map<Integer, Long> authenticatorIds) { super(context, lazyDaemon, null /* token */, null /* listener */, userId, owner, 0 /* cookie */, sensorId, BiometricsProtoEnums.MODALITY_UNKNOWN, - BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN); + BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN, + true /* shouldLogMetrics */); mCurrentUserId = currentUserId; mHasEnrolledBiometrics = hasEnrolledBIometrics; mAuthenticatorIds = authenticatorIds; diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java index 61f9cc40d233..e6cdbd28f9d1 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java @@ -66,10 +66,10 @@ import com.android.internal.widget.LockPatternUtils; import com.android.server.ServiceThread; import com.android.server.SystemService; import com.android.server.biometrics.Utils; +import com.android.server.biometrics.sensors.BiometricServiceCallback; import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter; import com.android.server.biometrics.sensors.LockoutResetDispatcher; import com.android.server.biometrics.sensors.LockoutTracker; -import com.android.server.biometrics.sensors.BiometricServiceCallback; import com.android.server.biometrics.sensors.fingerprint.aidl.FingerprintProvider; import com.android.server.biometrics.sensors.fingerprint.hidl.Fingerprint21; import com.android.server.biometrics.sensors.fingerprint.hidl.Fingerprint21UdfpsMock; @@ -188,7 +188,8 @@ public class FingerprintService extends SystemService implements BiometricServic @Override // Binder call public void enroll(final IBinder token, final byte[] hardwareAuthToken, final int userId, - final IFingerprintServiceReceiver receiver, final String opPackageName) { + final IFingerprintServiceReceiver receiver, final String opPackageName, + boolean shouldLogMetrics) { Utils.checkPermission(getContext(), MANAGE_FINGERPRINT); final Pair<Integer, ServiceProvider> provider = getSingleProvider(); @@ -198,7 +199,7 @@ public class FingerprintService extends SystemService implements BiometricServic } provider.second.scheduleEnroll(provider.first, token, hardwareAuthToken, userId, - receiver, opPackageName); + receiver, opPackageName, shouldLogMetrics); } @Override // Binder call diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java index 6e36cd24b2e7..d94c98481eeb 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java @@ -76,7 +76,8 @@ public interface ServiceProvider { @NonNull String opPackageName, long challenge); void scheduleEnroll(int sensorId, @NonNull IBinder token, byte[] hardwareAuthToken, int userId, - @NonNull IFingerprintServiceReceiver receiver, @NonNull String opPackageName); + @NonNull IFingerprintServiceReceiver receiver, @NonNull String opPackageName, + boolean shouldLogMetrics); void cancelEnrollment(int sensorId, @NonNull IBinder token); diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java index 3f9aef2b8651..e95447b49872 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java @@ -121,7 +121,7 @@ class BiometricTestSessionImpl extends ITestSession.Stub { Utils.checkPermission(mContext, TEST_BIOMETRIC); mProvider.scheduleEnroll(mSensorId, new Binder(), new byte[69], userId, mReceiver, - mContext.getOpPackageName()); + mContext.getOpPackageName(), true /* shouldLogMetrics */); } @Override diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java index 339832373b74..483ca5ddec3f 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java @@ -51,7 +51,7 @@ class FingerprintDetectClient extends AcquisitionClient<ISession> { int statsClient) { super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId, BiometricsProtoEnums.MODALITY_FINGERPRINT, BiometricsProtoEnums.ACTION_AUTHENTICATE, - statsClient); + statsClient, true /* shouldLogMetrics */); mIsStrongBiometric = isStrongBiometric; mUdfpsOverlayController = udfpsOverlayController; } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java index 3e13c45d335e..b4f705405fa9 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java @@ -51,10 +51,11 @@ class FingerprintEnrollClient extends EnrollClient<ISession> implements Udfps { @NonNull ClientMonitorCallbackConverter listener, int userId, @NonNull byte[] hardwareAuthToken, @NonNull String owner, @NonNull BiometricUtils<Fingerprint> utils, int sensorId, - @Nullable IUdfpsOverlayController udfpsOvelayController, int maxTemplatesPerUser) { + @Nullable IUdfpsOverlayController udfpsOvelayController, int maxTemplatesPerUser, + boolean shouldLogMetrics) { super(context, lazyDaemon, token, listener, userId, hardwareAuthToken, owner, utils, 0 /* timeoutSec */, BiometricsProtoEnums.MODALITY_FINGERPRINT, sensorId, - true /* shouldVibrate */); + true /* shouldVibrate */, shouldLogMetrics); mUdfpsOverlayController = udfpsOvelayController; mMaxTemplatesPerUser = maxTemplatesPerUser; } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGetAuthenticatorIdClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGetAuthenticatorIdClient.java index 2ad1fa306781..b30cc1a5f50a 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGetAuthenticatorIdClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGetAuthenticatorIdClient.java @@ -38,7 +38,8 @@ class FingerprintGetAuthenticatorIdClient extends ClientMonitor<ISession> { int sensorId, Map<Integer, Long> authenticatorIds) { super(context, lazyDaemon, null /* token */, null /* listener */, userId, owner, 0 /* cookie */, sensorId, BiometricsProtoEnums.MODALITY_FINGERPRINT, - BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN); + BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN, + true /* shouldLogMetrics */); mAuthenticatorIds = authenticatorIds; } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java index 99c662a57c3b..a03debadf36c 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java @@ -20,7 +20,6 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManager; import android.app.ActivityTaskManager; -import android.app.IActivityTaskManager; import android.app.TaskStackListener; import android.content.Context; import android.content.pm.UserInfo; @@ -348,7 +347,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi @Override public void scheduleEnroll(int sensorId, @NonNull IBinder token, byte[] hardwareAuthToken, int userId, @NonNull IFingerprintServiceReceiver receiver, - @NonNull String opPackageName) { + @NonNull String opPackageName, boolean shouldLogMetrics) { mHandler.post(() -> { final IFingerprint daemon = getHalInstance(); if (daemon == null) { @@ -370,7 +369,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi mSensors.get(sensorId).getLazySession(), token, new ClientMonitorCallbackConverter(receiver), userId, hardwareAuthToken, opPackageName, FingerprintUtils.getInstance(sensorId), sensorId, - mUdfpsOverlayController, maxTemplatesPerUser); + mUdfpsOverlayController, maxTemplatesPerUser, shouldLogMetrics); scheduleForSensor(sensorId, client, new ClientMonitor.Callback() { @Override public void onClientFinished(@NonNull ClientMonitor<?> clientMonitor, diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java index 1f1d19d07121..093c9449c498 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java @@ -50,7 +50,8 @@ class FingerprintResetLockoutClient extends ClientMonitor<ISession> { @NonNull LockoutResetDispatcher lockoutResetDispatcher) { super(context, lazyDaemon, null /* token */, null /* listener */, userId, owner, 0 /* cookie */, sensorId, BiometricsProtoEnums.MODALITY_UNKNOWN, - BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN); + BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN, + true /* shouldLogMetrics */); mHardwareAuthToken = HardwareAuthTokenUtils.toHardwareAuthToken(hardwareAuthToken); mLockoutCache = lockoutTracker; mLockoutResetDispatcher = lockoutResetDispatcher; diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java index 74549b917e82..95c4cee7e59e 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java @@ -122,7 +122,7 @@ public class BiometricTestSessionImpl extends ITestSession.Stub { Utils.checkPermission(mContext, TEST_BIOMETRIC); mFingerprint21.scheduleEnroll(mSensorId, new Binder(), new byte[69], userId, mReceiver, - mContext.getOpPackageName()); + mContext.getOpPackageName(), true/* shouldLogMetrics */); } @Override diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java index b8d27aa61806..f5ce8943c188 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java @@ -20,7 +20,6 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManager; import android.app.ActivityTaskManager; -import android.app.IActivityTaskManager; import android.app.SynchronousUserSwitchObserver; import android.app.TaskStackListener; import android.app.UserSwitchObserver; @@ -548,14 +547,16 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider @Override public void scheduleEnroll(int sensorId, @NonNull IBinder token, @NonNull byte[] hardwareAuthToken, int userId, - @NonNull IFingerprintServiceReceiver receiver, @NonNull String opPackageName) { + @NonNull IFingerprintServiceReceiver receiver, @NonNull String opPackageName, + boolean shouldLogMetrics) { mHandler.post(() -> { scheduleUpdateActiveUserWithoutHandler(userId); final FingerprintEnrollClient client = new FingerprintEnrollClient(mContext, mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), userId, hardwareAuthToken, opPackageName, FingerprintUtils.getLegacyInstance(mSensorId), - ENROLL_TIMEOUT_SEC, mSensorProperties.sensorId, mUdfpsOverlayController); + ENROLL_TIMEOUT_SEC, mSensorProperties.sensorId, mUdfpsOverlayController, + shouldLogMetrics); mScheduler.scheduleClientMonitor(client, new ClientMonitor.Callback() { @Override public void onClientFinished(@NonNull ClientMonitor<?> clientMonitor, diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java index 4747488e5a70..c00dd048eeda 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java @@ -56,7 +56,7 @@ class FingerprintDetectClient extends AcquisitionClient<IBiometricsFingerprint> boolean isStrongBiometric, int statsClient) { super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId, BiometricsProtoEnums.MODALITY_FINGERPRINT, BiometricsProtoEnums.ACTION_AUTHENTICATE, - statsClient); + statsClient, true /* shouldLogMetrics */); mUdfpsOverlayController = udfpsOverlayController; mIsStrongBiometric = isStrongBiometric; } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java index af61a8be3052..a261b0b8fcdf 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java @@ -52,10 +52,11 @@ public class FingerprintEnrollClient extends EnrollClient<IBiometricsFingerprint @NonNull ClientMonitorCallbackConverter listener, int userId, @NonNull byte[] hardwareAuthToken, @NonNull String owner, @NonNull BiometricUtils<Fingerprint> utils, int timeoutSec, int sensorId, - @Nullable IUdfpsOverlayController udfpsOverlayController) { + @Nullable IUdfpsOverlayController udfpsOverlayController, + boolean shouldLogMetrics) { super(context, lazyDaemon, token, listener, userId, hardwareAuthToken, owner, utils, timeoutSec, BiometricsProtoEnums.MODALITY_FINGERPRINT, sensorId, - true /* shouldVibrate */); + true /* shouldVibrate */, shouldLogMetrics); mUdfpsOverlayController = udfpsOverlayController; } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintUpdateActiveUserClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintUpdateActiveUserClient.java index 00e2413c2f38..c785eeff6d9c 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintUpdateActiveUserClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintUpdateActiveUserClient.java @@ -50,7 +50,8 @@ public class FingerprintUpdateActiveUserClient extends ClientMonitor<IBiometrics @NonNull Map<Integer, Long> authenticatorIds) { super(context, lazyDaemon, null /* token */, null /* listener */, userId, owner, 0 /* cookie */, sensorId, BiometricsProtoEnums.MODALITY_UNKNOWN, - BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN); + BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN, + true /* shouldLogMetrics */); mCurrentUserId = currentUserId; mHasEnrolledBiometrics = hasEnrolledBiometrics; mAuthenticatorIds = authenticatorIds; diff --git a/services/core/java/com/android/server/content/OWNERS b/services/core/java/com/android/server/content/OWNERS new file mode 100644 index 000000000000..b6a9fe869ffa --- /dev/null +++ b/services/core/java/com/android/server/content/OWNERS @@ -0,0 +1 @@ +include /services/core/java/com/android/server/am/OWNERS
\ No newline at end of file diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java index eb61a1c2ad40..fa063b223250 100644 --- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java +++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java @@ -46,6 +46,7 @@ import com.android.internal.BrightnessSynchronizer; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.os.BackgroundThread; import com.android.server.EventLogTags; +import com.android.server.display.DisplayDeviceConfig.HighBrightnessModeData; import java.io.PrintWriter; @@ -195,6 +196,9 @@ class AutomaticBrightnessController { private boolean mShortTermModelValid; private float mShortTermModelAnchor; + // Controls High Brightness Mode. + private HighBrightnessModeController mHbmController; + // Context-sensitive brightness configurations require keeping track of the foreground app's // package name and category, which is done by registering a TaskStackListener to call back to // us onTaskStackChanged, and then using the ActivityTaskManager to get the foreground app's @@ -216,12 +220,12 @@ class AutomaticBrightnessController { float dozeScaleFactor, int lightSensorRate, int initialLightSensorRate, long brighteningLightDebounceConfig, long darkeningLightDebounceConfig, boolean resetAmbientLuxAfterWarmUpConfig, HysteresisLevels ambientBrightnessThresholds, - HysteresisLevels screenBrightnessThresholds, Context context) { + HysteresisLevels screenBrightnessThresholds, LogicalDisplay display, Context context) { this(new Injector(), callbacks, looper, sensorManager, lightSensor, mapper, lightSensorWarmUpTime, brightnessMin, brightnessMax, dozeScaleFactor, lightSensorRate, initialLightSensorRate, brighteningLightDebounceConfig, darkeningLightDebounceConfig, resetAmbientLuxAfterWarmUpConfig, - ambientBrightnessThresholds, screenBrightnessThresholds, context + ambientBrightnessThresholds, screenBrightnessThresholds, display, context ); } @@ -232,7 +236,7 @@ class AutomaticBrightnessController { float dozeScaleFactor, int lightSensorRate, int initialLightSensorRate, long brighteningLightDebounceConfig, long darkeningLightDebounceConfig, boolean resetAmbientLuxAfterWarmUpConfig, HysteresisLevels ambientBrightnessThresholds, - HysteresisLevels screenBrightnessThresholds, Context context) { + HysteresisLevels screenBrightnessThresholds, LogicalDisplay display, Context context) { mInjector = injector; mContext = context; mCallbacks = callbacks; @@ -269,6 +273,20 @@ class AutomaticBrightnessController { mPendingForegroundAppPackageName = null; mForegroundAppCategory = ApplicationInfo.CATEGORY_UNDEFINED; mPendingForegroundAppCategory = ApplicationInfo.CATEGORY_UNDEFINED; + + final DisplayDeviceConfig ddConfig = + display.getPrimaryDisplayDeviceLocked().getDisplayDeviceConfig(); + HighBrightnessModeData hbmData = + ddConfig != null ? ddConfig.getHighBrightnessModeData() : null; + + final Runnable hbmChangeCallback = () -> { + updateAutoBrightness(true /*sendUpdate*/, false /*userInitiatedChange*/); + // TODO: b/175937645 - Callback to DisplayManagerService to indicate a change to the HBM + // allowance has been made so that the brightness limits can be calculated + // appropriately. + }; + mHbmController = new HighBrightnessModeController(mHandler, brightnessMin, brightnessMax, + hbmData, hbmChangeCallback); } /** @@ -538,6 +556,7 @@ class AutomaticBrightnessController { mAmbientLux = lux; mAmbientBrighteningThreshold = mAmbientBrightnessThresholds.getBrighteningThreshold(lux); mAmbientDarkeningThreshold = mAmbientBrightnessThresholds.getDarkeningThreshold(lux); + mHbmController.onAmbientLuxChange(mAmbientLux); // If the short term model was invalidated and the change is drastic enough, reset it. if (!mShortTermModelValid && mShortTermModelAnchor != -1) { @@ -751,6 +770,7 @@ class AutomaticBrightnessController { mScreenBrightnessThresholds.getBrighteningThreshold(newScreenAutoBrightness)); mScreenDarkeningThreshold = clampScreenBrightness( mScreenBrightnessThresholds.getDarkeningThreshold(newScreenAutoBrightness)); + mHbmController.onAutoBrightnessChanged(mScreenAutoBrightness); if (sendUpdate) { mCallbacks.updateBrightness(); @@ -761,7 +781,7 @@ class AutomaticBrightnessController { // Clamps values with float range [0.0-1.0] private float clampScreenBrightness(float value) { return MathUtils.constrain(value, - mScreenBrightnessRangeMinimum, mScreenBrightnessRangeMaximum); + mHbmController.getCurrentBrightnessMin(), mHbmController.getCurrentBrightnessMax()); } private void prepareBrightnessAdjustmentSample() { diff --git a/services/core/java/com/android/server/display/DisplayDevice.java b/services/core/java/com/android/server/display/DisplayDevice.java index 3516981c92a6..cd17cfef2726 100644 --- a/services/core/java/com/android/server/display/DisplayDevice.java +++ b/services/core/java/com/android/server/display/DisplayDevice.java @@ -37,7 +37,6 @@ abstract class DisplayDevice { private final DisplayAdapter mDisplayAdapter; private final IBinder mDisplayToken; private final String mUniqueId; - private DisplayDeviceConfig mDisplayDeviceConfig; // The display device does not manage these properties itself, they are set by // the display manager service. The display device shouldn't really be looking at these. @@ -71,12 +70,11 @@ abstract class DisplayDevice { /* * Gets the DisplayDeviceConfig for this DisplayDevice. - * Returns null for this device but is overridden in LocalDisplayDevice. * - * @return The DisplayDeviceConfig. + * @return The DisplayDeviceConfig; {@code null} if not overridden. */ public DisplayDeviceConfig getDisplayDeviceConfig() { - return mDisplayDeviceConfig; + return null; } /** diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java index 0a30e07165f9..68708d36f259 100644 --- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java +++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java @@ -16,6 +16,7 @@ package com.android.server.display; +import android.annotation.NonNull; import android.content.Context; import android.os.Environment; import android.os.PowerManager; @@ -25,6 +26,8 @@ import android.view.DisplayAddress; import com.android.internal.BrightnessSynchronizer; import com.android.server.display.config.DisplayConfiguration; import com.android.server.display.config.DisplayQuirks; +import com.android.server.display.config.HbmTiming; +import com.android.server.display.config.HighBrightnessMode; import com.android.server.display.config.NitsMap; import com.android.server.display.config.Point; import com.android.server.display.config.XmlParser; @@ -61,19 +64,20 @@ public class DisplayDeviceConfig { private static final String STABLE_ID_SUFFIX_FORMAT = "id_%d"; private static final String NO_SUFFIX_FORMAT = "%d"; private static final long STABLE_FLAG = 1L << 62; - // Float.NaN (used as invalid for brightness) cannot be stored in config.xml // so -2 is used instead private static final float INVALID_BRIGHTNESS_IN_CONFIG = -2f; + private final Context mContext; + private float[] mNits; private float[] mBrightness; private float mBrightnessMinimum = Float.NaN; private float mBrightnessMaximum = Float.NaN; private float mBrightnessDefault = Float.NaN; private List<String> mQuirks; - - private final Context mContext; + private boolean mIsHighBrightnessModeEnabled = false; + private HighBrightnessModeData mHbmData; private DisplayDeviceConfig(Context context) { mContext = context; @@ -182,6 +186,19 @@ public class DisplayDeviceConfig { return mQuirks != null && mQuirks.contains(quirkValue); } + /** + * @return high brightness mode configuration data for the display. + */ + public HighBrightnessModeData getHighBrightnessModeData() { + if (!mIsHighBrightnessModeEnabled || mHbmData == null) { + return null; + } + + HighBrightnessModeData hbmData = new HighBrightnessModeData(); + mHbmData.copyTo(hbmData); + return hbmData; + } + @Override public String toString() { String str = "DisplayDeviceConfig{" @@ -191,10 +208,16 @@ public class DisplayDeviceConfig { + ", mBrightnessMaximum=" + mBrightnessMaximum + ", mBrightnessDefault=" + mBrightnessDefault + ", mQuirks=" + mQuirks + + ", isHbmEnabled=" + mIsHighBrightnessModeEnabled + + ", mHbmData=" + mHbmData + "}"; return str; } + private float getMaxBrightness() { + return mBrightness[mBrightness.length - 1]; + } + private static DisplayDeviceConfig getConfigFromSuffix(Context context, File baseDirectory, String suffixFormat, long idNumber) { @@ -240,6 +263,7 @@ public class DisplayDeviceConfig { loadBrightnessMap(config); loadBrightnessDefaultFromDdcXml(config); loadBrightnessConstraintsFromConfigXml(); + loadHighBrightnessModeData(config); loadQuirks(config); } else { Slog.w(TAG, "DisplayDeviceConfig file is null"); @@ -353,4 +377,66 @@ public class DisplayDeviceConfig { mQuirks = new ArrayList<>(quirks.getQuirk()); } } + + private void loadHighBrightnessModeData(DisplayConfiguration config) { + final HighBrightnessMode hbm = config.getHighBrightnessMode(); + if (hbm != null) { + mIsHighBrightnessModeEnabled = hbm.getEnabled(); + mHbmData = new HighBrightnessModeData(); + mHbmData.minimumLux = hbm.getMinimumLux_all().floatValue(); + mHbmData.transitionPoint = hbm.getTransitionPoint_all().floatValue(); + if (mHbmData.transitionPoint >= getMaxBrightness()) { + throw new IllegalArgumentException("HBM transition point invalid. " + + mHbmData.transitionPoint + " is not less than " + + getMaxBrightness()); + } + final HbmTiming hbmTiming = hbm.getTiming_all(); + mHbmData.timeWindowMillis = hbmTiming.getTimeWindowSecs_all().longValue() * 1000; + mHbmData.timeMaxMillis = hbmTiming.getTimeMaxSecs_all().longValue() * 1000; + mHbmData.timeMinMillis = hbmTiming.getTimeMinSecs_all().longValue() * 1000; + } + } + + /** + * Container for high brightness mode configuration data. + */ + static class HighBrightnessModeData { + /** Minimum lux needed to enter high brightness mode */ + public float minimumLux; + + /** Brightness level at which we transition from normal to high-brightness. */ + public float transitionPoint; + + /** Time window for HBM. */ + public long timeWindowMillis; + + /** Maximum time HBM is allowed to be during in a {@code timeWindowMillis}. */ + public long timeMaxMillis; + + /** Minimum time that HBM can be on before being enabled. */ + public long timeMinMillis; + + /** + * Copies the HBM data to the specified parameter instance. + * @param other the instance to copy data to. + */ + public void copyTo(@NonNull HighBrightnessModeData other) { + other.minimumLux = minimumLux; + other.transitionPoint = transitionPoint; + other.timeWindowMillis = timeWindowMillis; + other.timeMaxMillis = timeMaxMillis; + other.timeMinMillis = timeMinMillis; + } + + @Override + public String toString() { + return "HBM{" + + "minLux: " + minimumLux + + ", transition: " + transitionPoint + + ", timeWindow: " + timeWindowMillis + "ms" + + ", timeMax: " + timeMaxMillis + "ms" + + ", timeMin: " + timeMinMillis + + "} "; + } + } } diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index 6fa244e2d9ee..60e4595a7679 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -1100,7 +1100,7 @@ public final class DisplayManagerService extends SystemService { recordStableDisplayStatsIfNeededLocked(display); recordTopInsetLocked(display); } - addDisplayPowerControllerLocked(displayId); + addDisplayPowerControllerLocked(display); mDisplayStates.append(displayId, Display.STATE_OFF); mDisplayBrightnesses.append(displayId, display.getDisplayInfoLocked().brightnessDefault); @@ -1132,6 +1132,11 @@ public final class DisplayManagerService extends SystemService { // this point. sendDisplayEventLocked(displayId, DisplayManagerGlobal.EVENT_DISPLAY_CHANGED); scheduleTraversalLocked(false); + + DisplayPowerController dpc = mDisplayPowerControllers.get(displayId); + if (dpc != null) { + dpc.onDisplayChanged(); + } } private void handleLogicalDisplayFrameRateOverridesChangedLocked( @@ -1860,18 +1865,18 @@ public final class DisplayManagerService extends SystemService { private void initializeDisplayPowerControllersLocked() { mLogicalDisplayMapper.forEachLocked((logicalDisplay) -> addDisplayPowerControllerLocked( - logicalDisplay.getDisplayIdLocked())); + logicalDisplay)); } - private void addDisplayPowerControllerLocked(int displayId) { + private void addDisplayPowerControllerLocked(LogicalDisplay display) { if (mPowerHandler == null) { // initPowerManagement has not yet been called. return; } final DisplayPowerController displayPowerController = new DisplayPowerController( mContext, mDisplayPowerCallbacks, mPowerHandler, mSensorManager, - mDisplayBlanker, mLogicalDisplayMapper.getLocked(displayId)); - mDisplayPowerControllers.append(displayId, displayPowerController); + mDisplayBlanker, display); + mDisplayPowerControllers.append(display.getDisplayIdLocked(), displayPowerController); } private final class DisplayManagerHandler extends Handler { diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java index e31704f03cd9..811625b06cf0 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController.java +++ b/services/core/java/com/android/server/display/DisplayPowerController.java @@ -523,7 +523,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mScreenBrightnessRangeMaximum, dozeScaleFactor, lightSensorRate, initialLightSensorRate, brighteningLightDebounce, darkeningLightDebounce, autoBrightnessResetAmbientLuxAfterWarmUp, ambientBrightnessThresholds, - screenBrightnessThresholds, context); + screenBrightnessThresholds, logicalDisplay, context); } else { mUseSoftwareAutoBrightnessConfig = false; } @@ -674,6 +674,15 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call return mAutomaticBrightnessController.getDefaultConfig(); } + /** + * Notified when the display is changed. We use this to apply any changes that might be needed + * when displays get swapped on foldable devices. For example, different brightness properties + * of each display need to be properly reflected in AutomaticBrightnessController. + */ + public void onDisplayChanged() { + // TODO: b/175821789 - Support high brightness on multiple (folding) displays + } + private void sendUpdatePowerState() { synchronized (mLock) { sendUpdatePowerStateLocked(); diff --git a/services/core/java/com/android/server/display/HighBrightnessModeController.java b/services/core/java/com/android/server/display/HighBrightnessModeController.java new file mode 100644 index 000000000000..12b810f906f2 --- /dev/null +++ b/services/core/java/com/android/server/display/HighBrightnessModeController.java @@ -0,0 +1,260 @@ +/* + * 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.server.display; + +import android.os.Handler; +import android.os.PowerManager; +import android.os.SystemClock; +import android.util.Slog; + +import com.android.server.display.DisplayDeviceConfig.HighBrightnessModeData; + +import java.util.Iterator; +import java.util.LinkedList; + +/** + * Controls the status of high-brightness mode for devices that support it. This class assumes that + * an instance is always created even if a device does not support high-brightness mode (HBM); in + * the case where it is not supported, the majority of the logic is skipped. On devices that support + * HBM, we keep track of the ambient lux as well as historical usage of HBM to determine when HBM is + * allowed and not. This class's output is simply a brightness-range maximum value (queried via + * {@link #getCurrentBrightnessMax}) that changes depending on whether HBM is enabled or not. + */ +class HighBrightnessModeController { + private static final String TAG = "HighBrightnessModeController"; + + private static final boolean DEBUG_HBM = false; + + private final float mBrightnessMin; + private final float mBrightnessMax; + private final HighBrightnessModeData mHbmData; + private final Handler mHandler; + private final Runnable mHbmChangeCallback; + private final Runnable mRecalcRunnable; + + private boolean mIsInAllowedAmbientRange = false; + private boolean mIsTimeAvailable = false; + private float mAutoBrightness; + + /** + * If HBM is currently running, this is the start time for the current HBM session. + */ + private long mRunningStartTimeMillis = -1; + + /** + * List of previous HBM-events ordered from most recent to least recent. + * Meant to store only the events that fall into the most recent + * {@link mHbmData.timeWindowSecs}. + */ + private LinkedList<HbmEvent> mEvents = new LinkedList<>(); + + HighBrightnessModeController(Handler handler, float brightnessMin, float brightnessMax, + HighBrightnessModeData hbmData, Runnable hbmChangeCallback) { + mHandler = handler; + mBrightnessMin = brightnessMin; + mBrightnessMax = brightnessMax; + mHbmData = hbmData; + mHbmChangeCallback = hbmChangeCallback; + mAutoBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT; + + mRecalcRunnable = () -> { + boolean oldIsAllowed = isCurrentlyAllowed(); + recalculateTimeAllowance(); + if (oldIsAllowed != isCurrentlyAllowed()) { + // Our allowed state has changed; tell AutomaticBrightnessController + // to update the brightness. + if (mHbmChangeCallback != null) { + mHbmChangeCallback.run(); + } + } + }; + } + + float getCurrentBrightnessMin() { + return mBrightnessMin; + } + + float getCurrentBrightnessMax() { + if (!deviceSupportsHbm() || isCurrentlyAllowed()) { + // Either the device doesn't support HBM, or HBM range is currently allowed (device + // it in a high-lux environment). In either case, return the highest brightness + // level supported by the device. + return mBrightnessMax; + } else { + // Hbm is not allowed, only allow up to the brightness where we + // transition to high brightness mode. + return mHbmData.transitionPoint; + } + } + + void onAmbientLuxChange(float ambientLux) { + if (!deviceSupportsHbm()) { + return; + } + + final boolean isHighLux = (ambientLux >= mHbmData.minimumLux); + if (isHighLux != mIsInAllowedAmbientRange) { + mIsInAllowedAmbientRange = isHighLux; + recalculateTimeAllowance(); + } + } + + void onAutoBrightnessChanged(float autoBrightness) { + if (!deviceSupportsHbm()) { + return; + } + final float oldAutoBrightness = mAutoBrightness; + mAutoBrightness = autoBrightness; + + // If we are starting or ending a high brightness mode session, store the current + // session in mRunningStartTimeMillis, or the old one in mEvents. + final boolean wasOldBrightnessHigh = oldAutoBrightness > mHbmData.transitionPoint; + final boolean isNewBrightnessHigh = mAutoBrightness > mHbmData.transitionPoint; + if (wasOldBrightnessHigh != isNewBrightnessHigh) { + final long currentTime = SystemClock.uptimeMillis(); + if (isNewBrightnessHigh) { + mRunningStartTimeMillis = currentTime; + } else { + mEvents.addFirst(new HbmEvent(mRunningStartTimeMillis, currentTime)); + mRunningStartTimeMillis = -1; + + if (DEBUG_HBM) { + Slog.d(TAG, "New HBM event: " + mEvents.getFirst()); + } + } + } + + recalculateTimeAllowance(); + } + + private boolean isCurrentlyAllowed() { + return mIsTimeAvailable && mIsInAllowedAmbientRange; + } + + private boolean deviceSupportsHbm() { + return mHbmData != null; + } + + /** + * Recalculates the allowable HBM time. + */ + private void recalculateTimeAllowance() { + final long currentTime = SystemClock.uptimeMillis(); + long timeAlreadyUsed = 0; + + // First, lets see how much time we've taken for any currently running + // session of HBM. + if (mRunningStartTimeMillis > 0) { + if (mRunningStartTimeMillis > currentTime) { + Slog.e(TAG, "Start time set to the future. curr: " + currentTime + + ", start: " + mRunningStartTimeMillis); + mRunningStartTimeMillis = currentTime; + } + timeAlreadyUsed = currentTime - mRunningStartTimeMillis; + } + + if (DEBUG_HBM) { + Slog.d(TAG, "Time already used after current session: " + timeAlreadyUsed); + } + + // Next, lets iterate through the history of previous sessions and add those times. + final long windowstartTimeMillis = currentTime - mHbmData.timeWindowMillis; + Iterator<HbmEvent> it = mEvents.iterator(); + while (it.hasNext()) { + final HbmEvent event = it.next(); + + // If this event ended before the current Timing window, discard forever and ever. + if (event.endTimeMillis < windowstartTimeMillis) { + it.remove(); + continue; + } + + final long startTimeMillis = Math.max(event.startTimeMillis, windowstartTimeMillis); + timeAlreadyUsed += event.endTimeMillis - startTimeMillis; + } + + if (DEBUG_HBM) { + Slog.d(TAG, "Time already used after all sessions: " + timeAlreadyUsed); + } + + // See how much allowable time we have left. + final long remainingTime = Math.max(0, mHbmData.timeMaxMillis - timeAlreadyUsed); + + // We allow HBM if there is more than the minimum required time available + // or if brightness is already in the high range, if there is any time left at all. + final boolean isAllowedWithoutRestrictions = remainingTime >= mHbmData.timeMinMillis; + final boolean isOnlyAllowedToStayOn = !isAllowedWithoutRestrictions + && remainingTime > 0 && mAutoBrightness > mHbmData.transitionPoint; + mIsTimeAvailable = isAllowedWithoutRestrictions || isOnlyAllowedToStayOn; + + // Calculate the time at which we want to recalculate mIsTimeAvailable in case a lux or + // brightness change doesn't happen before then. + long nextTimeout = -1; + if (mAutoBrightness > mHbmData.transitionPoint) { + // if we're in high-lux now, timeout when we run out of allowed time. + nextTimeout = currentTime + remainingTime; + } else if (!mIsTimeAvailable && mEvents.size() > 0) { + // If we are not allowed...timeout when the oldest event moved outside of the timing + // window by at least minTime. Basically, we're calculating the soonest time we can + // get {@code timeMinMillis} back to us. + final HbmEvent lastEvent = mEvents.getLast(); + final long startTimePlusMinMillis = + Math.max(windowstartTimeMillis, lastEvent.startTimeMillis) + + mHbmData.timeMinMillis; + final long timeWhenMinIsGainedBack = + currentTime + (startTimePlusMinMillis - windowstartTimeMillis) - remainingTime; + nextTimeout = timeWhenMinIsGainedBack; + } + + if (DEBUG_HBM) { + Slog.d(TAG, "HBM recalculated. IsAllowedWithoutRestrictions: " + + isAllowedWithoutRestrictions + + ", isOnlyAllowedToStayOn: " + isOnlyAllowedToStayOn + + ", remainingAllowedTime: " + remainingTime + + ", isLuxHigh: " + mIsInAllowedAmbientRange + + ", isHBMCurrentlyAllowed: " + isCurrentlyAllowed() + + ", brightness: " + mAutoBrightness + + ", RunningStartTimeMillis: " + mRunningStartTimeMillis + + ", nextTimeout: " + (nextTimeout != -1 ? (nextTimeout - currentTime) : -1) + + ", events: " + mEvents); + } + + if (nextTimeout != -1) { + mHandler.removeCallbacks(mRecalcRunnable); + mHandler.postAtTime(mRecalcRunnable, nextTimeout); + } + } + + /** + * Represents an event in which High Brightness Mode was enabled. + */ + private static class HbmEvent { + public long startTimeMillis; + public long endTimeMillis; + + HbmEvent(long startTimeMillis, long endTimeMillis) { + this.startTimeMillis = startTimeMillis; + this.endTimeMillis = endTimeMillis; + } + + @Override + public String toString() { + return "[Event: {" + startTimeMillis + ", " + endTimeMillis + "}, total: " + + ((endTimeMillis - startTimeMillis) / 1000) + "]"; + } + } +} diff --git a/services/core/java/com/android/server/location/timezone/ControllerImpl.java b/services/core/java/com/android/server/location/timezone/ControllerImpl.java index 03ce8ecd1cc0..2b3c0bff8548 100644 --- a/services/core/java/com/android/server/location/timezone/ControllerImpl.java +++ b/services/core/java/com/android/server/location/timezone/ControllerImpl.java @@ -610,6 +610,38 @@ class ControllerImpl extends LocationTimeZoneProviderController { } } + /** + * Sets whether the controller should record provider state changes for later dumping via + * {@link #getStateForTests()}. + */ + void setProviderStateRecordingEnabled(boolean enabled) { + mThreadingDomain.assertCurrentThread(); + + synchronized (mSharedLock) { + mPrimaryProvider.setStateChangeRecordingEnabled(enabled); + mSecondaryProvider.setStateChangeRecordingEnabled(enabled); + } + } + + /** + * Returns a snapshot of the current controller state for tests. + */ + @NonNull + LocationTimeZoneManagerServiceState getStateForTests() { + mThreadingDomain.assertCurrentThread(); + + synchronized (mSharedLock) { + LocationTimeZoneManagerServiceState.Builder builder = + new LocationTimeZoneManagerServiceState.Builder(); + if (mLastSuggestion != null) { + builder.setLastSuggestion(mLastSuggestion); + } + builder.setPrimaryProviderStateChanges(mPrimaryProvider.getRecordedStates()) + .setSecondaryProviderStateChanges(mSecondaryProvider.getRecordedStates()); + return builder.build(); + } + } + @Nullable private LocationTimeZoneProvider getLocationTimeZoneProvider(@NonNull String providerName) { LocationTimeZoneProvider targetProvider; diff --git a/services/core/java/com/android/server/location/timezone/LocationTimeZoneManagerService.java b/services/core/java/com/android/server/location/timezone/LocationTimeZoneManagerService.java index 9f159fbe7b65..54535eb50130 100644 --- a/services/core/java/com/android/server/location/timezone/LocationTimeZoneManagerService.java +++ b/services/core/java/com/android/server/location/timezone/LocationTimeZoneManagerService.java @@ -17,11 +17,10 @@ package com.android.server.location.timezone; import static android.app.time.LocationTimeZoneManager.PRIMARY_PROVIDER_NAME; +import static android.app.time.LocationTimeZoneManager.PROVIDER_MODE_OVERRIDE_DISABLED; +import static android.app.time.LocationTimeZoneManager.PROVIDER_MODE_OVERRIDE_NONE; +import static android.app.time.LocationTimeZoneManager.PROVIDER_MODE_OVERRIDE_SIMULATED; import static android.app.time.LocationTimeZoneManager.SECONDARY_PROVIDER_NAME; -import static android.app.time.LocationTimeZoneManager.SYSTEM_PROPERTY_KEY_PROVIDER_MODE_OVERRIDE_PRIMARY; -import static android.app.time.LocationTimeZoneManager.SYSTEM_PROPERTY_KEY_PROVIDER_MODE_OVERRIDE_SECONDARY; -import static android.app.time.LocationTimeZoneManager.SYSTEM_PROPERTY_VALUE_PROVIDER_MODE_DISABLED; -import static android.app.time.LocationTimeZoneManager.SYSTEM_PROPERTY_VALUE_PROVIDER_MODE_SIMULATED; import android.annotation.NonNull; import android.annotation.Nullable; @@ -33,7 +32,6 @@ import android.os.Handler; import android.os.RemoteCallback; import android.os.ResultReceiver; import android.os.ShellCallback; -import android.os.SystemProperties; import android.service.timezone.TimeZoneProviderService; import android.util.IndentingPrintWriter; import android.util.Log; @@ -42,6 +40,7 @@ import android.util.Slog; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.internal.util.DumpUtils; +import com.android.internal.util.Preconditions; import com.android.server.FgThread; import com.android.server.SystemService; import com.android.server.timezonedetector.TimeZoneDetectorInternal; @@ -161,6 +160,14 @@ public class LocationTimeZoneManagerService extends Binder { @GuardedBy("mSharedLock") private ControllerEnvironmentImpl mEnvironment; + @GuardedBy("mSharedLock") + @NonNull + private String mPrimaryProviderModeOverride = PROVIDER_MODE_OVERRIDE_NONE; + + @GuardedBy("mSharedLock") + @NonNull + private String mSecondaryProviderModeOverride = PROVIDER_MODE_OVERRIDE_NONE; + LocationTimeZoneManagerService(Context context) { mContext = context.createAttributionContext(ATTRIBUTION_TAG); mHandler = FgThread.getHandler(); @@ -231,9 +238,9 @@ public class LocationTimeZoneManagerService extends Binder { private LocationTimeZoneProvider createPrimaryProvider() { LocationTimeZoneProviderProxy proxy; - if (isInSimulationMode(PRIMARY_PROVIDER_NAME)) { + if (isProviderInSimulationMode(PRIMARY_PROVIDER_NAME)) { proxy = new SimulatedLocationTimeZoneProviderProxy(mContext, mThreadingDomain); - } else if (isDisabled(PRIMARY_PROVIDER_NAME)) { + } else if (isProviderDisabled(PRIMARY_PROVIDER_NAME)) { proxy = new NullLocationTimeZoneProviderProxy(mContext, mThreadingDomain); } else { proxy = new RealLocationTimeZoneProviderProxy( @@ -250,9 +257,9 @@ public class LocationTimeZoneManagerService extends Binder { private LocationTimeZoneProvider createSecondaryProvider() { LocationTimeZoneProviderProxy proxy; - if (isInSimulationMode(SECONDARY_PROVIDER_NAME)) { + if (isProviderInSimulationMode(SECONDARY_PROVIDER_NAME)) { proxy = new SimulatedLocationTimeZoneProviderProxy(mContext, mThreadingDomain); - } else if (isDisabled(SECONDARY_PROVIDER_NAME)) { + } else if (isProviderDisabled(SECONDARY_PROVIDER_NAME)) { proxy = new NullLocationTimeZoneProviderProxy(mContext, mThreadingDomain); } else { proxy = new RealLocationTimeZoneProviderProxy( @@ -268,16 +275,14 @@ public class LocationTimeZoneManagerService extends Binder { } /** Used for bug triage and in tests to simulate provider events. */ - private static boolean isInSimulationMode(String providerName) { - return isProviderModeSetInSystemProperties(providerName, - SYSTEM_PROPERTY_VALUE_PROVIDER_MODE_SIMULATED); + private boolean isProviderInSimulationMode(String providerName) { + return isProviderModeOverrideSet(providerName, PROVIDER_MODE_OVERRIDE_SIMULATED); } /** Used for bug triage, tests and experiments to remove a provider. */ - private boolean isDisabled(String providerName) { + private boolean isProviderDisabled(String providerName) { return !isProviderEnabledInConfig(providerName) - || isProviderModeSetInSystemProperties( - providerName, SYSTEM_PROPERTY_VALUE_PROVIDER_MODE_DISABLED); + || isProviderModeOverrideSet(providerName, PROVIDER_MODE_OVERRIDE_DISABLED); } private boolean isProviderEnabledInConfig(String providerName) { @@ -299,25 +304,18 @@ public class LocationTimeZoneManagerService extends Binder { return resources.getBoolean(providerEnabledConfigId); } - private static boolean isProviderModeSetInSystemProperties( - @NonNull String providerName, @NonNull String mode) { - String systemPropertyKey; + private boolean isProviderModeOverrideSet(@NonNull String providerName, @NonNull String mode) { switch (providerName) { case PRIMARY_PROVIDER_NAME: { - systemPropertyKey = SYSTEM_PROPERTY_KEY_PROVIDER_MODE_OVERRIDE_PRIMARY; - break; + return Objects.equals(mPrimaryProviderModeOverride, mode); } case SECONDARY_PROVIDER_NAME: { - systemPropertyKey = SYSTEM_PROPERTY_KEY_PROVIDER_MODE_OVERRIDE_SECONDARY; - break; + return Objects.equals(mSecondaryProviderModeOverride, mode); } default: { throw new IllegalArgumentException(providerName); } } - - String systemPropertyProviderMode = SystemProperties.get(systemPropertyKey, null); - return Objects.equals(systemPropertyProviderMode, mode); } /** @@ -347,6 +345,67 @@ public class LocationTimeZoneManagerService extends Binder { this, in, out, err, args, callback, resultReceiver); } + /** Sets this service into provider state recording mode for tests. */ + void setProviderModeOverride(@NonNull String providerName, @NonNull String mode) { + enforceManageTimeZoneDetectorPermission(); + + Preconditions.checkArgument( + PRIMARY_PROVIDER_NAME.equals(providerName) + || SECONDARY_PROVIDER_NAME.equals(providerName)); + Preconditions.checkArgument(PROVIDER_MODE_OVERRIDE_DISABLED.equals(mode) + || PROVIDER_MODE_OVERRIDE_SIMULATED.equals(mode) + || PROVIDER_MODE_OVERRIDE_NONE.equals(mode)); + + mThreadingDomain.postAndWait(() -> { + synchronized (mSharedLock) { + switch (providerName) { + case PRIMARY_PROVIDER_NAME: { + mPrimaryProviderModeOverride = mode; + break; + } + case SECONDARY_PROVIDER_NAME: { + mSecondaryProviderModeOverride = mode; + break; + } + } + } + }, BLOCKING_OP_WAIT_DURATION_MILLIS); + } + + /** Sets this service into provider state recording mode for tests. */ + void setProviderStateRecordingEnabled(boolean enabled) { + enforceManageTimeZoneDetectorPermission(); + + mThreadingDomain.postAndWait(() -> { + synchronized (mSharedLock) { + if (mLocationTimeZoneDetectorController != null) { + mLocationTimeZoneDetectorController.setProviderStateRecordingEnabled(enabled); + } + } + }, BLOCKING_OP_WAIT_DURATION_MILLIS); + } + + /** Returns a snapshot of the current controller state for tests. */ + @NonNull + LocationTimeZoneManagerServiceState getStateForTests() { + enforceManageTimeZoneDetectorPermission(); + + try { + return mThreadingDomain.postAndWait( + () -> { + synchronized (mSharedLock) { + if (mLocationTimeZoneDetectorController == null) { + return null; + } + return mLocationTimeZoneDetectorController.getStateForTests(); + } + }, + BLOCKING_OP_WAIT_DURATION_MILLIS); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + /** * Passes a {@link TestCommand} to the specified provider and waits for the response. */ diff --git a/services/core/java/com/android/server/location/timezone/LocationTimeZoneManagerServiceState.java b/services/core/java/com/android/server/location/timezone/LocationTimeZoneManagerServiceState.java new file mode 100644 index 000000000000..b1dd55f3d4fd --- /dev/null +++ b/services/core/java/com/android/server/location/timezone/LocationTimeZoneManagerServiceState.java @@ -0,0 +1,97 @@ +/* + * 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.server.location.timezone; + +import android.annotation.NonNull; +import android.annotation.Nullable; + +import com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState; +import com.android.server.timezonedetector.GeolocationTimeZoneSuggestion; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +/** A snapshot of the location time zone manager service's state for tests. */ +final class LocationTimeZoneManagerServiceState { + + @Nullable private final GeolocationTimeZoneSuggestion mLastSuggestion; + @NonNull private final List<ProviderState> mPrimaryProviderStates; + @NonNull private final List<ProviderState> mSecondaryProviderStates; + + LocationTimeZoneManagerServiceState(@NonNull Builder builder) { + mLastSuggestion = builder.mLastSuggestion; + mPrimaryProviderStates = Objects.requireNonNull(builder.mPrimaryProviderStates); + mSecondaryProviderStates = Objects.requireNonNull(builder.mSecondaryProviderStates); + } + + @Nullable + public GeolocationTimeZoneSuggestion getLastSuggestion() { + return mLastSuggestion; + } + + @NonNull + public List<ProviderState> getPrimaryProviderStates() { + return Collections.unmodifiableList(mPrimaryProviderStates); + } + + @NonNull + public List<ProviderState> getSecondaryProviderStates() { + return Collections.unmodifiableList(mSecondaryProviderStates); + } + + @Override + public String toString() { + return "LocationTimeZoneManagerServiceState{" + + "mLastSuggestion=" + mLastSuggestion + + ", mPrimaryProviderStates=" + mPrimaryProviderStates + + ", mSecondaryProviderStates=" + mSecondaryProviderStates + + '}'; + } + + static final class Builder { + + private GeolocationTimeZoneSuggestion mLastSuggestion; + private List<ProviderState> mPrimaryProviderStates; + private List<ProviderState> mSecondaryProviderStates; + + @NonNull + Builder setLastSuggestion(@NonNull GeolocationTimeZoneSuggestion lastSuggestion) { + mLastSuggestion = Objects.requireNonNull(lastSuggestion); + return this; + } + + @NonNull + Builder setPrimaryProviderStateChanges(@NonNull List<ProviderState> primaryProviderStates) { + mPrimaryProviderStates = new ArrayList<>(primaryProviderStates); + return this; + } + + @NonNull + Builder setSecondaryProviderStateChanges( + @NonNull List<ProviderState> secondaryProviderStates) { + mSecondaryProviderStates = new ArrayList<>(secondaryProviderStates); + return this; + } + + @NonNull + LocationTimeZoneManagerServiceState build() { + return new LocationTimeZoneManagerServiceState(this); + } + } +} diff --git a/services/core/java/com/android/server/location/timezone/LocationTimeZoneManagerShellCommand.java b/services/core/java/com/android/server/location/timezone/LocationTimeZoneManagerShellCommand.java index 554df07aa485..6f9863c9bd09 100644 --- a/services/core/java/com/android/server/location/timezone/LocationTimeZoneManagerShellCommand.java +++ b/services/core/java/com/android/server/location/timezone/LocationTimeZoneManagerShellCommand.java @@ -15,23 +15,46 @@ */ package com.android.server.location.timezone; +import static android.app.time.LocationTimeZoneManager.DUMP_STATE_OPTION_PROTO; import static android.app.time.LocationTimeZoneManager.PRIMARY_PROVIDER_NAME; +import static android.app.time.LocationTimeZoneManager.PROVIDER_MODE_OVERRIDE_DISABLED; +import static android.app.time.LocationTimeZoneManager.PROVIDER_MODE_OVERRIDE_NONE; +import static android.app.time.LocationTimeZoneManager.PROVIDER_MODE_OVERRIDE_SIMULATED; import static android.app.time.LocationTimeZoneManager.SECONDARY_PROVIDER_NAME; +import static android.app.time.LocationTimeZoneManager.SHELL_COMMAND_DUMP_STATE; +import static android.app.time.LocationTimeZoneManager.SHELL_COMMAND_RECORD_PROVIDER_STATES; import static android.app.time.LocationTimeZoneManager.SHELL_COMMAND_SEND_PROVIDER_TEST_COMMAND; +import static android.app.time.LocationTimeZoneManager.SHELL_COMMAND_SET_PROVIDER_MODE_OVERRIDE; import static android.app.time.LocationTimeZoneManager.SHELL_COMMAND_START; import static android.app.time.LocationTimeZoneManager.SHELL_COMMAND_STOP; -import static android.app.time.LocationTimeZoneManager.SYSTEM_PROPERTY_KEY_PROVIDER_MODE_OVERRIDE_PRIMARY; -import static android.app.time.LocationTimeZoneManager.SYSTEM_PROPERTY_KEY_PROVIDER_MODE_OVERRIDE_SECONDARY; -import static android.app.time.LocationTimeZoneManager.SYSTEM_PROPERTY_VALUE_PROVIDER_MODE_DISABLED; -import static android.app.time.LocationTimeZoneManager.SYSTEM_PROPERTY_VALUE_PROVIDER_MODE_SIMULATED; + +import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_DESTROYED; +import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_PERM_FAILED; +import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_CERTAIN; +import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_INITIALIZING; +import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_UNCERTAIN; +import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STOPPED; +import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_UNKNOWN; import android.annotation.NonNull; +import android.app.time.GeolocationTimeZoneSuggestionProto; +import android.app.time.LocationTimeZoneManagerProto; +import android.app.time.LocationTimeZoneManagerServiceStateProto; +import android.app.time.TimeZoneProviderStateProto; import android.os.Bundle; import android.os.ShellCommand; +import android.util.IndentingPrintWriter; +import android.util.proto.ProtoOutputStream; + +import com.android.internal.util.dump.DualDumpOutputStream; +import com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.ProviderStateEnum; +import com.android.server.timezonedetector.GeolocationTimeZoneSuggestion; +import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.Arrays; import java.util.List; +import java.util.Objects; /** Implements the shell command interface for {@link LocationTimeZoneManagerService}. */ class LocationTimeZoneManagerShellCommand extends ShellCommand { @@ -58,9 +81,18 @@ class LocationTimeZoneManagerShellCommand extends ShellCommand { case SHELL_COMMAND_STOP: { return runStop(); } + case SHELL_COMMAND_SET_PROVIDER_MODE_OVERRIDE: { + return runSetProviderModeOverride(); + } case SHELL_COMMAND_SEND_PROVIDER_TEST_COMMAND: { return runSendProviderTestCommand(); } + case SHELL_COMMAND_RECORD_PROVIDER_STATES: { + return runRecordProviderStates(); + } + case SHELL_COMMAND_DUMP_STATE: { + return runDumpControllerState(); + } default: { return handleDefaultCommands(cmd); } @@ -70,35 +102,42 @@ class LocationTimeZoneManagerShellCommand extends ShellCommand { @Override public void onHelp() { final PrintWriter pw = getOutPrintWriter(); - pw.println("Location Time Zone Manager (location_time_zone_manager) commands:"); + pw.println("Location Time Zone Manager (location_time_zone_manager) commands for tests:"); pw.println(" help"); pw.println(" Print this help text."); pw.printf(" %s\n", SHELL_COMMAND_START); pw.println(" Starts the location_time_zone_manager, creating time zone providers."); pw.printf(" %s\n", SHELL_COMMAND_STOP); pw.println(" Stops the location_time_zone_manager, destroying time zone providers."); + pw.printf(" %s <provider name> <mode>\n", SHELL_COMMAND_SET_PROVIDER_MODE_OVERRIDE); + pw.println(" Sets a provider into a test mode next time the service started."); + pw.printf(" Values: %s|%s|%s\n", PROVIDER_MODE_OVERRIDE_NONE, + PROVIDER_MODE_OVERRIDE_DISABLED, PROVIDER_MODE_OVERRIDE_SIMULATED); + pw.printf(" %s (true|false)\n", SHELL_COMMAND_RECORD_PROVIDER_STATES); + pw.printf(" Enables / disables provider state recording mode. See also %s. The default" + + " state is always \"false\".\n", SHELL_COMMAND_DUMP_STATE); + pw.println(" Note: When enabled, this mode consumes memory and it is only intended for" + + " testing."); + pw.println(" It should be disabled after use, or the device can be rebooted to" + + " reset the mode to disabled."); + pw.println(" Disabling (or enabling repeatedly) clears any existing stored states."); + pw.printf(" %s [%s]\n", SHELL_COMMAND_DUMP_STATE, DUMP_STATE_OPTION_PROTO); + pw.println(" Dumps Location Time Zone Manager state for tests as text or binary proto" + + " form."); + pw.println(" See the LocationTimeZoneManagerServiceStateProto definition for details."); pw.printf(" %s <provider name> <test command>\n", SHELL_COMMAND_SEND_PROVIDER_TEST_COMMAND); pw.println(" Passes a test command to the named provider."); pw.println(); - pw.printf("%s details:\n", SHELL_COMMAND_SEND_PROVIDER_TEST_COMMAND); - pw.println(); pw.printf("<provider name> = One of %s\n", VALID_PROVIDER_NAMES); pw.println(); - pw.println("<test command> encoding:"); + pw.printf("%s details:\n", SHELL_COMMAND_SEND_PROVIDER_TEST_COMMAND); pw.println(); - TestCommand.printShellCommandEncodingHelp(pw); + pw.println("Provider <test command> encoding:"); pw.println(); - pw.printf("Provider modes can be modified by setting the \"%s\" or \"%s\"\n system" - + " property and restarting the service or rebooting the device.\n", - SYSTEM_PROPERTY_KEY_PROVIDER_MODE_OVERRIDE_PRIMARY, - SYSTEM_PROPERTY_KEY_PROVIDER_MODE_OVERRIDE_SECONDARY); - pw.println("Values are:"); - pw.printf(" %s - simulation mode (see below for commands)\n", - SYSTEM_PROPERTY_VALUE_PROVIDER_MODE_SIMULATED); - pw.printf(" %s - disabled mode\n", SYSTEM_PROPERTY_VALUE_PROVIDER_MODE_DISABLED); + TestCommand.printShellCommandEncodingHelp(pw); pw.println(); - pw.println("Simulated providers can be used to test the system server behavior or to" + pw.println("Simulated provider mode can be used to test the system server behavior or to" + " reproduce bugs without the complexity of using real providers."); pw.println(); pw.println("The test commands for simulated providers are:"); @@ -132,6 +171,119 @@ class LocationTimeZoneManagerShellCommand extends ShellCommand { return 0; } + private int runSetProviderModeOverride() { + PrintWriter outPrintWriter = getOutPrintWriter(); + try { + String providerName = getNextArgRequired(); + String modeOverride = getNextArgRequired(); + outPrintWriter.println("Setting provider mode override for " + providerName + + " to " + modeOverride); + mService.setProviderModeOverride(providerName, modeOverride); + } catch (RuntimeException e) { + reportError(e); + return 1; + } + return 0; + } + + private int runRecordProviderStates() { + PrintWriter outPrintWriter = getOutPrintWriter(); + boolean enabled; + try { + String nextArg = getNextArgRequired(); + enabled = Boolean.parseBoolean(nextArg); + } catch (RuntimeException e) { + reportError(e); + return 1; + } + + outPrintWriter.println("Setting provider state recording to " + enabled); + try { + mService.setProviderStateRecordingEnabled(enabled); + } catch (IllegalStateException e) { + reportError(e); + return 2; + } + return 0; + } + + private int runDumpControllerState() { + LocationTimeZoneManagerServiceState state; + try { + state = mService.getStateForTests(); + } catch (RuntimeException e) { + reportError(e); + return 1; + } + + DualDumpOutputStream outputStream; + boolean useProto = Objects.equals(DUMP_STATE_OPTION_PROTO, getNextOption()); + if (useProto) { + FileDescriptor outFd = getOutFileDescriptor(); + outputStream = new DualDumpOutputStream(new ProtoOutputStream(outFd)); + } else { + outputStream = new DualDumpOutputStream( + new IndentingPrintWriter(getOutPrintWriter(), " ")); + } + if (state.getLastSuggestion() != null) { + GeolocationTimeZoneSuggestion lastSuggestion = state.getLastSuggestion(); + long lastSuggestionToken = outputStream.start( + "last_suggestion", LocationTimeZoneManagerServiceStateProto.LAST_SUGGESTION); + for (String zoneId : lastSuggestion.getZoneIds()) { + outputStream.write( + "zone_ids" , GeolocationTimeZoneSuggestionProto.ZONE_IDS, zoneId); + } + for (String debugInfo : lastSuggestion.getDebugInfo()) { + outputStream.write( + "debug_info", GeolocationTimeZoneSuggestionProto.DEBUG_INFO, debugInfo); + } + outputStream.end(lastSuggestionToken); + } + + writeProviderStates(outputStream, state.getPrimaryProviderStates(), + "primary_provider_states", + LocationTimeZoneManagerServiceStateProto.PRIMARY_PROVIDER_STATES); + writeProviderStates(outputStream, state.getSecondaryProviderStates(), + "secondary_provider_states", + LocationTimeZoneManagerServiceStateProto.SECONDARY_PROVIDER_STATES); + outputStream.flush(); + + return 0; + } + + private static void writeProviderStates(DualDumpOutputStream outputStream, + List<LocationTimeZoneProvider.ProviderState> providerStates, String fieldName, + long fieldId) { + for (LocationTimeZoneProvider.ProviderState providerState : providerStates) { + long providerStateToken = outputStream.start(fieldName, fieldId); + outputStream.write("state", TimeZoneProviderStateProto.STATE, + convertProviderStateEnumToProtoEnum(providerState.stateEnum)); + outputStream.end(providerStateToken); + } + } + + private static int convertProviderStateEnumToProtoEnum(@ProviderStateEnum int stateEnum) { + switch (stateEnum) { + case PROVIDER_STATE_UNKNOWN: + return LocationTimeZoneManagerProto.TIME_ZONE_PROVIDER_STATE_UNKNOWN; + case PROVIDER_STATE_STARTED_INITIALIZING: + return LocationTimeZoneManagerProto.TIME_ZONE_PROVIDER_STATE_INITIALIZING; + case PROVIDER_STATE_STARTED_CERTAIN: + return LocationTimeZoneManagerProto.TIME_ZONE_PROVIDER_STATE_CERTAIN; + case PROVIDER_STATE_STARTED_UNCERTAIN: + return LocationTimeZoneManagerProto.TIME_ZONE_PROVIDER_STATE_UNCERTAIN; + case PROVIDER_STATE_STOPPED: + return LocationTimeZoneManagerProto.TIME_ZONE_PROVIDER_STATE_DISABLED; + case PROVIDER_STATE_PERM_FAILED: + return LocationTimeZoneManagerProto.TIME_ZONE_PROVIDER_STATE_PERM_FAILED; + case PROVIDER_STATE_DESTROYED: + return LocationTimeZoneManagerProto.TIME_ZONE_PROVIDER_STATE_DESTROYED; + default: { + throw new IllegalArgumentException("Unknown stateEnum=" + stateEnum); + } + } + } + private int runSendProviderTestCommand() { PrintWriter outPrintWriter = getOutPrintWriter(); diff --git a/services/core/java/com/android/server/location/timezone/LocationTimeZoneProvider.java b/services/core/java/com/android/server/location/timezone/LocationTimeZoneProvider.java index 132c1671f725..9a7b7750659c 100644 --- a/services/core/java/com/android/server/location/timezone/LocationTimeZoneProvider.java +++ b/services/core/java/com/android/server/location/timezone/LocationTimeZoneProvider.java @@ -49,6 +49,8 @@ import com.android.server.timezonedetector.Dumpable; import com.android.server.timezonedetector.ReferenceWithHistory; import java.time.Duration; +import java.util.ArrayList; +import java.util.List; import java.util.Objects; /** @@ -339,11 +341,20 @@ abstract class LocationTimeZoneProvider implements Dumpable { @NonNull final String mProviderName; /** + * Usually {@code false} but can be set to {@code true} for testing. + */ + @GuardedBy("mSharedLock") + private boolean mStateChangeRecording; + + @GuardedBy("mSharedLock") + @NonNull + private final ArrayList<ProviderState> mRecordedStates = new ArrayList<>(0); + + /** * The current state (with history for debugging). */ @GuardedBy("mSharedLock") - final ReferenceWithHistory<ProviderState> mCurrentState = - new ReferenceWithHistory<>(10); + final ReferenceWithHistory<ProviderState> mCurrentState = new ReferenceWithHistory<>(10); /** * Used for scheduling initialization timeouts, i.e. for providers that have just been started. @@ -423,6 +434,28 @@ abstract class LocationTimeZoneProvider implements Dumpable { abstract void onDestroy(); /** + * Sets the provider into state recording mode for tests. + */ + final void setStateChangeRecordingEnabled(boolean enabled) { + mThreadingDomain.assertCurrentThread(); + synchronized (mSharedLock) { + mStateChangeRecording = enabled; + mRecordedStates.clear(); + mRecordedStates.trimToSize(); + } + } + + /** + * Returns recorded states. + */ + final List<ProviderState> getRecordedStates() { + mThreadingDomain.assertCurrentThread(); + synchronized (mSharedLock) { + return new ArrayList<>(mRecordedStates); + } + } + + /** * Set the current state, for use by this class and subclasses only. If {@code #notifyChanges} * is {@code true} and {@code newState} is not equal to the old state, then {@link * ProviderListener#onProviderStateChange(ProviderState)} must be called on @@ -434,8 +467,11 @@ abstract class LocationTimeZoneProvider implements Dumpable { ProviderState oldState = mCurrentState.get(); mCurrentState.set(newState); onSetCurrentState(newState); - if (notifyChanges) { - if (!Objects.equals(newState, oldState)) { + if (!Objects.equals(newState, oldState)) { + if (mStateChangeRecording) { + mRecordedStates.add(newState); + } + if (notifyChanges) { mProviderListener.onProviderStateChange(newState); } } diff --git a/services/core/java/com/android/server/media/MediaShellCommand.java b/services/core/java/com/android/server/media/MediaShellCommand.java index 69c57a9a5d74..103cdd997efc 100644 --- a/services/core/java/com/android/server/media/MediaShellCommand.java +++ b/services/core/java/com/android/server/media/MediaShellCommand.java @@ -67,7 +67,7 @@ public class MediaShellCommand extends ShellCommand { } if (sThread == null) { Looper.prepare(); - sThread = ActivityThread.systemMain(); + sThread = ActivityThread.currentActivityThread(); Context context = sThread.getSystemContext(); sMediaSessionManager = (MediaSessionManager) context.getSystemService(Context.MEDIA_SESSION_SERVICE); diff --git a/services/core/java/com/android/server/net/NetworkIdentitySet.java b/services/core/java/com/android/server/net/NetworkIdentitySet.java index 2326ad39fd27..bce80696f72c 100644 --- a/services/core/java/com/android/server/net/NetworkIdentitySet.java +++ b/services/core/java/com/android/server/net/NetworkIdentitySet.java @@ -20,8 +20,8 @@ import android.net.NetworkIdentity; import android.service.NetworkIdentitySetProto; import android.util.proto.ProtoOutputStream; -import java.io.DataInputStream; -import java.io.DataOutputStream; +import java.io.DataInput; +import java.io.DataOutput; import java.io.IOException; import java.util.HashSet; @@ -44,7 +44,7 @@ public class NetworkIdentitySet extends HashSet<NetworkIdentity> implements public NetworkIdentitySet() { } - public NetworkIdentitySet(DataInputStream in) throws IOException { + public NetworkIdentitySet(DataInput in) throws IOException { final int version = in.readInt(); final int size = in.readInt(); for (int i = 0; i < size; i++) { @@ -89,7 +89,7 @@ public class NetworkIdentitySet extends HashSet<NetworkIdentity> implements } } - public void writeToStream(DataOutputStream out) throws IOException { + public void writeToStream(DataOutput out) throws IOException { out.writeInt(VERSION_ADD_DEFAULT_NETWORK); out.writeInt(size()); for (NetworkIdentity ident : this) { @@ -143,7 +143,7 @@ public class NetworkIdentitySet extends HashSet<NetworkIdentity> implements return true; } - private static void writeOptionalString(DataOutputStream out, String value) throws IOException { + private static void writeOptionalString(DataOutput out, String value) throws IOException { if (value != null) { out.writeByte(1); out.writeUTF(value); @@ -152,7 +152,7 @@ public class NetworkIdentitySet extends HashSet<NetworkIdentity> implements } } - private static String readOptionalString(DataInputStream in) throws IOException { + private static String readOptionalString(DataInput in) throws IOException { if (in.readByte() != 0) { return in.readUTF(); } else { diff --git a/services/core/java/com/android/server/net/NetworkStatsCollection.java b/services/core/java/com/android/server/net/NetworkStatsCollection.java index c4beddd42eaf..6aefe41891f9 100644 --- a/services/core/java/com/android/server/net/NetworkStatsCollection.java +++ b/services/core/java/com/android/server/net/NetworkStatsCollection.java @@ -63,12 +63,15 @@ import com.google.android.collect.Lists; import com.google.android.collect.Maps; import java.io.BufferedInputStream; +import java.io.DataInput; import java.io.DataInputStream; +import java.io.DataOutput; import java.io.DataOutputStream; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; +import java.io.OutputStream; import java.io.PrintWriter; import java.net.ProtocolException; import java.time.ZonedDateTime; @@ -82,7 +85,7 @@ import java.util.Objects; * Collection of {@link NetworkStatsHistory}, stored based on combined key of * {@link NetworkIdentitySet}, UID, set, and tag. Knows how to persist itself. */ -public class NetworkStatsCollection implements FileRotator.Reader { +public class NetworkStatsCollection implements FileRotator.Reader, FileRotator.Writer { /** File header magic number: "ANET" */ private static final int FILE_MAGIC = 0x414E4554; @@ -431,10 +434,10 @@ public class NetworkStatsCollection implements FileRotator.Reader { @Override public void read(InputStream in) throws IOException { - read(new DataInputStream(in)); + read((DataInput) new DataInputStream(in)); } - public void read(DataInputStream in) throws IOException { + private void read(DataInput in) throws IOException { // verify file magic header intact final int magic = in.readInt(); if (magic != FILE_MAGIC) { @@ -468,7 +471,13 @@ public class NetworkStatsCollection implements FileRotator.Reader { } } - public void write(DataOutputStream out) throws IOException { + @Override + public void write(OutputStream out) throws IOException { + write((DataOutput) new DataOutputStream(out)); + out.flush(); + } + + private void write(DataOutput out) throws IOException { // cluster key lists grouped by ident final HashMap<NetworkIdentitySet, ArrayList<Key>> keysByIdent = Maps.newHashMap(); for (Key key : mStats.keySet()) { @@ -497,8 +506,6 @@ public class NetworkStatsCollection implements FileRotator.Reader { history.writeToStream(out); } } - - out.flush(); } @Deprecated diff --git a/services/core/java/com/android/server/net/NetworkStatsRecorder.java b/services/core/java/com/android/server/net/NetworkStatsRecorder.java index ce741693cb4b..978ae87d39d5 100644 --- a/services/core/java/com/android/server/net/NetworkStatsRecorder.java +++ b/services/core/java/com/android/server/net/NetworkStatsRecorder.java @@ -42,7 +42,6 @@ import com.google.android.collect.Sets; import libcore.io.IoUtils; import java.io.ByteArrayOutputStream; -import java.io.DataOutputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; @@ -375,7 +374,7 @@ public class NetworkStatsRecorder { @Override public void write(OutputStream out) throws IOException { - mCollection.write(new DataOutputStream(out)); + mCollection.write(out); mCollection.reset(); } } @@ -412,7 +411,7 @@ public class NetworkStatsRecorder { @Override public void write(OutputStream out) throws IOException { - mTemp.write(new DataOutputStream(out)); + mTemp.write(out); } } diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index c10655896ec3..cc5dfdceb07d 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -68,6 +68,7 @@ import static android.os.UserHandle.USER_NULL; import static android.os.UserHandle.USER_SYSTEM; import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_ALERTING; import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_CONVERSATIONS; +import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_ONGOING; import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_SILENT; import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_CALL_EFFECTS; import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_EFFECTS; @@ -9497,7 +9498,7 @@ public class NotificationManagerService extends SystemService { public class NotificationListeners extends ManagedServices { static final String TAG_ENABLED_NOTIFICATION_LISTENERS = "enabled_listeners"; - static final String TAG_REQUESTED_LISTENERS = "requested_listeners"; + static final String TAG_REQUESTED_LISTENERS = "req_listeners"; static final String TAG_REQUESTED_LISTENER = "listener"; static final String ATT_COMPONENT = "component"; static final String ATT_TYPES = "types"; @@ -9686,7 +9687,7 @@ public class NotificationManagerService extends SystemService { final ComponentName cn = ComponentName.unflattenFromString( XmlUtils.readStringAttribute(parser, ATT_COMPONENT)); int approved = FLAG_FILTER_TYPE_CONVERSATIONS | FLAG_FILTER_TYPE_ALERTING - | FLAG_FILTER_TYPE_SILENT; + | FLAG_FILTER_TYPE_SILENT | FLAG_FILTER_TYPE_ONGOING; ArraySet<String> disallowedPkgs = new ArraySet<>(); final int listenerOuterDepth = parser.getDepth(); diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java index 094be0622bab..f8990c065341 100644 --- a/services/core/java/com/android/server/pm/AppsFilter.java +++ b/services/core/java/com/android/server/pm/AppsFilter.java @@ -167,6 +167,7 @@ public class AppsFilter implements Watchable, Snappable { * * @param observer The {@link Watcher} to be notified when the {@link Watchable} changes. */ + @Override public void registerObserver(@NonNull Watcher observer) { mWatchable.registerObserver(observer); } @@ -177,17 +178,29 @@ public class AppsFilter implements Watchable, Snappable { * * @param observer The {@link Watcher} that should not be in the notification list. */ + @Override public void unregisterObserver(@NonNull Watcher observer) { mWatchable.unregisterObserver(observer); } /** + * Return true if the {@link Watcher) is a registered observer. + * @param observer A {@link Watcher} that might be registered + * @return true if the observer is registered with this {@link Watchable}. + */ + @Override + public boolean isRegisteredObserver(@NonNull Watcher observer) { + return mWatchable.isRegisteredObserver(observer); + } + + /** * Invokes {@link Watcher#onChange} on each registered observer. The method can be called * with the {@link Watchable} that generated the event. In a tree of {@link Watchable}s, this * is generally the first (deepest) {@link Watchable} to detect a change. * * @param what The {@link Watchable} that generated the event. */ + @Override public void dispatchChange(@Nullable Watchable what) { mSnapshot = null; mWatchable.dispatchChange(what); @@ -443,7 +456,7 @@ public class AppsFilter implements Watchable, Snappable { } final StateProvider stateProvider = command -> { synchronized (injector.getLock()) { - command.currentState(injector.getSettings().getPackagesLocked().untrackedMap(), + command.currentState(injector.getSettings().getPackagesLocked().untrackedStorage(), injector.getUserManagerInternal().getUserInfos()); } }; @@ -979,7 +992,7 @@ public class AppsFilter implements Watchable, Snappable { @Nullable SparseArray<int[]> getVisibilityAllowList(PackageSetting setting, int[] users, WatchedArrayMap<String, PackageSetting> existingSettings) { - return getVisibilityAllowList(setting, users, existingSettings.untrackedMap()); + return getVisibilityAllowList(setting, users, existingSettings.untrackedStorage()); } /** diff --git a/services/core/java/com/android/server/pm/CrossProfileIntentResolver.java b/services/core/java/com/android/server/pm/CrossProfileIntentResolver.java index bf7f466457e9..aae6ce46de66 100644 --- a/services/core/java/com/android/server/pm/CrossProfileIntentResolver.java +++ b/services/core/java/com/android/server/pm/CrossProfileIntentResolver.java @@ -19,8 +19,8 @@ package com.android.server.pm; import android.annotation.NonNull; import android.content.IntentFilter; +import com.android.server.WatchableIntentResolver; import com.android.server.utils.Snappable; -import com.android.server.utils.WatchableIntentResolver; import java.util.List; @@ -57,7 +57,7 @@ class CrossProfileIntentResolver */ public CrossProfileIntentResolver snapshot() { CrossProfileIntentResolver result = new CrossProfileIntentResolver(); - result.doCopy(this); + result.copyFrom(this); return result; } } diff --git a/services/core/java/com/android/server/pm/InstantAppRegistry.java b/services/core/java/com/android/server/pm/InstantAppRegistry.java index 69d3e5c2f941..c3bca285dca3 100644 --- a/services/core/java/com/android/server/pm/InstantAppRegistry.java +++ b/services/core/java/com/android/server/pm/InstantAppRegistry.java @@ -154,6 +154,9 @@ class InstantAppRegistry implements Watchable, Snappable { public void unregisterObserver(@NonNull Watcher observer) { mWatchable.unregisterObserver(observer); } + public boolean isRegisteredObserver(@NonNull Watcher observer) { + return mWatchable.isRegisteredObserver(observer); + } public void dispatchChange(@Nullable Watchable what) { mSnapshot = null; mWatchable.dispatchChange(what); diff --git a/services/core/java/com/android/server/pm/PersistentPreferredIntentResolver.java b/services/core/java/com/android/server/pm/PersistentPreferredIntentResolver.java index d0f9787bacb8..c1bfcac50772 100644 --- a/services/core/java/com/android/server/pm/PersistentPreferredIntentResolver.java +++ b/services/core/java/com/android/server/pm/PersistentPreferredIntentResolver.java @@ -19,8 +19,8 @@ package com.android.server.pm; import android.annotation.NonNull; import android.content.IntentFilter; +import com.android.server.WatchableIntentResolver; import com.android.server.utils.Snappable; -import com.android.server.utils.WatchableIntentResolver; public class PersistentPreferredIntentResolver extends WatchableIntentResolver<PersistentPreferredActivity, PersistentPreferredActivity> @@ -47,7 +47,7 @@ public class PersistentPreferredIntentResolver */ public PersistentPreferredIntentResolver snapshot() { PersistentPreferredIntentResolver result = new PersistentPreferredIntentResolver(); - result.doCopy(this); + result.copyFrom(this); return result; } } diff --git a/services/core/java/com/android/server/pm/PreferredIntentResolver.java b/services/core/java/com/android/server/pm/PreferredIntentResolver.java index b62421e31361..0e3b85ca677a 100644 --- a/services/core/java/com/android/server/pm/PreferredIntentResolver.java +++ b/services/core/java/com/android/server/pm/PreferredIntentResolver.java @@ -19,8 +19,8 @@ package com.android.server.pm; import android.annotation.NonNull; import android.content.IntentFilter; +import com.android.server.WatchableIntentResolver; import com.android.server.utils.Snappable; -import com.android.server.utils.WatchableIntentResolver; import java.io.PrintWriter; import java.util.ArrayList; @@ -76,7 +76,7 @@ public class PreferredIntentResolver */ public PreferredIntentResolver snapshot() { PreferredIntentResolver result = new PreferredIntentResolver(); - result.doCopy(this); + result.copyFrom(this); return result; } } diff --git a/services/core/java/com/android/server/pm/SettingBase.java b/services/core/java/com/android/server/pm/SettingBase.java index 7924d5d48876..0e8a278f3b6b 100644 --- a/services/core/java/com/android/server/pm/SettingBase.java +++ b/services/core/java/com/android/server/pm/SettingBase.java @@ -49,6 +49,7 @@ public abstract class SettingBase implements Watchable { * * @param observer The {@link Watcher} to be notified when the {@link Watchable} changes. */ + @Override public void registerObserver(@NonNull Watcher observer) { mWatchable.registerObserver(observer); } @@ -59,20 +60,33 @@ public abstract class SettingBase implements Watchable { * * @param observer The {@link Watcher} that should not be in the notification list. */ + @Override public void unregisterObserver(@NonNull Watcher observer) { mWatchable.unregisterObserver(observer); } /** + * Return true if the {@link Watcher) is a registered observer. + * @param observer A {@link Watcher} that might be registered + * @return true if the observer is registered with this {@link Watchable}. + */ + @Override + public boolean isRegisteredObserver(@NonNull Watcher observer) { + return mWatchable.isRegisteredObserver(observer); + } + + /** * Invokes {@link Watcher#onChange} on each registered observer. The method can be called * with the {@link Watchable} that generated the event. In a tree of {@link Watchable}s, this * is generally the first (deepest) {@link Watchable} to detect a change. * * @param what The {@link Watchable} that generated the event. */ + @Override public void dispatchChange(@Nullable Watchable what) { mWatchable.dispatchChange(what); } + /** * Notify listeners that this object has changed. */ diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index 7c4dadea89eb..50c1065a6000 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -115,12 +115,15 @@ import com.android.server.pm.permission.LegacyPermissionSettings; import com.android.server.pm.permission.LegacyPermissionState; import com.android.server.pm.permission.LegacyPermissionState.PermissionState; import com.android.server.utils.Snappable; -import com.android.server.utils.Snapshots; import com.android.server.utils.TimingsTraceAndSlog; import com.android.server.utils.Watchable; import com.android.server.utils.WatchableImpl; +import com.android.server.utils.Watched; +import com.android.server.utils.WatchedArrayList; import com.android.server.utils.WatchedArrayMap; +import com.android.server.utils.WatchedArraySet; import com.android.server.utils.WatchedSparseArray; +import com.android.server.utils.WatchedSparseIntArray; import com.android.server.utils.Watcher; import libcore.io.IoUtils; @@ -189,6 +192,16 @@ public final class Settings implements Watchable, Snappable { } /** + * Return true if the {@link Watcher) is a registered observer. + * @param observer A {@link Watcher} that might be registered + * @return true if the observer is registered with this {@link Watchable}. + */ + @Override + public boolean isRegisteredObserver(@NonNull Watcher observer) { + return mWatchable.isRegisteredObserver(observer); + } + + /** * Invokes {@link Watcher#onChange} on each registered observer. The method can be called * with the {@link Watchable} that generated the event. In a tree of {@link Watchable}s, this * is generally the first (deepest) {@link Watchable} to detect a change. @@ -350,6 +363,7 @@ public final class Settings implements Watchable, Snappable { private final File mKernelMappingFilename; /** Map from package name to settings */ + @Watched @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) final WatchedArrayMap<String, PackageSetting> mPackages = new WatchedArrayMap<>(); @@ -357,21 +371,29 @@ public final class Settings implements Watchable, Snappable { * List of packages that were involved in installing other packages, i.e. are listed * in at least one app's InstallSource. */ - private final ArraySet<String> mInstallerPackages = new ArraySet<>(); + @Watched + private final WatchedArraySet<String> mInstallerPackages = new WatchedArraySet<>(); /** Map from package name to appId and excluded userids */ - private final ArrayMap<String, KernelPackageState> mKernelMapping = new ArrayMap<>(); + @Watched + private final WatchedArrayMap<String, KernelPackageState> mKernelMapping = + new WatchedArrayMap<>(); // List of replaced system applications + @Watched @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) - final ArrayMap<String, PackageSetting> mDisabledSysPackages = new ArrayMap<>(); + final WatchedArrayMap<String, PackageSetting> mDisabledSysPackages = new WatchedArrayMap<>(); /** List of packages that are blocked for uninstall for specific users */ - private final SparseArray<ArraySet<String>> mBlockUninstallPackages = new SparseArray<>(); + @Watched + private final WatchedSparseArray<ArraySet<String>> mBlockUninstallPackages = + new WatchedSparseArray<>(); // Set of restored intent-filter verification states - private final ArrayMap<String, IntentFilterVerificationInfo> mRestoredIntentFilterVerifications = - new ArrayMap<String, IntentFilterVerificationInfo>(); + @Watched + private final WatchedArrayMap<String, IntentFilterVerificationInfo> + mRestoredIntentFilterVerifications = + new WatchedArrayMap<String, IntentFilterVerificationInfo>(); private static final class KernelPackageState { int appId; @@ -381,7 +403,8 @@ public final class Settings implements Watchable, Snappable { private static int mFirstAvailableUid = 0; /** Map from volume UUID to {@link VersionInfo} */ - private ArrayMap<String, VersionInfo> mVersion = new ArrayMap<>(); + @Watched + private WatchedArrayMap<String, VersionInfo> mVersion = new WatchedArrayMap<>(); /** * Version details for a storage volume that may hold apps. @@ -423,21 +446,27 @@ public final class Settings implements Watchable, Snappable { // The user's preferred activities associated with particular intent // filters. + @Watched private final WatchedSparseArray<PreferredIntentResolver> mPreferredActivities = new WatchedSparseArray<>(); // The persistent preferred activities of the user's profile/device owner // associated with particular intent filters. + @Watched private final WatchedSparseArray<PersistentPreferredIntentResolver> mPersistentPreferredActivities = new WatchedSparseArray<>(); // For every user, it is used to find to which other users the intent can be forwarded. + @Watched private final WatchedSparseArray<CrossProfileIntentResolver> mCrossProfileIntentResolvers = new WatchedSparseArray<>(); - final ArrayMap<String, SharedUserSetting> mSharedUsers = new ArrayMap<>(); - private final ArrayList<SettingBase> mAppIds; - private final SparseArray<SettingBase> mOtherAppIds; + @Watched + final WatchedArrayMap<String, SharedUserSetting> mSharedUsers = new WatchedArrayMap<>(); + @Watched + private final WatchedArrayList<SettingBase> mAppIds; + @Watched + private final WatchedSparseArray<SettingBase> mOtherAppIds; // For reading/writing settings file. private final ArrayList<Signature> mPastSignatures = @@ -449,13 +478,17 @@ public final class Settings implements Watchable, Snappable { // Keys are the new names of the packages, values are the original // names. The packages appear everywhere else under their original // names. - private final ArrayMap<String, String> mRenamedPackages = new ArrayMap<String, String>(); + @Watched + private final WatchedArrayMap<String, String> mRenamedPackages = + new WatchedArrayMap<String, String>(); // For every user, it is used to find the package name of the default Browser App. - final SparseArray<String> mDefaultBrowserApp = new SparseArray<String>(); + @Watched + final WatchedSparseArray<String> mDefaultBrowserApp = new WatchedSparseArray<String>(); // App-link priority tracking, per-user - final SparseIntArray mNextAppLinkGeneration = new SparseIntArray(); + @Watched + final WatchedSparseIntArray mNextAppLinkGeneration = new WatchedSparseIntArray(); final StringBuilder mReadMessages = new StringBuilder(); @@ -471,7 +504,7 @@ public final class Settings implements Watchable, Snappable { private final File mSystemDir; public final KeySetManagerService mKeySetManagerService = - new KeySetManagerService(mPackages.untrackedMap()); + new KeySetManagerService(mPackages.untrackedStorage()); /** Settings and other information about permissions */ final LegacyPermissionSettings mPermissions; @@ -492,8 +525,8 @@ public final class Settings implements Watchable, Snappable { public Settings(Map<String, PackageSetting> pkgSettings) { mLock = new Object(); mPackages.putAll(pkgSettings); - mAppIds = new ArrayList<>(); - mOtherAppIds = new SparseArray<>(); + mAppIds = new WatchedArrayList<>(); + mOtherAppIds = new WatchedSparseArray<>(); mSystemDir = null; mPermissions = null; mRuntimePermissionsPersistence = null; @@ -504,17 +537,32 @@ public final class Settings implements Watchable, Snappable { mStoppedPackagesFilename = null; mBackupStoppedPackagesFilename = null; mKernelMappingFilename = null; + mPackages.registerObserver(mObserver); + mInstallerPackages.registerObserver(mObserver); + mKernelMapping.registerObserver(mObserver); + mDisabledSysPackages.registerObserver(mObserver); + mBlockUninstallPackages.registerObserver(mObserver); + mRestoredIntentFilterVerifications.registerObserver(mObserver); + mVersion.registerObserver(mObserver); mPreferredActivities.registerObserver(mObserver); mPersistentPreferredActivities.registerObserver(mObserver); mCrossProfileIntentResolvers.registerObserver(mObserver); + mSharedUsers.registerObserver(mObserver); + mAppIds.registerObserver(mObserver); + mOtherAppIds.registerObserver(mObserver); + mRenamedPackages.registerObserver(mObserver); + mDefaultBrowserApp.registerObserver(mObserver); + mNextAppLinkGeneration.registerObserver(mObserver); + + Watchable.verifyWatchedAttributes(this, mObserver); } Settings(File dataDir, RuntimePermissionsPersistence runtimePermissionsPersistence, LegacyPermissionDataProvider permissionDataProvider, Object lock) { mLock = lock; - mAppIds = new ArrayList<>(); - mOtherAppIds = new SparseArray<>(); + mAppIds = new WatchedArrayList<>(); + mOtherAppIds = new WatchedSparseArray<>(); mPermissions = new LegacyPermissionSettings(lock); mRuntimePermissionsPersistence = new RuntimePermissionPersistence( runtimePermissionsPersistence); @@ -537,10 +585,25 @@ public final class Settings implements Watchable, Snappable { // Deprecated: Needed for migration mStoppedPackagesFilename = new File(mSystemDir, "packages-stopped.xml"); mBackupStoppedPackagesFilename = new File(mSystemDir, "packages-stopped-backup.xml"); + mPackages.registerObserver(mObserver); + mInstallerPackages.registerObserver(mObserver); + mKernelMapping.registerObserver(mObserver); + mDisabledSysPackages.registerObserver(mObserver); + mBlockUninstallPackages.registerObserver(mObserver); + mRestoredIntentFilterVerifications.registerObserver(mObserver); + mVersion.registerObserver(mObserver); mPreferredActivities.registerObserver(mObserver); mPersistentPreferredActivities.registerObserver(mObserver); mCrossProfileIntentResolvers.registerObserver(mObserver); + mSharedUsers.registerObserver(mObserver); + mAppIds.registerObserver(mObserver); + mOtherAppIds.registerObserver(mObserver); + mRenamedPackages.registerObserver(mObserver); + mDefaultBrowserApp.registerObserver(mObserver); + mNextAppLinkGeneration.registerObserver(mObserver); + + Watchable.verifyWatchedAttributes(this, mObserver); } /** @@ -568,7 +631,7 @@ public final class Settings implements Watchable, Snappable { mInstallerPackages.addAll(r.mInstallerPackages); mKernelMapping.putAll(r.mKernelMapping); mDisabledSysPackages.putAll(r.mDisabledSysPackages); - Snapshots.copy(mBlockUninstallPackages, r.mBlockUninstallPackages); + mBlockUninstallPackages.snapshot(r.mBlockUninstallPackages); mRestoredIntentFilterVerifications.putAll(r.mRestoredIntentFilterVerifications); mVersion.putAll(r.mVersion); mVerifierDeviceIdentity = r.mVerifierDeviceIdentity; @@ -579,13 +642,13 @@ public final class Settings implements Watchable, Snappable { WatchedSparseArray.snapshot( mCrossProfileIntentResolvers, r.mCrossProfileIntentResolvers); mSharedUsers.putAll(r.mSharedUsers); - mAppIds = new ArrayList<>(r.mAppIds); - mOtherAppIds = r.mOtherAppIds.clone(); + mAppIds = r.mAppIds.snapshot(); + mOtherAppIds = r.mOtherAppIds.snapshot(); mPastSignatures.addAll(r.mPastSignatures); mKeySetRefs.putAll(r.mKeySetRefs); - mRenamedPackages.putAll(r.mRenamedPackages); - Snapshots.copy(mDefaultBrowserApp, r.mDefaultBrowserApp); - Snapshots.snapshot(mNextAppLinkGeneration, r.mNextAppLinkGeneration); + mRenamedPackages.snapshot(r.mRenamedPackages); + mDefaultBrowserApp.snapshot(r.mDefaultBrowserApp); + mNextAppLinkGeneration.snapshot(r.mNextAppLinkGeneration); // mReadMessages mPendingPackages.addAll(r.mPendingPackages); mSystemDir = null; diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java index 033bfa648f2f..865571e90338 100644 --- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java +++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java @@ -25,6 +25,7 @@ import android.app.timezonedetector.ITimeZoneDetectorService; import android.app.timezonedetector.ManualTimeZoneSuggestion; import android.app.timezonedetector.TelephonyTimeZoneSuggestion; import android.content.Context; +import android.location.LocationManager; import android.os.Handler; import android.os.IBinder; import android.os.RemoteException; @@ -311,6 +312,19 @@ public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub mHandler.post(() -> mTimeZoneDetectorStrategy.suggestTelephonyTimeZone(timeZoneSuggestion)); } + boolean isGeoTimeZoneDetectionSupported() { + enforceManageTimeZoneDetectorPermission(); + + return isGeoLocationTimeZoneDetectionEnabled(mContext); + } + + boolean isLocationEnabled() { + enforceManageTimeZoneDetectorPermission(); + + return mContext.getSystemService(LocationManager.class) + .isLocationEnabledForUser(mContext.getUser()); + } + @Override protected void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @Nullable String[] args) { diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorShellCommand.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorShellCommand.java index 13b44566c66d..b2630300a6aa 100644 --- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorShellCommand.java +++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorShellCommand.java @@ -15,10 +15,17 @@ */ package com.android.server.timezonedetector; +import static android.app.timezonedetector.TimeZoneDetector.SHELL_COMMAND_IS_AUTO_DETECTION_ENABLED; +import static android.app.timezonedetector.TimeZoneDetector.SHELL_COMMAND_IS_GEO_DETECTION_ENABLED; +import static android.app.timezonedetector.TimeZoneDetector.SHELL_COMMAND_IS_GEO_DETECTION_SUPPORTED; +import static android.app.timezonedetector.TimeZoneDetector.SHELL_COMMAND_IS_LOCATION_ENABLED; +import static android.app.timezonedetector.TimeZoneDetector.SHELL_COMMAND_SET_AUTO_DETECTION_ENABLED; +import static android.app.timezonedetector.TimeZoneDetector.SHELL_COMMAND_SET_GEO_DETECTION_ENABLED; import static android.app.timezonedetector.TimeZoneDetector.SHELL_COMMAND_SUGGEST_GEO_LOCATION_TIME_ZONE; import static android.app.timezonedetector.TimeZoneDetector.SHELL_COMMAND_SUGGEST_MANUAL_TIME_ZONE; import static android.app.timezonedetector.TimeZoneDetector.SHELL_COMMAND_SUGGEST_TELEPHONY_TIME_ZONE; +import android.app.time.TimeZoneConfiguration; import android.app.timezonedetector.ManualTimeZoneSuggestion; import android.app.timezonedetector.TelephonyTimeZoneSuggestion; import android.os.ShellCommand; @@ -43,6 +50,18 @@ class TimeZoneDetectorShellCommand extends ShellCommand { } switch (cmd) { + case SHELL_COMMAND_IS_AUTO_DETECTION_ENABLED: + return runIsAutoDetectionEnabled(); + case SHELL_COMMAND_SET_AUTO_DETECTION_ENABLED: + return runSetAutoDetectionEnabled(); + case SHELL_COMMAND_IS_GEO_DETECTION_SUPPORTED: + return runIsGeoDetectionSupported(); + case SHELL_COMMAND_IS_LOCATION_ENABLED: + return runIsLocationEnabled(); + case SHELL_COMMAND_IS_GEO_DETECTION_ENABLED: + return runIsGeoDetectionEnabled(); + case SHELL_COMMAND_SET_GEO_DETECTION_ENABLED: + return runSetGeoDetectionEnabled(); case SHELL_COMMAND_SUGGEST_GEO_LOCATION_TIME_ZONE: return runSuggestGeolocationTimeZone(); case SHELL_COMMAND_SUGGEST_MANUAL_TIME_ZONE: @@ -55,6 +74,54 @@ class TimeZoneDetectorShellCommand extends ShellCommand { } } + private int runIsAutoDetectionEnabled() { + final PrintWriter pw = getOutPrintWriter(); + boolean enabled = mInterface.getCapabilitiesAndConfig() + .getConfiguration() + .isAutoDetectionEnabled(); + pw.println(enabled); + return 0; + } + + private int runIsGeoDetectionSupported() { + final PrintWriter pw = getOutPrintWriter(); + boolean enabled = mInterface.isGeoTimeZoneDetectionSupported(); + pw.println(enabled); + return 0; + } + + private int runIsLocationEnabled() { + final PrintWriter pw = getOutPrintWriter(); + boolean enabled = mInterface.isLocationEnabled(); + pw.println(enabled); + return 0; + } + + private int runIsGeoDetectionEnabled() { + final PrintWriter pw = getOutPrintWriter(); + boolean enabled = mInterface.getCapabilitiesAndConfig() + .getConfiguration() + .isGeoDetectionEnabled(); + pw.println(enabled); + return 0; + } + + private int runSetAutoDetectionEnabled() { + boolean enabled = Boolean.parseBoolean(getNextArgRequired()); + TimeZoneConfiguration configuration = new TimeZoneConfiguration.Builder() + .setAutoDetectionEnabled(enabled) + .build(); + return mInterface.updateConfiguration(configuration) ? 0 : 1; + } + + private int runSetGeoDetectionEnabled() { + boolean enabled = Boolean.parseBoolean(getNextArgRequired()); + TimeZoneConfiguration configuration = new TimeZoneConfiguration.Builder() + .setGeoDetectionEnabled(enabled) + .build(); + return mInterface.updateConfiguration(configuration) ? 0 : 1; + } + private int runSuggestGeolocationTimeZone() { return runSuggestTimeZone( () -> GeolocationTimeZoneSuggestion.parseCommandLineArg(this), @@ -96,6 +163,20 @@ class TimeZoneDetectorShellCommand extends ShellCommand { pw.println("Time Zone Detector (time_zone_detector) commands:"); pw.println(" help"); pw.println(" Print this help text."); + pw.printf(" %s\n", SHELL_COMMAND_IS_AUTO_DETECTION_ENABLED); + pw.println(" Prints true/false according to the automatic tz detection setting"); + pw.printf(" %s true|false\n", SHELL_COMMAND_SET_AUTO_DETECTION_ENABLED); + pw.println(" Sets the automatic tz detection setting."); + pw.printf(" %s\n", SHELL_COMMAND_IS_GEO_DETECTION_SUPPORTED); + pw.println(" Prints true/false according to whether geolocation time zone detection is" + + " supported on this device"); + pw.printf(" %s\n", SHELL_COMMAND_IS_LOCATION_ENABLED); + pw.println(" Prints true/false according to whether the master location toggle is" + + " enabled for the current user"); + pw.printf(" %s\n", SHELL_COMMAND_IS_GEO_DETECTION_ENABLED); + pw.println(" Prints true/false according to the geolocation tz detection setting"); + pw.printf(" %s true|false\n", SHELL_COMMAND_SET_GEO_DETECTION_ENABLED); + pw.println(" Sets the geolocation tz detection setting."); pw.printf(" %s <geolocation suggestion opts>\n", SHELL_COMMAND_SUGGEST_GEO_LOCATION_TIME_ZONE); pw.printf(" %s <manual suggestion opts>\n", diff --git a/services/core/java/com/android/server/utils/Watchable.java b/services/core/java/com/android/server/utils/Watchable.java index 7c99274f3df2..f936693bd621 100644 --- a/services/core/java/com/android/server/utils/Watchable.java +++ b/services/core/java/com/android/server/utils/Watchable.java @@ -18,6 +18,10 @@ package com.android.server.utils; import android.annotation.NonNull; import android.annotation.Nullable; +import android.os.Build; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Field; /** * Notify registered {@link Watcher}s when the content changes. @@ -41,6 +45,13 @@ public interface Watchable { public void unregisterObserver(@NonNull Watcher observer); /** + * Return true if the {@link Watcher) is a registered observer. + * @param observer A {@link Watcher} that might be registered + * @return true if the observer is registered with this {@link Watchable}. + */ + public boolean isRegisteredObserver(@NonNull Watcher observer); + + /** * Invokes {@link Watcher#onChange} on each registered observer. The method can be called * with the {@link Watchable} that generated the event. In a tree of {@link Watchable}s, this * is generally the first (deepest) {@link Watchable} to detect a change. @@ -48,4 +59,42 @@ public interface Watchable { * @param what The {@link Watchable} that generated the event. */ public void dispatchChange(@Nullable Watchable what); + + /** + * Return true if the field is tagged with @Watched + */ + private static boolean isWatched(Field f) { + for (Annotation a : f.getDeclaredAnnotations()) { + if (a.annotationType().equals(Watched.class)) { + return true; + } + } + return false; + } + + /** + * Verify that all @Watched {@link Watchable} attributes are being watched by this + * class. This requires reflection and only runs in engineering or user debug + * builds. + */ + static void verifyWatchedAttributes(Object base, Watcher observer) { + if (Build.IS_ENG || Build.IS_USERDEBUG) { + for (Field f : base.getClass().getDeclaredFields()) { + try { + final boolean flagged = isWatched(f); + final Object o = f.get(base); + final boolean watchable = o instanceof Watchable; + if (flagged && watchable) { + Watchable attr = (Watchable) f.get(base); + if (attr != null && !attr.isRegisteredObserver(observer)) { + throw new RuntimeException(f.getName() + " missing an observer"); + } + } + } catch (IllegalAccessException e) { + // The field is protected; ignore it. Other exceptions that may be thrown by + // Field.get() are allowed to roll up. + } + } + } + } } diff --git a/services/core/java/com/android/server/utils/WatchableImpl.java b/services/core/java/com/android/server/utils/WatchableImpl.java index 16400b186ab0..527db5402e74 100644 --- a/services/core/java/com/android/server/utils/WatchableImpl.java +++ b/services/core/java/com/android/server/utils/WatchableImpl.java @@ -66,6 +66,18 @@ public class WatchableImpl implements Watchable { } /** + * Return true if the {@link Watcher) is a registered observer. + * @param observer A {@link Watcher} that might be registered + * @return true if the observer is registered with this {@link Watchable}. + */ + @Override + public boolean isRegisteredObserver(@NonNull Watcher observer) { + synchronized (mObservers) { + return mObservers.contains(observer); + } + } + + /** * Return the number of registered observers. * * @return The number of registered observers. @@ -100,7 +112,7 @@ public class WatchableImpl implements Watchable { /** * Freeze the {@link Watchable}. - **/ + */ public void seal() { synchronized (mObservers) { mSealed = true; diff --git a/services/core/java/com/android/server/utils/Watched.java b/services/core/java/com/android/server/utils/Watched.java new file mode 100644 index 000000000000..4340d015119a --- /dev/null +++ b/services/core/java/com/android/server/utils/Watched.java @@ -0,0 +1,32 @@ +/* + * 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.server.utils; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Annotation type to mark an attribute that is monitored for change detection and + * snapshot creation. + * TODO(b/176923052) Automate validation of @Watchable attributes. + */ +@Target({ ElementType.FIELD }) +@Retention(RetentionPolicy.RUNTIME) +public @interface Watched { +} diff --git a/services/core/java/com/android/server/utils/WatchedArrayList.java b/services/core/java/com/android/server/utils/WatchedArrayList.java new file mode 100644 index 000000000000..bb0ba1329d86 --- /dev/null +++ b/services/core/java/com/android/server/utils/WatchedArrayList.java @@ -0,0 +1,416 @@ +/* + * 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.server.utils; + +import android.annotation.NonNull; +import android.annotation.Nullable; + +import java.util.ArrayList; +import java.util.Collection; + +/** + * WatchedArrayMap is an {@link android.util.ArrayMap} that can report changes to itself. If its + * values are {@link Watchable} then the WatchedArrayMap will also report changes to the values. + * A {@link Watchable} is notified only once, no matter how many times it is stored in the array. + * @param <E> The element type, stored in the array. + */ +public class WatchedArrayList<E> extends WatchableImpl + implements Snappable { + + // The storage + private final ArrayList<E> mStorage; + + // If true, the array is watching its children + private volatile boolean mWatching = false; + + // The local observer + private final Watcher mObserver = new Watcher() { + @Override + public void onChange(@Nullable Watchable what) { + WatchedArrayList.this.dispatchChange(what); + } + }; + + /** + * A convenience function called when the elements are added to or removed from the storage. + * The watchable is always {@link this}. + */ + private void onChanged() { + dispatchChange(this); + } + + /** + * A convenience function. Register the object if it is {@link Watchable} and if the + * array is currently watching. Note that the watching flag must be true if this + * function is to succeed. Also note that if this is called with the same object + * twice, <this> is only registered once. + */ + private void registerChild(Object o) { + if (mWatching && o instanceof Watchable) { + ((Watchable) o).registerObserver(mObserver); + } + } + + /** + * A convenience function. Unregister the object if it is {@link Watchable} and if the + * array is currently watching. This unconditionally removes the object from the + * registered list. + */ + private void unregisterChild(Object o) { + if (mWatching && o instanceof Watchable) { + ((Watchable) o).unregisterObserver(mObserver); + } + } + + /** + * A convenience function. Unregister the object if it is {@link Watchable}, if the + * array is currently watching, and if there are no other instances of this object in + * the storage. Note that the watching flag must be true if this function is to + * succeed. The object must already have been removed from the storage before this + * method is called. + */ + private void unregisterChildIf(Object o) { + if (mWatching && o instanceof Watchable) { + if (!mStorage.contains(o)) { + ((Watchable) o).unregisterObserver(mObserver); + } + } + } + + /** + * Register a {@link Watcher} with the array. If this is the first Watcher than any + * array values that are {@link Watchable} are registered to the array itself. + */ + @Override + public void registerObserver(@NonNull Watcher observer) { + super.registerObserver(observer); + if (registeredObserverCount() == 1) { + // The watching flag must be set true before any children are registered. + mWatching = true; + final int end = mStorage.size(); + for (int i = 0; i < end; i++) { + registerChild(mStorage.get(i)); + } + } + } + + /** + * Unregister a {@link Watcher} from the array. If this is the last Watcher than any + * array values that are {@link Watchable} are unregistered to the array itself. + */ + @Override + public void unregisterObserver(@NonNull Watcher observer) { + super.unregisterObserver(observer); + if (registeredObserverCount() == 0) { + final int end = mStorage.size(); + for (int i = 0; i < end; i++) { + unregisterChild(mStorage.get(i)); + } + // The watching flag must be true while children are unregistered. + mWatching = false; + } + } + + /** + * Create a new empty {@link WatchedArrayList}. The default capacity of an array map + * is 0, and will grow once items are added to it. + */ + public WatchedArrayList() { + this(0); + } + + /** + * Create a new {@link WatchedArrayList} with a given initial capacity. + */ + public WatchedArrayList(int capacity) { + mStorage = new ArrayList<E>(capacity); + } + + /** + * Create a new {@link WatchedArrayList} with the content of the collection. + */ + public WatchedArrayList(@Nullable Collection<? extends E> c) { + mStorage = new ArrayList<E>(); + if (c != null) { + // There is no need to register children because the WatchedArrayList starts + // life unobserved. + mStorage.addAll(c); + } + } + + /** + * Create a {@link WatchedArrayList} from an {@link ArrayList} + */ + public WatchedArrayList(@NonNull ArrayList<E> c) { + mStorage = new ArrayList<>(c); + } + + /** + * Create a {@link WatchedArrayList} from an {@link WatchedArrayList} + */ + public WatchedArrayList(@NonNull WatchedArrayList<E> c) { + mStorage = new ArrayList<>(c.mStorage); + } + + /** + * Make <this> a copy of src. Any data in <this> is discarded. + */ + public void copyFrom(@NonNull ArrayList<E> src) { + clear(); + final int end = src.size(); + mStorage.ensureCapacity(end); + for (int i = 0; i < end; i++) { + add(src.get(i)); + } + } + + /** + * Make dst a copy of <this>. Any previous data in dst is discarded. + */ + public void copyTo(@NonNull ArrayList<E> dst) { + dst.clear(); + final int end = size(); + dst.ensureCapacity(end); + for (int i = 0; i < end; i++) { + dst.add(get(i)); + } + } + + /** + * Return the underlying storage. This breaks the wrapper but is necessary when + * passing the array to distant methods. + */ + public ArrayList<E> untrackedStorage() { + return mStorage; + } + + /** + * Append the specified element to the end of the list + */ + public boolean add(E value) { + final boolean result = mStorage.add(value); + registerChild(value); + onChanged(); + return result; + } + + /** + * Insert the element into the list + */ + public void add(int index, E value) { + mStorage.add(index, value); + registerChild(value); + onChanged(); + } + + /** + * Append the elements of the collection to the list. + */ + public boolean addAll(Collection<? extends E> c) { + if (c.size() > 0) { + for (E e: c) { + mStorage.add(e); + } + onChanged(); + return true; + } else { + return false; + } + } + + /** + * Insert the elements of the collection into the list at the index. + */ + public boolean addAll(int index, Collection<? extends E> c) { + if (c.size() > 0) { + for (E e: c) { + mStorage.add(index++, e); + } + onChanged(); + return true; + } else { + return false; + } + } + + + /** + * Remove all elements from the list. + */ + public void clear() { + // The storage cannot be simply cleared. Each element in the storage must be + // unregistered. Deregistration is only needed if the array is actually + // watching. + if (mWatching) { + final int end = mStorage.size(); + for (int i = 0; i < end; i++) { + unregisterChild(mStorage.get(i)); + } + } + mStorage.clear(); + onChanged(); + } + + /** + * Return true if the object is in the array. + */ + public boolean contains(Object o) { + return mStorage.contains(o); + } + + /** + * Ensure capacity. + */ + public void ensureCapacity(int min) { + mStorage.ensureCapacity(min); + } + + /** + * Retrieve the element at the specified index. + */ + public E get(int index) { + return mStorage.get(index); + } + + /** + * Return the index of the object. -1 is returned if the object is not in the list. + */ + public int indexOf(Object o) { + return mStorage.indexOf(o); + } + + /** + * True if the list has no elements + */ + public boolean isEmpty() { + return mStorage.isEmpty(); + } + + /** + * Return the index of the last occurrence of the object. + */ + public int lastIndexOf(Object o) { + return mStorage.lastIndexOf(o); + } + + /** + * Remove and return the element at the specified position. + */ + public E remove(int index) { + final E result = mStorage.remove(index); + unregisterChildIf(result); + onChanged(); + return result; + } + + /** + * Remove the first occurrence of the object in the list. Return true if the object + * was actually in the list and false otherwise. + */ + public boolean remove(Object o) { + if (mStorage.remove(o)) { + unregisterChildIf(o); + onChanged(); + return true; + } + return false; + } + + /** + * Replace the object at the index. + */ + public E set(int index, E value) { + final E result = mStorage.set(index, value); + if (value != result) { + unregisterChildIf(result); + registerChild(value); + onChanged(); + } + return result; + } + + /** + * Return the number of elements in the list. + */ + public int size() { + return mStorage.size(); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean equals(@Nullable Object o) { + if (o instanceof WatchedArrayList) { + WatchedArrayList w = (WatchedArrayList) o; + return mStorage.equals(w.mStorage); + } else { + return false; + } + } + + /** + * {@inheritDoc} + */ + @Override + public int hashCode() { + return mStorage.hashCode(); + } + + /** + * Create a copy of the array. If the element is a subclass of Snapper then the copy + * contains snapshots of the elements. Otherwise the copy contains references to the + * elements. The returned snapshot is immutable. + * @return A new array whose elements are the elements of <this>. + */ + public WatchedArrayList<E> snapshot() { + WatchedArrayList<E> l = new WatchedArrayList<>(size()); + snapshot(l, this); + return l; + } + + /** + * Make <this> a snapshot of the argument. Note that <this> is immutable when the + * method returns. <this> must be empty when the function is called. + * @param r The source array, which is copied into <this> + */ + public void snapshot(@NonNull WatchedArrayList<E> r) { + snapshot(this, r); + } + + /** + * Make the destination a copy of the source. If the element is a subclass of Snapper then the + * copy contains snapshots of the elements. Otherwise the copy contains references to the + * elements. The destination must be initially empty. Upon return, the destination is + * immutable. + * @param dst The destination array. It must be empty. + * @param src The source array. It is not modified. + */ + public static <E> void snapshot(@NonNull WatchedArrayList<E> dst, + @NonNull WatchedArrayList<E> src) { + if (dst.size() != 0) { + throw new IllegalArgumentException("snapshot destination is not empty"); + } + final int end = src.size(); + dst.mStorage.ensureCapacity(end); + for (int i = 0; i < end; i++) { + final E val = Snapshots.maybeSnapshot(src.get(i)); + dst.add(i, val); + } + dst.seal(); + } +} diff --git a/services/core/java/com/android/server/utils/WatchedArrayMap.java b/services/core/java/com/android/server/utils/WatchedArrayMap.java index e8065f140af7..7c1cde8502bd 100644 --- a/services/core/java/com/android/server/utils/WatchedArrayMap.java +++ b/services/core/java/com/android/server/utils/WatchedArrayMap.java @@ -160,10 +160,48 @@ public class WatchedArrayMap<K, V> extends WatchableImpl } /** + * Create a {@link WatchedArrayMap} from an {@link ArrayMap} + */ + public WatchedArrayMap(@NonNull ArrayMap<K, V> c) { + mStorage = new ArrayMap<>(c); + } + + /** + * Create a {@link WatchedArrayMap} from an {@link WatchedArrayMap} + */ + public WatchedArrayMap(@NonNull WatchedArrayMap<K, V> c) { + mStorage = new ArrayMap<>(c.mStorage); + } + + /** + * Make <this> a copy of src. Any data in <this> is discarded. + */ + public void copyFrom(@NonNull ArrayMap<K, V> src) { + clear(); + final int end = src.size(); + mStorage.ensureCapacity(end); + for (int i = 0; i < end; i++) { + put(src.keyAt(i), src.valueAt(i)); + } + } + + /** + * Make dst a copy of <this>. Any previous data in dst is discarded. + */ + public void copyTo(@NonNull ArrayMap<K, V> dst) { + dst.clear(); + final int end = size(); + dst.ensureCapacity(end); + for (int i = 0; i < end; i++) { + dst.put(keyAt(i), valueAt(i)); + } + } + + /** * Return the underlying storage. This breaks the wrapper but is necessary when * passing the array to distant methods. */ - public ArrayMap untrackedMap() { + public ArrayMap<K, V> untrackedStorage() { return mStorage; } @@ -213,7 +251,7 @@ public class WatchedArrayMap<K, V> extends WatchableImpl * {@inheritDoc} */ @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (o instanceof WatchedArrayMap) { WatchedArrayMap w = (WatchedArrayMap) o; return mStorage.equals(w.mStorage); @@ -401,6 +439,15 @@ public class WatchedArrayMap<K, V> extends WatchableImpl } /** + * Make <this> a snapshot of the argument. Note that <this> is immutable when the + * method returns. <this> must be empty when the function is called. + * @param r The source array, which is copied into <this> + */ + public void snapshot(@NonNull WatchedArrayMap<K, V> r) { + snapshot(this, r); + } + + /** * Make the destination a copy of the source. If the element is a subclass of Snapper then the * copy contains snapshots of the elements. Otherwise the copy contains references to the * elements. The destination must be initially empty. Upon return, the destination is @@ -414,6 +461,7 @@ public class WatchedArrayMap<K, V> extends WatchableImpl throw new IllegalArgumentException("snapshot destination is not empty"); } final int end = src.size(); + dst.mStorage.ensureCapacity(end); for (int i = 0; i < end; i++) { final V val = Snapshots.maybeSnapshot(src.valueAt(i)); final K key = src.keyAt(i); diff --git a/services/core/java/com/android/server/utils/WatchedArraySet.java b/services/core/java/com/android/server/utils/WatchedArraySet.java new file mode 100644 index 000000000000..5070dd1675d3 --- /dev/null +++ b/services/core/java/com/android/server/utils/WatchedArraySet.java @@ -0,0 +1,434 @@ +/* + * 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.server.utils; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.util.ArraySet; + +/** + * WatchedArraySet is an {@link android.util.ArraySet} that can report changes to itself. If its + * values are {@link Watchable} then the WatchedArraySet will also report changes to the values. + * A {@link Watchable} is notified only once, no matter how many times it is stored in the array. + * @param <E> The element type + */ +public class WatchedArraySet<E> extends WatchableImpl + implements Snappable { + + // The storage + private final ArraySet<E> mStorage; + + // If true, the array is watching its children + private volatile boolean mWatching = false; + + // The local observer + private final Watcher mObserver = new Watcher() { + @Override + public void onChange(@Nullable Watchable what) { + WatchedArraySet.this.dispatchChange(what); + } + }; + + /** + * A convenience function called when the elements are added to or removed from the storage. + * The watchable is always {@link this}. + */ + private void onChanged() { + dispatchChange(this); + } + + /** + * A convenience function. Register the object if it is {@link Watchable} and if the + * array is currently watching. Note that the watching flag must be true if this + * function is to succeed. Also note that if this is called with the same object + * twice, <this> is only registered once. + */ + private void registerChild(Object o) { + if (mWatching && o instanceof Watchable) { + ((Watchable) o).registerObserver(mObserver); + } + } + + /** + * A convenience function. Unregister the object if it is {@link Watchable} and if the + * array is currently watching. This unconditionally removes the object from the + * registered list. + */ + private void unregisterChild(Object o) { + if (mWatching && o instanceof Watchable) { + ((Watchable) o).unregisterObserver(mObserver); + } + } + + /** + * A convenience function. Unregister the object if it is {@link Watchable}, if the + * array is currently watching, and if there are no other instances of this object in + * the storage. Note that the watching flag must be true if this function is to + * succeed. The object must already have been removed from the storage before this + * method is called. + */ + private void unregisterChildIf(Object o) { + if (mWatching && o instanceof Watchable) { + if (!mStorage.contains(o)) { + ((Watchable) o).unregisterObserver(mObserver); + } + } + } + + /** + * Register a {@link Watcher} with the array. If this is the first Watcher than any + * array values that are {@link Watchable} are registered to the array itself. + */ + @Override + public void registerObserver(@NonNull Watcher observer) { + super.registerObserver(observer); + if (registeredObserverCount() == 1) { + // The watching flag must be set true before any children are registered. + mWatching = true; + final int end = mStorage.size(); + for (int i = 0; i < end; i++) { + registerChild(mStorage.valueAt(i)); + } + } + } + + /** + * Unregister a {@link Watcher} from the array. If this is the last Watcher than any + * array values that are {@link Watchable} are unregistered to the array itself. + */ + @Override + public void unregisterObserver(@NonNull Watcher observer) { + super.unregisterObserver(observer); + if (registeredObserverCount() == 0) { + final int end = mStorage.size(); + for (int i = 0; i < end; i++) { + unregisterChild(mStorage.valueAt(i)); + } + // The watching flag must be true while children are unregistered. + mWatching = false; + } + } + + /** + * Create a new empty {@link WatchedArraySet}. The default capacity of an array map + * is 0, and will grow once items are added to it. + */ + public WatchedArraySet() { + this(0, false); + } + + /** + * Create a new {@link WatchedArraySet} with a given initial capacity. + */ + public WatchedArraySet(int capacity) { + this(capacity, false); + } + + /** {@hide} */ + public WatchedArraySet(int capacity, boolean identityHashCode) { + mStorage = new ArraySet<E>(capacity, identityHashCode); + } + + /** + * Create a new {@link WatchedArraySet} with items from the given array + */ + public WatchedArraySet(@Nullable E[] array) { + mStorage = new ArraySet(array); + } + + /** + * Create a {@link WatchedArraySet} from an {@link ArraySet} + */ + public WatchedArraySet(@NonNull ArraySet<E> c) { + mStorage = new ArraySet<>(c); + } + + /** + * Create a {@link WatchedArraySet} from an {@link WatchedArraySet} + */ + public WatchedArraySet(@NonNull WatchedArraySet<E> c) { + mStorage = new ArraySet<>(c.mStorage); + } + + /** + * Make <this> a copy of src. Any data in <this> is discarded. + */ + public void copyFrom(@NonNull ArraySet<E> src) { + clear(); + final int end = src.size(); + mStorage.ensureCapacity(end); + for (int i = 0; i < end; i++) { + add(src.valueAt(i)); + } + } + + /** + * Make dst a copy of <this>. Any previous data in dst is discarded. + */ + public void copyTo(@NonNull ArraySet<E> dst) { + dst.clear(); + final int end = size(); + dst.ensureCapacity(end); + for (int i = 0; i < end; i++) { + dst.add(valueAt(i)); + } + } + + /** + * Return the underlying storage. This breaks the wrapper but is necessary when + * passing the array to distant methods. + */ + public ArraySet<E> untrackedStorage() { + return mStorage; + } + + /** + * Make the array map empty. All storage is released. + */ + public void clear() { + // The storage cannot be simply cleared. Each element in the storage must be + // unregistered. Deregistration is only needed if the array is actually + // watching. + if (mWatching) { + final int end = mStorage.size(); + for (int i = 0; i < end; i++) { + unregisterChild(mStorage.valueAt(i)); + } + } + mStorage.clear(); + onChanged(); + } + + /** + * Check whether a value exists in the set. + * + * @param key The value to search for. + * @return Returns true if the value exists, else false. + */ + public boolean contains(Object key) { + return mStorage.contains(key); + } + + /** + * Returns the index of a value in the set. + * + * @param key The value to search for. + * @return Returns the index of the value if it exists, else a negative integer. + */ + public int indexOf(Object key) { + return mStorage.indexOf(key); + } + + /** + * Return the value at the given index in the array. + * + * <p>For indices outside of the range <code>0...size()-1</code>, an + * {@link ArrayIndexOutOfBoundsException} is thrown.</p> + * + * @param index The desired index, must be between 0 and {@link #size()}-1. + * @return Returns the value stored at the given index. + */ + public E valueAt(int index) { + return mStorage.valueAt(index); + } + + /** + * Return true if the array map contains no items. + */ + public boolean isEmpty() { + return mStorage.isEmpty(); + } + + /** + * Adds the specified object to this set. The set is not modified if it + * already contains the object. + * + * @param value the object to add. + * @return {@code true} if this set is modified, {@code false} otherwise. + */ + public boolean add(E value) { + final boolean result = mStorage.add(value); + registerChild(value); + onChanged(); + return result; + } + + /** + * Special fast path for appending items to the end of the array without validation. + * The array must already be large enough to contain the item. + * @hide + */ + public void append(E value) { + mStorage.append(value); + registerChild(value); + onChanged(); + } + + /** + * Perform a {@link #add(Object)} of all values in <var>array</var> + * @param array The array whose contents are to be retrieved. + */ + public void addAll(ArraySet<? extends E> array) { + final int end = array.size(); + for (int i = 0; i < end; i++) { + add(array.valueAt(i)); + } + } + + /** + * Perform a {@link #add(Object)} of all values in <var>array</var> + * @param array The array whose contents are to be retrieved. + */ + public void addAll(WatchedArraySet<? extends E> array) { + final int end = array.size(); + for (int i = 0; i < end; i++) { + add(array.valueAt(i)); + } + } + + /** + * Removes the specified object from this set. + * + * @param o the object to remove. + * @return {@code true} if this set was modified, {@code false} otherwise. + */ + public boolean remove(Object o) { + if (mStorage.remove(o)) { + unregisterChildIf(o); + onChanged(); + return true; + } + return false; + } + + /** + * Remove the key/value mapping at the given index. + * + * <p>For indices outside of the range <code>0...size()-1</code>, an + * {@link ArrayIndexOutOfBoundsException} is thrown.</p> + * + * @param index The desired index, must be between 0 and {@link #size()}-1. + * @return Returns the value that was stored at this index. + */ + public E removeAt(int index) { + final E result = mStorage.removeAt(index); + unregisterChildIf(result); + onChanged(); + return result; + } + + /** + * Perform a {@link #remove(Object)} of all values in <var>array</var> + * @param array The array whose contents are to be removed. + */ + public boolean removeAll(ArraySet<? extends E> array) { + final int end = array.size(); + boolean any = false; + for (int i = 0; i < end; i++) { + any = remove(array.valueAt(i)) || any; + } + return any; + } + + /** + * Return the number of items in this array map. + */ + public int size() { + return mStorage.size(); + } + + /** + * {@inheritDoc} + * + * <p>This implementation returns false if the object is not a set, or + * if the sets have different sizes. Otherwise, for each value in this + * set, it checks to make sure the value also exists in the other set. + * If any value doesn't exist, the method returns false; otherwise, it + * returns true. + */ + @Override + public boolean equals(@Nullable Object object) { + if (object instanceof WatchedArraySet) { + return mStorage.equals(((WatchedArraySet) object).mStorage); + } else { + return mStorage.equals(object); + } + } + + /** + * {@inheritDoc} + */ + @Override + public int hashCode() { + return mStorage.hashCode(); + } + + /** + * {@inheritDoc} + * + * <p>This implementation composes a string by iterating over its values. If + * this set contains itself as a value, the string "(this Set)" + * will appear in its place. + */ + @Override + public String toString() { + return mStorage.toString(); + } + + /** + * Create a copy of the array. If the element is a subclass of Snapper then the copy + * contains snapshots of the elements. Otherwise the copy contains references to the + * elements. The returned snapshot is immutable. + * @return A new array whose elements are the elements of <this>. + */ + public WatchedArraySet<E> snapshot() { + WatchedArraySet<E> l = new WatchedArraySet<>(); + snapshot(l, this); + return l; + } + + /** + * Make <this> a snapshot of the argument. Note that <this> is immutable when the + * method returns. <this> must be empty when the function is called. + * @param r The source array, which is copied into <this> + */ + public void snapshot(@NonNull WatchedArraySet<E> r) { + snapshot(this, r); + } + + /** + * Make the destination a copy of the source. If the element is a subclass of Snapper then the + * copy contains snapshots of the elements. Otherwise the copy contains references to the + * elements. The destination must be initially empty. Upon return, the destination is + * immutable. + * @param dst The destination array. It must be empty. + * @param src The source array. It is not modified. + */ + public static <E> void snapshot(@NonNull WatchedArraySet<E> dst, + @NonNull WatchedArraySet<E> src) { + if (dst.size() != 0) { + throw new IllegalArgumentException("snapshot destination is not empty"); + } + final int end = src.size(); + dst.mStorage.ensureCapacity(end); + for (int i = 0; i < end; i++) { + final E val = Snapshots.maybeSnapshot(src.valueAt(i)); + dst.append(val); + } + dst.seal(); + } +} diff --git a/services/core/java/com/android/server/utils/WatchedSparseArray.java b/services/core/java/com/android/server/utils/WatchedSparseArray.java index 6797c6eb7801..9b99b9176d19 100644 --- a/services/core/java/com/android/server/utils/WatchedSparseArray.java +++ b/services/core/java/com/android/server/utils/WatchedSparseArray.java @@ -143,6 +143,13 @@ public class WatchedSparseArray<E> extends WatchableImpl } /** + * Create a {@link WatchedSparseArray} from a {@link SparseArray} + */ + public WatchedSparseArray(@NonNull SparseArray<E> c) { + mStorage = c.clone(); + } + + /** * The copy constructor does not copy the watcher data. */ public WatchedSparseArray(@NonNull WatchedSparseArray<E> r) { @@ -150,6 +157,36 @@ public class WatchedSparseArray<E> extends WatchableImpl } /** + * Make <this> a copy of src. Any data in <this> is discarded. + */ + public void copyFrom(@NonNull SparseArray<E> src) { + clear(); + final int end = src.size(); + for (int i = 0; i < end; i++) { + put(src.keyAt(i), src.valueAt(i)); + } + } + + /** + * Make dst a copy of <this>. Any previous data in dst is discarded. + */ + public void copyTo(@NonNull SparseArray<E> dst) { + dst.clear(); + final int end = size(); + for (int i = 0; i < end; i++) { + dst.put(keyAt(i), valueAt(i)); + } + } + + /** + * Return the underlying storage. This breaks the wrapper but is necessary when + * passing the array to distant methods. + */ + public SparseArray<E> untrackedStorage() { + return mStorage; + } + + /** * Returns true if the key exists in the array. This is equivalent to * {@link #indexOfKey(int)} >= 0. * @@ -390,6 +427,21 @@ public class WatchedSparseArray<E> extends WatchableImpl onChanged(); } + @Override + public int hashCode() { + return mStorage.hashCode(); + } + + @Override + public boolean equals(@Nullable Object o) { + if (o instanceof WatchedSparseArray) { + WatchedSparseArray w = (WatchedSparseArray) o; + return mStorage.equals(w.mStorage); + } else { + return false; + } + } + /** * <p>This implementation composes a string by iterating over its mappings. If * this map contains itself as a value, the string "(this Map)" @@ -407,12 +459,21 @@ public class WatchedSparseArray<E> extends WatchableImpl * @return A new array whose elements are the elements of <this>. */ public WatchedSparseArray<E> snapshot() { - WatchedSparseArray<E> l = new WatchedSparseArray<>(); + WatchedSparseArray<E> l = new WatchedSparseArray<>(size()); snapshot(l, this); return l; } /** + * Make <this> a snapshot of the argument. Note that <this> is immutable when the + * method returns. <this> must be empty when the function is called. + * @param r The source array, which is copied into <this> + */ + public void snapshot(@NonNull WatchedSparseArray<E> r) { + snapshot(this, r); + } + + /** * Make the destination a copy of the source. If the element is a subclass of Snapper then the * copy contains snapshots of the elements. Otherwise the copy contains references to the * elements. The destination must be initially empty. Upon return, the destination is diff --git a/services/core/java/com/android/server/utils/WatchedSparseBooleanArray.java b/services/core/java/com/android/server/utils/WatchedSparseBooleanArray.java index b845eea168a5..772a8d07cffb 100644 --- a/services/core/java/com/android/server/utils/WatchedSparseBooleanArray.java +++ b/services/core/java/com/android/server/utils/WatchedSparseBooleanArray.java @@ -17,6 +17,7 @@ package com.android.server.utils; import android.annotation.NonNull; +import android.annotation.Nullable; import android.util.SparseBooleanArray; /** @@ -53,6 +54,13 @@ public class WatchedSparseBooleanArray extends WatchableImpl } /** + * Create a {@link WatchedSparseBooleanArray} from a {@link SparseBooleanArray} + */ + public WatchedSparseBooleanArray(@NonNull SparseBooleanArray c) { + mStorage = c.clone(); + } + + /** * The copy constructor does not copy the watcher data. */ public WatchedSparseBooleanArray(@NonNull WatchedSparseBooleanArray r) { @@ -60,6 +68,36 @@ public class WatchedSparseBooleanArray extends WatchableImpl } /** + * Make <this> a copy of src. Any data in <this> is discarded. + */ + public void copyFrom(@NonNull SparseBooleanArray src) { + clear(); + final int end = src.size(); + for (int i = 0; i < end; i++) { + put(src.keyAt(i), src.valueAt(i)); + } + } + + /** + * Make dst a copy of <this>. Any previous data in dst is discarded. + */ + public void copyTo(@NonNull SparseBooleanArray dst) { + dst.clear(); + final int end = size(); + for (int i = 0; i < end; i++) { + dst.put(keyAt(i), valueAt(i)); + } + } + + /** + * Return the underlying storage. This breaks the wrapper but is necessary when + * passing the array to distant methods. + */ + public SparseBooleanArray untrackedStorage() { + return mStorage; + } + + /** * Gets the boolean mapped from the specified key, or <code>false</code> * if no such mapping has been made. */ @@ -99,10 +137,10 @@ public class WatchedSparseBooleanArray extends WatchableImpl * was one. */ public void put(int key, boolean value) { - if (mStorage.get(key) != value) { - mStorage.put(key, value); - onChanged(); - } + // There is no fast way to know if the key exists with the input value, so this + // method always notifies change listeners. + mStorage.put(key, value); + onChanged(); } /** @@ -219,8 +257,13 @@ public class WatchedSparseBooleanArray extends WatchableImpl } @Override - public boolean equals(Object that) { - return this == that || mStorage.equals(that); + public boolean equals(@Nullable Object o) { + if (o instanceof WatchedSparseBooleanArray) { + WatchedSparseBooleanArray w = (WatchedSparseBooleanArray) o; + return mStorage.equals(w.mStorage); + } else { + return false; + } } /** @@ -249,13 +292,26 @@ public class WatchedSparseBooleanArray extends WatchableImpl * @param r The source array, which is copied into <this> */ public void snapshot(@NonNull WatchedSparseBooleanArray r) { - if (size() != 0) { + snapshot(this, r); + } + + /** + * Make the destination a copy of the source. If the element is a subclass of Snapper then the + * copy contains snapshots of the elements. Otherwise the copy contains references to the + * elements. The destination must be initially empty. Upon return, the destination is + * immutable. + * @param dst The destination array. It must be empty. + * @param src The source array. It is not modified. + */ + public static void snapshot(@NonNull WatchedSparseBooleanArray dst, + @NonNull WatchedSparseBooleanArray src) { + if (dst.size() != 0) { throw new IllegalArgumentException("snapshot destination is not empty"); } - final int end = r.size(); + final int end = src.size(); for (int i = 0; i < end; i++) { - put(r.keyAt(i), r.valueAt(i)); + dst.put(src.keyAt(i), src.valueAt(i)); } - seal(); + dst.seal(); } } diff --git a/services/core/java/com/android/server/utils/WatchedSparseIntArray.java b/services/core/java/com/android/server/utils/WatchedSparseIntArray.java new file mode 100644 index 000000000000..72705bf24199 --- /dev/null +++ b/services/core/java/com/android/server/utils/WatchedSparseIntArray.java @@ -0,0 +1,323 @@ +/* + * 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.server.utils; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.util.SparseIntArray; + +/** + * A watched variant of SparseIntArray. Changes to the array are notified to + * registered {@link Watcher}s. + */ +public class WatchedSparseIntArray extends WatchableImpl + implements Snappable { + + // The storage + private final SparseIntArray mStorage; + + // A private convenience function + private void onChanged() { + dispatchChange(this); + } + + /** + * Creates a new WatchedSparseIntArray containing no mappings. + */ + public WatchedSparseIntArray() { + mStorage = new SparseIntArray(); + } + + /** + * Creates a new WatchedSparseIntArray containing no mappings that + * will not require any additional memory allocation to store the + * specified number of mappings. If you supply an initial capacity of + * 0, the sparse array will be initialized with a light-weight + * representation not requiring any additional array allocations. + */ + public WatchedSparseIntArray(int initialCapacity) { + mStorage = new SparseIntArray(initialCapacity); + } + + /** + * Create a {@link WatchedSparseIntArray} from a {@link SparseIntArray} + */ + public WatchedSparseIntArray(@NonNull SparseIntArray c) { + mStorage = c.clone(); + } + + /** + * The copy constructor does not copy the watcher data. + */ + public WatchedSparseIntArray(@NonNull WatchedSparseIntArray r) { + mStorage = r.mStorage.clone(); + } + + /** + * Make <this> a copy of src. Any data in <this> is discarded. + */ + public void copyFrom(@NonNull SparseIntArray src) { + clear(); + final int end = src.size(); + for (int i = 0; i < end; i++) { + put(src.keyAt(i), src.valueAt(i)); + } + } + + /** + * Make dst a copy of <this>. Any previous data in dst is discarded. + */ + public void copyTo(@NonNull SparseIntArray dst) { + dst.clear(); + final int end = size(); + for (int i = 0; i < end; i++) { + dst.put(keyAt(i), valueAt(i)); + } + } + + /** + * Return the underlying storage. This breaks the wrapper but is necessary when + * passing the array to distant methods. + */ + public SparseIntArray untrackedStorage() { + return mStorage; + } + + /** + * Gets the boolean mapped from the specified key, or <code>false</code> + * if no such mapping has been made. + */ + public int get(int key) { + return mStorage.get(key); + } + + /** + * Gets the boolean mapped from the specified key, or the specified value + * if no such mapping has been made. + */ + public int get(int key, int valueIfKeyNotFound) { + return mStorage.get(key, valueIfKeyNotFound); + } + + /** + * Removes the mapping from the specified key, if there was any. + */ + public void delete(int key) { + // This code ensures that onChanged is called only if the key is actually + // present. + final int index = mStorage.indexOfKey(key); + if (index >= 0) { + mStorage.removeAt(index); + onChanged(); + } + } + + /** + * Removes the mapping at the specified index. + */ + public void removeAt(int index) { + mStorage.removeAt(index); + onChanged(); + } + + /** + * Adds a mapping from the specified key to the specified value, + * replacing the previous mapping from the specified key if there + * was one. + */ + public void put(int key, int value) { + // There is no fast way to know if the key exists with the input value, so this + // method always notifies change listeners. + mStorage.put(key, value); + onChanged(); + } + + /** + * Returns the number of key-value mappings that this SparseIntArray + * currently stores. + */ + public int size() { + return mStorage.size(); + } + + /** + * Given an index in the range <code>0...size()-1</code>, returns + * the key from the <code>index</code>th key-value mapping that this + * SparseIntArray stores. + * + * <p>The keys corresponding to indices in ascending order are guaranteed to + * be in ascending order, e.g., <code>keyAt(0)</code> will return the + * smallest key and <code>keyAt(size()-1)</code> will return the largest + * key.</p> + * + * <p>For indices outside of the range <code>0...size()-1</code>, the behavior is undefined for + * apps targeting {@link android.os.Build.VERSION_CODES#P} and earlier, and an + * {@link ArrayIndexOutOfBoundsException} is thrown for apps targeting + * {@link android.os.Build.VERSION_CODES#Q} and later.</p> + */ + public int keyAt(int index) { + return mStorage.keyAt(index); + } + + /** + * Given an index in the range <code>0...size()-1</code>, returns + * the value from the <code>index</code>th key-value mapping that this + * SparseIntArray stores. + * + * <p>The values corresponding to indices in ascending order are guaranteed + * to be associated with keys in ascending order, e.g., + * <code>valueAt(0)</code> will return the value associated with the + * smallest key and <code>valueAt(size()-1)</code> will return the value + * associated with the largest key.</p> + * + * <p>For indices outside of the range <code>0...size()-1</code>, the behavior is undefined for + * apps targeting {@link android.os.Build.VERSION_CODES#P} and earlier, and an + * {@link ArrayIndexOutOfBoundsException} is thrown for apps targeting + * {@link android.os.Build.VERSION_CODES#Q} and later.</p> + */ + public int valueAt(int index) { + return mStorage.valueAt(index); + } + + /** + * Directly set the value at a particular index. + * + * <p>For indices outside of the range <code>0...size()-1</code>, the behavior is undefined for + * apps targeting {@link android.os.Build.VERSION_CODES#P} and earlier, and an + * {@link ArrayIndexOutOfBoundsException} is thrown for apps targeting + * {@link android.os.Build.VERSION_CODES#Q} and later.</p> + */ + public void setValueAt(int index, int value) { + if (mStorage.valueAt(index) != value) { + mStorage.setValueAt(index, value); + onChanged(); + } + } + + /** + * Returns the index for which {@link #keyAt} would return the + * specified key, or a negative number if the specified + * key is not mapped. + */ + public int indexOfKey(int key) { + return mStorage.indexOfKey(key); + } + + /** + * Returns an index for which {@link #valueAt} would return the + * specified key, or a negative number if no keys map to the + * specified value. + * Beware that this is a linear search, unlike lookups by key, + * and that multiple keys can map to the same value and this will + * find only one of them. + */ + public int indexOfValue(int value) { + return mStorage.indexOfValue(value); + } + + /** + * Removes all key-value mappings from this SparseIntArray. + */ + public void clear() { + final int count = size(); + mStorage.clear(); + if (count > 0) { + onChanged(); + } + } + + /** + * Puts a key/value pair into the array, optimizing for the case where + * the key is greater than all existing keys in the array. + */ + public void append(int key, int value) { + mStorage.append(key, value); + onChanged(); + } + + /** + * Provides a copy of keys. + **/ + public int[] copyKeys() { + return mStorage.copyKeys(); + } + + @Override + public int hashCode() { + return mStorage.hashCode(); + } + + @Override + public boolean equals(@Nullable Object o) { + if (o instanceof WatchedSparseIntArray) { + WatchedSparseIntArray w = (WatchedSparseIntArray) o; + return mStorage.equals(w.mStorage); + } else { + return false; + } + } + + /** + * {@inheritDoc} + * + * <p>This implementation composes a string by iterating over its mappings. + */ + @Override + public String toString() { + return mStorage.toString(); + } + + /** + * Create a snapshot. The snapshot does not include any {@link Watchable} + * information. + */ + public WatchedSparseIntArray snapshot() { + WatchedSparseIntArray l = new WatchedSparseIntArray(this); + l.seal(); + return l; + } + + /** + * Make <this> a snapshot of the argument. Note that <this> is immutable when the + * method returns. <this> must be empty when the function is called. + * @param r The source array, which is copied into <this> + */ + public void snapshot(@NonNull WatchedSparseIntArray r) { + snapshot(this, r); + } + + /** + * Make the destination a copy of the source. If the element is a subclass of Snapper then the + * copy contains snapshots of the elements. Otherwise the copy contains references to the + * elements. The destination must be initially empty. Upon return, the destination is + * immutable. + * @param dst The destination array. It must be empty. + * @param src The source array. It is not modified. + */ + public static void snapshot(@NonNull WatchedSparseIntArray dst, + @NonNull WatchedSparseIntArray src) { + if (dst.size() != 0) { + throw new IllegalArgumentException("snapshot destination is not empty"); + } + final int end = src.size(); + for (int i = 0; i < end; i++) { + dst.put(src.keyAt(i), src.valueAt(i)); + } + dst.seal(); + } + +} diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index 5289f8604f36..375c3e138a39 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -1618,7 +1618,8 @@ class ActivityStarter { mService.getTransitionController().collectExistenceChange(r); } if (newTransition != null) { - mService.getTransitionController().requestStartTransition(newTransition); + mService.getTransitionController().requestStartTransition(newTransition, + r.getTask()); } else { // Make the collecting transition wait until this request is ready. mService.getTransitionController().setReady(false); diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 61da564ebab0..9cbe2778359d 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -744,7 +744,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { mActivityClientController.onSystemReady(); mBlockActivityAfterHomeEnabled = DeviceConfig.getBoolean( DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, - BLOCK_ACTIVITY_STARTS_AFTER_HOME_FLAG, false); + BLOCK_ACTIVITY_STARTS_AFTER_HOME_FLAG, true); } } diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java index 9d291b12c2c9..73a6efdb6799 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java +++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java @@ -2207,6 +2207,7 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { } if (!task.supportsSplitScreenWindowingMode() || forceNonResizable) { + if (mService.getTransitionController().getTransitionPlayer() != null) return; // Dismiss docked stack. If task appeared to be in docked stack but is not resizable - // we need to move it to top of fullscreen stack, otherwise it will be covered. final TaskDisplayArea taskDisplayArea = task.getDisplayArea(); diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java index dde527d161e0..d562bde45925 100644 --- a/services/core/java/com/android/server/wm/AppTransitionController.java +++ b/services/core/java/com/android/server/wm/AppTransitionController.java @@ -154,7 +154,7 @@ public class AppTransitionController { ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "**** GOOD TO GO"); // TODO(new-app-transition): Remove code using appTransition.getAppTransition() final AppTransition appTransition = mDisplayContent.mAppTransition; - mDisplayContent.mSkipAppTransitionAnimation = false; + mDisplayContent.mNoAnimationNotifyOnTransitionFinished.clear(); appTransition.removeAppTransitionTimeoutCallbacks(); @@ -188,7 +188,9 @@ public class AppTransitionController { final @TransitionOldType int transit = getTransitCompatType( mDisplayContent.mAppTransition, mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps, - mWallpaperControllerLocked.getWallpaperTarget(), getOldWallpaper()); + mWallpaperControllerLocked.getWallpaperTarget(), getOldWallpaper(), + mDisplayContent.mSkipAppTransitionAnimation); + mDisplayContent.mSkipAppTransitionAnimation = false; ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "handleAppTransitionReady: displayId=%d appTransition={%s}" @@ -274,7 +276,8 @@ public class AppTransitionController { */ static @TransitionOldType int getTransitCompatType(AppTransition appTransition, ArraySet<ActivityRecord> openingApps, ArraySet<ActivityRecord> closingApps, - @Nullable WindowState wallpaperTarget, @Nullable WindowState oldWallpaper) { + @Nullable WindowState wallpaperTarget, @Nullable WindowState oldWallpaper, + boolean skipAppTransitionAnimation) { // Determine if closing and opening app token sets are wallpaper targets, in which case // special animations are needed. @@ -298,6 +301,10 @@ public class AppTransitionController { return TRANSIT_OLD_KEYGUARD_UNOCCLUDE; } + // This is not keyguard transition and one of the app has request to skip app transition. + if (skipAppTransitionAnimation) { + return WindowManager.TRANSIT_OLD_UNSET; + } final @TransitionFlags int flags = appTransition.getTransitFlags(); final @TransitionType int firstTransit = appTransition.getFirstAppTransition(); diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java index dc75bbeea452..6e8110e9c36e 100644 --- a/services/core/java/com/android/server/wm/RecentTasks.java +++ b/services/core/java/com/android/server/wm/RecentTasks.java @@ -1496,6 +1496,7 @@ class RecentTasks { final Task removedTask = mTasks.remove(removeIndex); if (removedTask != task) { if (removedTask.hasChild()) { + Slog.i(TAG, "Add " + removedTask + " to hidden list because adding " + task); // A non-empty task is replaced by a new task. Because the removed task is no longer // managed by the recent tasks list, add it to the hidden list to prevent the task // from becoming dangling. diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index a0e6b423f299..aa8865773795 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -4421,8 +4421,17 @@ class Task extends WindowContainer<WindowContainer> { sb.append(stringName); sb.append(" U="); sb.append(mUserId); - sb.append(" StackId="); - sb.append(getRootTaskId()); + final Task rootTask = getRootTask(); + if (rootTask != this) { + sb.append(" rootTaskId="); + sb.append(rootTask.mTaskId); + } + sb.append(" visible="); + sb.append(shouldBeVisible(null /* starting */)); + sb.append(" mode="); + sb.append(windowingModeToString(getWindowingMode())); + sb.append(" translucent="); + sb.append(isTranslucent(null /* starting */)); sb.append(" sz="); sb.append(getChildCount()); sb.append('}'); @@ -4432,10 +4441,7 @@ class Task extends WindowContainer<WindowContainer> { sb.append(Integer.toHexString(System.identityHashCode(this))); sb.append(" #"); sb.append(mTaskId); - sb.append(" visible=" + shouldBeVisible(null /* starting */)); sb.append(" type=" + activityTypeToString(getActivityType())); - sb.append(" mode=" + windowingModeToString(getWindowingMode())); - sb.append(" translucent=" + isTranslucent(null /* starting */)); if (affinity != null) { sb.append(" A="); sb.append(affinity); @@ -5180,9 +5186,6 @@ class Task extends WindowContainer<WindowContainer> { @Override public void setWindowingMode(int windowingMode) { - // Reset the cached result of toString() - stringName = null; - // Calling Task#setWindowingMode() for leaf task since this is the a specialization of // {@link #setWindowingMode(int)} for ActivityStack. if (!isRootTask()) { diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java index be78d7a18377..b37e3c42f568 100644 --- a/services/core/java/com/android/server/wm/Transition.java +++ b/services/core/java/com/android/server/wm/Transition.java @@ -37,6 +37,7 @@ import static android.window.TransitionInfo.FLAG_TRANSLUCENT; import android.annotation.IntDef; import android.annotation.NonNull; +import android.app.ActivityManager; import android.graphics.Point; import android.graphics.Rect; import android.os.Binder; @@ -567,6 +568,10 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe tmpList.add(wc); } for (WindowContainer p = wc.getParent(); p != null; p = p.getParent()) { + if (!p.isAttached() || !changes.get(p).hasChanged(p)) { + // Again, we're skipping no-ops + break; + } if (participants.contains(p)) { topParent = p; break; @@ -695,6 +700,7 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe final TransitionInfo.Change change = new TransitionInfo.Change( target.mRemoteToken != null ? target.mRemoteToken.toWindowContainerToken() : null, target.getSurfaceControl()); + // TODO(shell-transitions): Use leash for non-organized windows. if (info.mParent != null) { change.setParent(info.mParent.mRemoteToken.toWindowContainerToken()); } @@ -704,6 +710,12 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe change.setEndRelOffset(target.getBounds().left - target.getParent().getBounds().left, target.getBounds().top - target.getParent().getBounds().top); change.setFlags(info.getChangeFlags(target)); + final Task task = target.asTask(); + if (task != null) { + final ActivityManager.RunningTaskInfo tinfo = new ActivityManager.RunningTaskInfo(); + task.fillTaskInfo(tinfo); + change.setTaskInfo(tinfo); + } out.addChange(change); } diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java index 2f5d10afe3da..0fe0afa54dd3 100644 --- a/services/core/java/com/android/server/wm/TransitionController.java +++ b/services/core/java/com/android/server/wm/TransitionController.java @@ -21,6 +21,7 @@ import static android.view.WindowManager.TRANSIT_OPEN; import android.annotation.NonNull; import android.annotation.Nullable; +import android.app.ActivityManager; import android.os.IBinder; import android.os.RemoteException; import android.util.Slog; @@ -167,7 +168,8 @@ class TransitionController { // Make the collecting transition wait until this request is ready. mCollectingTransition.setReady(false); } else { - newTransition = requestStartTransition(createTransition(type, flags)); + newTransition = requestStartTransition(createTransition(type, flags), + trigger != null ? trigger.asTask() : null); } if (trigger != null) { if (isExistenceType(type)) { @@ -181,11 +183,16 @@ class TransitionController { /** Asks the transition player (shell) to start a created but not yet started transition. */ @NonNull - Transition requestStartTransition(@NonNull Transition transition) { + Transition requestStartTransition(@NonNull Transition transition, @Nullable Task startTask) { try { ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "Requesting StartTransition: %s", transition); - mTransitionPlayer.requestStartTransition(transition.mType, transition); + ActivityManager.RunningTaskInfo info = null; + if (startTask != null) { + info = new ActivityManager.RunningTaskInfo(); + startTask.fillTaskInfo(info); + } + mTransitionPlayer.requestStartTransition(transition.mType, transition, info); } catch (RemoteException e) { Slog.e(TAG, "Error requesting transition", e); transition.start(); diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 4eeae6c0710c..3bdc16f0107a 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -2332,15 +2332,9 @@ public class WindowManagerService extends IWindowManager.Stub } } - // We may be deferring layout passes at the moment, but since the client is interested - // in the new out values right now we need to force a layout. - mWindowPlacerLocked.performSurfacePlacement(true /* force */); - + // Create surfaceControl before surface placement otherwise layout will be skipped + // (because WS.isGoneForLayout() is true when there is no surface. if (shouldRelayout) { - Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "relayoutWindow: viewVisibility_1"); - - result = win.relayoutVisibleWindow(result, attrChanges); - try { result = createSurfaceControl(outSurfaceControl, result, win, winAnimator); } catch (Exception e) { @@ -2352,6 +2346,17 @@ public class WindowManagerService extends IWindowManager.Stub Binder.restoreCallingIdentity(origId); return 0; } + } + + // We may be deferring layout passes at the moment, but since the client is interested + // in the new out values right now we need to force a layout. + mWindowPlacerLocked.performSurfacePlacement(true /* force */); + + if (shouldRelayout) { + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "relayoutWindow: viewVisibility_1"); + + result = win.relayoutVisibleWindow(result, attrChanges); + if ((result & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) { focusMayChange = true; } diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java index 5f2b628e056a..b0e67ce17711 100644 --- a/services/core/java/com/android/server/wm/WindowOrganizerController.java +++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java @@ -19,8 +19,8 @@ package com.android.server.wm; import static android.Manifest.permission.READ_FRAME_BUFFER; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_ORGANIZER; -import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS; import static com.android.server.wm.ActivityTaskManagerService.LAYOUT_REASON_CONFIG_CHANGED; +import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS; import static com.android.server.wm.Task.FLAG_FORCE_HIDDEN_FOR_TASK_ORG; import static com.android.server.wm.WindowContainer.POSITION_BOTTOM; import static com.android.server.wm.WindowContainer.POSITION_TOP; @@ -163,6 +163,11 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub try { synchronized (mGlobalLock) { Transition transition = Transition.fromBinder(transitionToken); + // In cases where transition is already provided, the "readiness lifecycle" of the + // transition is determined outside of this transaction. However, if this is a + // direct call from shell, the entire transition lifecycle is contained in the + // provided transaction and thus we can setReady immediately after apply. + boolean needsSetReady = transition == null && t != null; if (transition == null) { if (type < 0) { throw new IllegalArgumentException("Can't create transition with no type"); @@ -174,6 +179,9 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub t = new WindowContainerTransaction(); } applyTransaction(t, -1 /*syncId*/, transition); + if (needsSetReady) { + transition.setReady(); + } return transition; } } finally { @@ -258,14 +266,21 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub } if (transition != null) { transition.collect(wc); - if (hop.isReparent() && hop.getNewParent() != null) { - final WindowContainer parentWc = - WindowContainer.fromBinder(hop.getNewParent()); - if (parentWc == null) { - Slog.e(TAG, "Can't resolve parent window from token"); - continue; + if (hop.isReparent()) { + if (wc.getParent() != null) { + // Collect the current parent. It's visibility may change as a result + // of this reparenting. + transition.collect(wc.getParent()); + } + if (hop.getNewParent() != null) { + final WindowContainer parentWc = + WindowContainer.fromBinder(hop.getNewParent()); + if (parentWc == null) { + Slog.e(TAG, "Can't resolve parent window from token"); + continue; + } + transition.collect(parentWc); } - transition.collect(parentWc); } } effects |= sanitizeAndApplyHierarchyOp(wc, hop); @@ -307,6 +322,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub if ((effects & TRANSACT_EFFECTS_LIFECYCLE) != 0) { // Already calls ensureActivityConfig mService.mRootWindowContainer.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS); + mService.mRootWindowContainer.resumeFocusedTasksTopActivities(); } else if ((effects & TRANSACT_EFFECTS_CLIENT_CONFIG) != 0) { final PooledConsumer f = PooledLambda.obtainConsumer( ActivityRecord::ensureActivityConfiguration, diff --git a/services/core/xsd/display-device-config/display-device-config.xsd b/services/core/xsd/display-device-config/display-device-config.xsd index 88964b746aca..1cb9e5786a70 100644 --- a/services/core/xsd/display-device-config/display-device-config.xsd +++ b/services/core/xsd/display-device-config/display-device-config.xsd @@ -33,6 +33,7 @@ <xs:annotation name="nonnull"/> <xs:annotation name="final"/> </xs:element> + <xs:element type="highBrightnessMode" name="highBrightnessMode" minOccurs="0" maxOccurs="1"/> <xs:element type="displayQuirks" name="quirks" minOccurs="0" maxOccurs="1" /> </xs:sequence> </xs:complexType> @@ -46,6 +47,38 @@ </xs:sequence> </xs:complexType> + <xs:complexType name="highBrightnessMode"> + <xs:all> + <xs:element name="transitionPoint" type="nonNegativeDecimal" minOccurs="1" maxOccurs="1"> + <xs:annotation name="nonnull"/> + <xs:annotation name="final"/> + </xs:element> + <xs:element name="minimumLux" type="nonNegativeDecimal" minOccurs="1" maxOccurs="1"> + <xs:annotation name="nonnull"/> + <xs:annotation name="final"/> + </xs:element> + <xs:element name="timing" type="hbmTiming" minOccurs="1" maxOccurs="1"/> + </xs:all> + <xs:attribute name="enabled" type="xs:boolean" use="optional"/> + </xs:complexType> + + <xs:complexType name="hbmTiming"> + <xs:all> + <xs:element name="timeWindowSecs" type="xs:nonNegativeInteger" minOccurs="1" maxOccurs="1"> + <xs:annotation name="nonnull"/> + <xs:annotation name="final"/> + </xs:element> + <xs:element name="timeMaxSecs" type="xs:nonNegativeInteger" minOccurs="1" maxOccurs="1"> + <xs:annotation name="nonnull"/> + <xs:annotation name="final"/> + </xs:element> + <xs:element name="timeMinSecs" type="xs:nonNegativeInteger" minOccurs="1" maxOccurs="1"> + <xs:annotation name="nonnull"/> + <xs:annotation name="final"/> + </xs:element> + </xs:all> + </xs:complexType> + <xs:complexType name="nitsMap"> <xs:sequence> <xs:element name="point" type="point" maxOccurs="unbounded" minOccurs="2"> diff --git a/services/core/xsd/display-device-config/schema/current.txt b/services/core/xsd/display-device-config/schema/current.txt index 6906fda5b76f..e073ab3675ca 100644 --- a/services/core/xsd/display-device-config/schema/current.txt +++ b/services/core/xsd/display-device-config/schema/current.txt @@ -3,9 +3,11 @@ package com.android.server.display.config { public class DisplayConfiguration { ctor public DisplayConfiguration(); + method public com.android.server.display.config.HighBrightnessMode getHighBrightnessMode(); method public com.android.server.display.config.DisplayQuirks getQuirks(); method @NonNull public final java.math.BigDecimal getScreenBrightnessDefault(); method @NonNull public final com.android.server.display.config.NitsMap getScreenBrightnessMap(); + method public void setHighBrightnessMode(com.android.server.display.config.HighBrightnessMode); method public void setQuirks(com.android.server.display.config.DisplayQuirks); method public final void setScreenBrightnessDefault(@NonNull java.math.BigDecimal); method public final void setScreenBrightnessMap(@NonNull com.android.server.display.config.NitsMap); @@ -16,6 +18,28 @@ package com.android.server.display.config { method public java.util.List<java.lang.String> getQuirk(); } + public class HbmTiming { + ctor public HbmTiming(); + method @NonNull public final java.math.BigInteger getTimeMaxSecs_all(); + method @NonNull public final java.math.BigInteger getTimeMinSecs_all(); + method @NonNull public final java.math.BigInteger getTimeWindowSecs_all(); + method public final void setTimeMaxSecs_all(@NonNull java.math.BigInteger); + method public final void setTimeMinSecs_all(@NonNull java.math.BigInteger); + method public final void setTimeWindowSecs_all(@NonNull java.math.BigInteger); + } + + public class HighBrightnessMode { + ctor public HighBrightnessMode(); + method public boolean getEnabled(); + method @NonNull public final java.math.BigDecimal getMinimumLux_all(); + method public com.android.server.display.config.HbmTiming getTiming_all(); + method @NonNull public final java.math.BigDecimal getTransitionPoint_all(); + method public void setEnabled(boolean); + method public final void setMinimumLux_all(@NonNull java.math.BigDecimal); + method public void setTiming_all(com.android.server.display.config.HbmTiming); + method public final void setTransitionPoint_all(@NonNull java.math.BigDecimal); + } + public class NitsMap { ctor public NitsMap(); method @NonNull public final java.util.List<com.android.server.display.config.Point> getPoint(); diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/CallerIdentity.java b/services/devicepolicy/java/com/android/server/devicepolicy/CallerIdentity.java index b6b4d8a04cb6..4e422b2374b2 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/CallerIdentity.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/CallerIdentity.java @@ -25,7 +25,7 @@ import android.os.UserHandle; * All parameters are verified on object creation unless the component name is null and the * caller is a delegate. */ -class CallerIdentity { +final class CallerIdentity { private final int mUid; @Nullable @@ -51,7 +51,7 @@ class CallerIdentity { return UserHandle.getUserHandleForUid(mUid); } - @Nullable public String getPackageName() { + @Nullable public String getPackageName() { return mPackageName; } @@ -66,4 +66,16 @@ class CallerIdentity { public boolean hasPackage() { return mPackageName != null; } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder("CallerIdentity[uid=").append(mUid); + if (mPackageName != null) { + builder.append(", pkg=").append(mPackageName); + } + if (mComponentName != null) { + builder.append(", cmp=").append(mComponentName.flattenToShortString()); + } + return builder.append("]").toString(); + } } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index cd89f9ec12c2..fdbd85a77a5b 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -983,8 +983,20 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public void setDevicePolicySafetyChecker(DevicePolicySafetyChecker safetyChecker) { - Slog.i(LOG_TAG, "Setting DevicePolicySafetyChecker as " + safetyChecker); + CallerIdentity callerIdentity = getCallerIdentity(); + Preconditions.checkCallAuthorization(mIsAutomotive || isAdb(callerIdentity), "can only set " + + "DevicePolicySafetyChecker on automotive builds or from ADB (but caller is %s)", + callerIdentity); + setDevicePolicySafetyCheckerUnchecked(safetyChecker); + } + + /** + * Used by {@code setDevicePolicySafetyChecker()} above and {@link OneTimeSafetyChecker}. + */ + void setDevicePolicySafetyCheckerUnchecked(DevicePolicySafetyChecker safetyChecker) { + Slog.i(LOG_TAG, String.format("Setting DevicePolicySafetyChecker as %s", safetyChecker)); mSafetyChecker = safetyChecker; + mInjector.setDevicePolicySafetyChecker(safetyChecker); } /** @@ -1021,8 +1033,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { public void setNextOperationSafety(@DevicePolicyOperation int operation, boolean safe) { Preconditions.checkCallAuthorization( hasCallingOrSelfPermission(permission.MANAGE_DEVICE_ADMINS)); - Slog.i(LOG_TAG, "setNextOperationSafety(" + DevicePolicyManager.operationToString(operation) - + ", " + safe + ")"); + Slog.i(LOG_TAG, String.format("setNextOperationSafety(%s, %b)", + DevicePolicyManager.operationToString(operation), safe)); mSafetyChecker = new OneTimeSafetyChecker(this, operation, safe); } @@ -1034,6 +1046,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { public final Context mContext; + private @Nullable DevicePolicySafetyChecker mSafetyChecker; + Injector(Context context) { mContext = context; } @@ -1278,7 +1292,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { void recoverySystemRebootWipeUserData(boolean shutdown, String reason, boolean force, boolean wipeEuicc, boolean wipeExtRequested, boolean wipeResetProtectionData) throws IOException { - FactoryResetter.newBuilder(mContext).setReason(reason).setShutdown(shutdown) + FactoryResetter.newBuilder(mContext).setSafetyChecker(mSafetyChecker) + .setReason(reason).setShutdown(shutdown) .setForce(force).setWipeEuicc(wipeEuicc) .setWipeAdoptableStorage(wipeExtRequested) .setWipeFactoryResetProtection(wipeResetProtectionData) @@ -1433,6 +1448,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { public boolean isChangeEnabled(long changeId, String packageName, int userId) { return CompatChanges.isChangeEnabled(changeId, packageName, UserHandle.of(userId)); } + + void setDevicePolicySafetyChecker(DevicePolicySafetyChecker safetyChecker) { + mSafetyChecker = safetyChecker; + } } /** diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/FactoryResetter.java b/services/devicepolicy/java/com/android/server/devicepolicy/FactoryResetter.java index a0cf7a31650d..564950bef4f5 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/FactoryResetter.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/FactoryResetter.java @@ -17,14 +17,18 @@ package com.android.server.devicepolicy; import android.annotation.Nullable; +import android.app.admin.DevicePolicySafetyChecker; import android.content.Context; import android.content.pm.PackageManager; +import android.os.Bundle; import android.os.RecoverySystem; +import android.os.RemoteException; import android.os.UserManager; import android.os.storage.StorageManager; import android.service.persistentdata.PersistentDataBlockManager; -import android.util.Log; +import android.util.Slog; +import com.android.internal.os.IResultReceiver; import com.android.internal.util.Preconditions; import java.io.IOException; @@ -38,6 +42,7 @@ public final class FactoryResetter { private static final String TAG = FactoryResetter.class.getSimpleName(); private final Context mContext; + private final @Nullable DevicePolicySafetyChecker mSafetyChecker; private final @Nullable String mReason; private final boolean mShutdown; private final boolean mForce; @@ -45,18 +50,40 @@ public final class FactoryResetter { private final boolean mWipeAdoptableStorage; private final boolean mWipeFactoryResetProtection; - /** * Factory reset the device according to the builder's arguments. */ public void factoryReset() throws IOException { - Log.i(TAG, String.format("factoryReset(): reason=%s, shutdown=%b, force=%b, wipeEuicc=%b" - + ", wipeAdoptableStorage=%b, wipeFRP=%b", mReason, mShutdown, mForce, mWipeEuicc, - mWipeAdoptableStorage, mWipeFactoryResetProtection)); - Preconditions.checkCallAuthorization(mContext.checkCallingOrSelfPermission( android.Manifest.permission.MASTER_CLEAR) == PackageManager.PERMISSION_GRANTED); + if (mSafetyChecker == null) { + factoryResetInternalUnchecked(); + return; + } + + IResultReceiver receiver = new IResultReceiver.Stub() { + @Override + public void send(int resultCode, Bundle resultData) throws RemoteException { + Slog.i(TAG, String.format("Factory reset confirmed by %s, proceeding", + mSafetyChecker)); + try { + factoryResetInternalUnchecked(); + } catch (IOException e) { + // Shouldn't happen + Slog.wtf(TAG, "IOException calling underlying systems", e); + } + } + }; + Slog.i(TAG, String.format("Delaying factory reset until %s confirms", mSafetyChecker)); + mSafetyChecker.onFactoryReset(receiver); + } + + private void factoryResetInternalUnchecked() throws IOException { + Slog.i(TAG, String.format("factoryReset(): reason=%s, shutdown=%b, force=%b, wipeEuicc=%b, " + + "wipeAdoptableStorage=%b, wipeFRP=%b", mReason, mShutdown, mForce, mWipeEuicc, + mWipeAdoptableStorage, mWipeFactoryResetProtection)); + UserManager um = mContext.getSystemService(UserManager.class); if (!mForce && um.hasUserRestriction(UserManager.DISALLOW_FACTORY_RESET)) { throw new SecurityException("Factory reset is not allowed for this user."); @@ -66,15 +93,15 @@ public final class FactoryResetter { PersistentDataBlockManager manager = mContext .getSystemService(PersistentDataBlockManager.class); if (manager != null) { - Log.w(TAG, "Wiping factory reset protection"); + Slog.w(TAG, "Wiping factory reset protection"); manager.wipe(); } else { - Log.w(TAG, "No need to wipe factory reset protection"); + Slog.w(TAG, "No need to wipe factory reset protection"); } } if (mWipeAdoptableStorage) { - Log.w(TAG, "Wiping adoptable storage"); + Slog.w(TAG, "Wiping adoptable storage"); StorageManager sm = mContext.getSystemService(StorageManager.class); sm.wipeAdoptableDisks(); } @@ -84,8 +111,9 @@ public final class FactoryResetter { private FactoryResetter(Builder builder) { mContext = builder.mContext; - mShutdown = builder.mShutdown; + mSafetyChecker = builder.mSafetyChecker; mReason = builder.mReason; + mShutdown = builder.mShutdown; mForce = builder.mForce; mWipeEuicc = builder.mWipeEuicc; mWipeAdoptableStorage = builder.mWipeAdoptableStorage; @@ -105,6 +133,7 @@ public final class FactoryResetter { public static final class Builder { private final Context mContext; + private @Nullable DevicePolicySafetyChecker mSafetyChecker; private @Nullable String mReason; private boolean mShutdown; private boolean mForce; @@ -117,6 +146,15 @@ public final class FactoryResetter { } /** + * Sets a {@link DevicePolicySafetyChecker} object that will be used to delay the + * factory reset when it's not safe to do so. + */ + public Builder setSafetyChecker(@Nullable DevicePolicySafetyChecker safetyChecker) { + mSafetyChecker = safetyChecker; + return this; + } + + /** * Sets the (non-null) reason for the factory reset that is visible in the logs */ public Builder setReason(String reason) { diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/OneTimeSafetyChecker.java b/services/devicepolicy/java/com/android/server/devicepolicy/OneTimeSafetyChecker.java index b0f8bfb713ab..f7a826156d68 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/OneTimeSafetyChecker.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/OneTimeSafetyChecker.java @@ -21,6 +21,8 @@ import android.app.admin.DevicePolicyManager.DevicePolicyOperation; import android.app.admin.DevicePolicySafetyChecker; import android.util.Slog; +import com.android.internal.os.IResultReceiver; + import java.util.Objects; //TODO(b/172376923): add unit tests @@ -61,7 +63,12 @@ final class OneTimeSafetyChecker implements DevicePolicySafetyChecker { } Slog.i(TAG, "isDevicePolicyOperationSafe(" + name + "): returning " + safe + " and restoring DevicePolicySafetyChecker to " + mRealSafetyChecker); - mService.setDevicePolicySafetyChecker(mRealSafetyChecker); + mService.setDevicePolicySafetyCheckerUnchecked(mRealSafetyChecker); return safe; } + + @Override + public void onFactoryReset(IResultReceiver callback) { + throw new UnsupportedOperationException(); + } } diff --git a/services/musicrecognition/OWNERS b/services/musicrecognition/OWNERS new file mode 100644 index 000000000000..58f5d40dd8c3 --- /dev/null +++ b/services/musicrecognition/OWNERS @@ -0,0 +1,6 @@ +# Bug component: 830636 + +joannechung@google.com +oni@google.com +volnov@google.com + diff --git a/services/tests/mockingservicestests/src/com/android/server/devicepolicy/FactoryResetterTest.java b/services/tests/mockingservicestests/src/com/android/server/devicepolicy/FactoryResetterTest.java index 829e3124dc39..c4d14f947cda 100644 --- a/services/tests/mockingservicestests/src/com/android/server/devicepolicy/FactoryResetterTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/devicepolicy/FactoryResetterTest.java @@ -26,6 +26,7 @@ import static org.mockito.Mockito.never; import static org.mockito.Mockito.when; import static org.testng.Assert.assertThrows; +import android.app.admin.DevicePolicySafetyChecker; import android.content.Context; import android.content.pm.PackageManager; import android.os.RecoverySystem; @@ -35,6 +36,8 @@ import android.platform.test.annotations.Presubmit; import android.service.persistentdata.PersistentDataBlockManager; import android.util.Log; +import com.android.internal.os.IResultReceiver; + import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -58,6 +61,7 @@ public final class FactoryResetterTest { private @Mock StorageManager mSm; private @Mock PersistentDataBlockManager mPdbm; private @Mock UserManager mUm; + private @Mock DevicePolicySafetyChecker mSafetyChecker; @Before public void startSession() { @@ -69,7 +73,7 @@ public final class FactoryResetterTest { when(mContext.getSystemService(any(Class.class))).thenAnswer((inv) -> { Log.d(TAG, "Mocking " + inv); - Class serviceClass = (Class) inv.getArguments()[0]; + Class<?> serviceClass = (Class<?>) inv.getArguments()[0]; if (serviceClass.equals(PersistentDataBlockManager.class)) return mPdbm; if (serviceClass.equals(StorageManager.class)) return mSm; if (serviceClass.equals(UserManager.class)) return mUm; @@ -194,6 +198,44 @@ public final class FactoryResetterTest { verifyRebootWipeUserDataAllArgsCalled(); } + @Test + public void testFactoryReset_minimumArgs_safetyChecker_neverReplied() throws Exception { + allowFactoryReset(); + + FactoryResetter.newBuilder(mContext).setSafetyChecker(mSafetyChecker).build() + .factoryReset(); + + verifyWipeAdoptableStorageNotCalled(); + verifyWipeFactoryResetProtectionNotCalled(); + verifyRebootWipeUserDataNotCalled(); + } + + @Test + public void testFactoryReset_allArgs_safetyChecker_replied() throws Exception { + allowFactoryReset(); + + doAnswer((inv) -> { + Log.d(TAG, "Mocking " + inv); + IResultReceiver receiver = (IResultReceiver) inv.getArguments()[0]; + receiver.send(0, null); + return null; + }).when(mSafetyChecker).onFactoryReset(any()); + + FactoryResetter.newBuilder(mContext) + .setSafetyChecker(mSafetyChecker) + .setReason(REASON) + .setForce(true) + .setShutdown(true) + .setWipeEuicc(true) + .setWipeAdoptableStorage(true) + .setWipeFactoryResetProtection(true) + .build().factoryReset(); + + verifyWipeAdoptableStorageCalled(); + verifyWipeFactoryResetProtectionCalled(); + verifyRebootWipeUserDataAllArgsCalled(); + } + private void revokeMasterClearPermission() { when(mContext.checkCallingOrSelfPermission(android.Manifest.permission.MASTER_CLEAR)) .thenReturn(PackageManager.PERMISSION_DENIED); diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java index 78bcc13c9265..4740df5a2a44 100644 --- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java @@ -383,27 +383,6 @@ public class ConnectivityControllerTest { } @Test - public void testWouldBeReadyWithConnectivityLocked() { - final ConnectivityController controller = spy(new ConnectivityController(mService)); - final JobStatus red = createJobStatus(createJob() - .setEstimatedNetworkBytes(DataUnit.MEBIBYTES.toBytes(1), 0) - .setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED), UID_RED); - - doReturn(false).when(controller).isNetworkAvailable(any()); - assertFalse(controller.wouldBeReadyWithConnectivityLocked(red)); - - doReturn(true).when(controller).isNetworkAvailable(any()); - doReturn(false).when(controller).wouldBeReadyWithConstraintLocked(any(), - eq(JobStatus.CONSTRAINT_CONNECTIVITY)); - assertFalse(controller.wouldBeReadyWithConnectivityLocked(red)); - - doReturn(true).when(controller).isNetworkAvailable(any()); - doReturn(true).when(controller).wouldBeReadyWithConstraintLocked(any(), - eq(JobStatus.CONSTRAINT_CONNECTIVITY)); - assertTrue(controller.wouldBeReadyWithConnectivityLocked(red)); - } - - @Test public void testEvaluateStateLocked_JobWithoutConnectivity() { final ConnectivityController controller = new ConnectivityController(mService); final JobStatus red = createJobStatus(createJob().setMinimumLatency(1)); @@ -417,7 +396,9 @@ public class ConnectivityControllerTest { @Test public void testEvaluateStateLocked_JobWouldBeReady() { final ConnectivityController controller = spy(new ConnectivityController(mService)); - doReturn(true).when(controller).wouldBeReadyWithConnectivityLocked(any()); + doReturn(true).when(controller) + .wouldBeReadyWithConstraintLocked(any(), eq(JobStatus.CONSTRAINT_CONNECTIVITY)); + doReturn(true).when(controller).isNetworkAvailable(any()); final JobStatus red = createJobStatus(createJob() .setEstimatedNetworkBytes(DataUnit.MEBIBYTES.toBytes(1), 0) .setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED), UID_RED); @@ -455,7 +436,8 @@ public class ConnectivityControllerTest { @Test public void testEvaluateStateLocked_JobWouldNotBeReady() { final ConnectivityController controller = spy(new ConnectivityController(mService)); - doReturn(false).when(controller).wouldBeReadyWithConnectivityLocked(any()); + doReturn(false).when(controller) + .wouldBeReadyWithConstraintLocked(any(), eq(JobStatus.CONSTRAINT_CONNECTIVITY)); final JobStatus red = createJobStatus(createJob() .setEstimatedNetworkBytes(DataUnit.MEBIBYTES.toBytes(1), 0) .setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED), UID_RED); @@ -523,21 +505,26 @@ public class ConnectivityControllerTest { .setAppIdleWhitelist(eq(12345), anyBoolean()); // Both jobs would still be ready. Exception should not be revoked. - doReturn(true).when(controller).wouldBeReadyWithConnectivityLocked(any()); + doReturn(true).when(controller) + .wouldBeReadyWithConstraintLocked(any(), eq(JobStatus.CONSTRAINT_CONNECTIVITY)); + doReturn(true).when(controller).isNetworkAvailable(any()); controller.reevaluateStateLocked(UID_RED); inOrder.verify(mNetPolicyManagerInternal, never()) .setAppIdleWhitelist(eq(UID_RED), anyBoolean()); // One job is still ready. Exception should not be revoked. - doReturn(true).when(controller).wouldBeReadyWithConnectivityLocked(eq(redOne)); - doReturn(false).when(controller).wouldBeReadyWithConnectivityLocked(eq(redTwo)); + doReturn(true).when(controller).wouldBeReadyWithConstraintLocked( + eq(redOne), eq(JobStatus.CONSTRAINT_CONNECTIVITY)); + doReturn(false).when(controller).wouldBeReadyWithConstraintLocked( + eq(redTwo), eq(JobStatus.CONSTRAINT_CONNECTIVITY)); controller.reevaluateStateLocked(UID_RED); inOrder.verify(mNetPolicyManagerInternal, never()) .setAppIdleWhitelist(eq(UID_RED), anyBoolean()); assertTrue(controller.isStandbyExceptionRequestedLocked(UID_RED)); // Both jobs are not ready. Exception should be revoked. - doReturn(false).when(controller).wouldBeReadyWithConnectivityLocked(any()); + doReturn(false).when(controller) + .wouldBeReadyWithConstraintLocked(any(), eq(JobStatus.CONSTRAINT_CONNECTIVITY)); controller.reevaluateStateLocked(UID_RED); inOrder.verify(mNetPolicyManagerInternal, times(1)) .setAppIdleWhitelist(eq(UID_RED), eq(false)); 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 05f1ed8a20b3..1a65894f85b1 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 @@ -136,6 +136,8 @@ public class QuotaControllerTest { @Mock private JobSchedulerService mJobSchedulerService; @Mock + private PackageManagerInternal mPackageManagerInternal; + @Mock private UsageStatsManagerInternal mUsageStatsManager; private JobStore mJobStore; @@ -172,7 +174,7 @@ public class QuotaControllerTest { doReturn(mUsageStatsManager) .when(() -> LocalServices.getService(UsageStatsManagerInternal.class)); // Used in JobStatus. - doReturn(mock(PackageManagerInternal.class)) + doReturn(mPackageManagerInternal) .when(() -> LocalServices.getService(PackageManagerInternal.class)); // Used in QuotaController.Handler. mJobStore = JobStore.initAndGetForTesting(mContext, mContext.getFilesDir()); @@ -2377,6 +2379,7 @@ public class QuotaControllerTest { setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_FREQUENT_MS, 1 * HOUR_IN_MILLIS); setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_RARE_MS, 30 * MINUTE_IN_MILLIS); setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_RESTRICTED_MS, 27 * MINUTE_IN_MILLIS); + setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_SPECIAL_ADDITION_MS, 10 * HOUR_IN_MILLIS); setDeviceConfigLong(QcConstants.KEY_EJ_WINDOW_SIZE_MS, 12 * HOUR_IN_MILLIS); setDeviceConfigLong(QcConstants.KEY_EJ_TOP_APP_TIME_CHUNK_SIZE_MS, 10 * MINUTE_IN_MILLIS); setDeviceConfigLong(QcConstants.KEY_EJ_REWARD_TOP_APP_MS, 87 * SECOND_IN_MILLIS); @@ -2414,6 +2417,7 @@ public class QuotaControllerTest { assertEquals(HOUR_IN_MILLIS, mQuotaController.getEJLimitsMs()[FREQUENT_INDEX]); assertEquals(30 * MINUTE_IN_MILLIS, mQuotaController.getEJLimitsMs()[RARE_INDEX]); assertEquals(27 * MINUTE_IN_MILLIS, mQuotaController.getEJLimitsMs()[RESTRICTED_INDEX]); + assertEquals(10 * HOUR_IN_MILLIS, mQuotaController.getEjLimitSpecialAdditionMs()); assertEquals(12 * HOUR_IN_MILLIS, mQuotaController.getEJLimitWindowSizeMs()); assertEquals(10 * MINUTE_IN_MILLIS, mQuotaController.getEJTopAppTimeChunkSizeMs()); assertEquals(87 * SECOND_IN_MILLIS, mQuotaController.getEJRewardTopAppMs()); @@ -2452,6 +2456,7 @@ public class QuotaControllerTest { setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_FREQUENT_MS, -1); setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_RARE_MS, -1); setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_RESTRICTED_MS, -1); + setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_SPECIAL_ADDITION_MS, -1); setDeviceConfigLong(QcConstants.KEY_EJ_WINDOW_SIZE_MS, -1); setDeviceConfigLong(QcConstants.KEY_EJ_TOP_APP_TIME_CHUNK_SIZE_MS, -1); setDeviceConfigLong(QcConstants.KEY_EJ_REWARD_TOP_APP_MS, -1); @@ -2486,6 +2491,7 @@ public class QuotaControllerTest { assertEquals(10 * MINUTE_IN_MILLIS, mQuotaController.getEJLimitsMs()[FREQUENT_INDEX]); assertEquals(10 * MINUTE_IN_MILLIS, mQuotaController.getEJLimitsMs()[RARE_INDEX]); assertEquals(0, mQuotaController.getEJLimitsMs()[RESTRICTED_INDEX]); + assertEquals(0, mQuotaController.getEjLimitSpecialAdditionMs()); assertEquals(HOUR_IN_MILLIS, mQuotaController.getEJLimitWindowSizeMs()); assertEquals(1, mQuotaController.getEJTopAppTimeChunkSizeMs()); assertEquals(10 * SECOND_IN_MILLIS, mQuotaController.getEJRewardTopAppMs()); @@ -2518,6 +2524,7 @@ public class QuotaControllerTest { setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_FREQUENT_MS, 25 * HOUR_IN_MILLIS); setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_RARE_MS, 25 * HOUR_IN_MILLIS); setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_RESTRICTED_MS, 25 * HOUR_IN_MILLIS); + setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_SPECIAL_ADDITION_MS, 25 * HOUR_IN_MILLIS); setDeviceConfigLong(QcConstants.KEY_EJ_WINDOW_SIZE_MS, 25 * HOUR_IN_MILLIS); setDeviceConfigLong(QcConstants.KEY_EJ_TOP_APP_TIME_CHUNK_SIZE_MS, 25 * HOUR_IN_MILLIS); setDeviceConfigLong(QcConstants.KEY_EJ_REWARD_TOP_APP_MS, 25 * HOUR_IN_MILLIS); @@ -2542,6 +2549,7 @@ public class QuotaControllerTest { assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getEJLimitsMs()[FREQUENT_INDEX]); assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getEJLimitsMs()[RARE_INDEX]); assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getEJLimitsMs()[RESTRICTED_INDEX]); + assertEquals(0, mQuotaController.getEjLimitSpecialAdditionMs()); assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getEJLimitWindowSizeMs()); assertEquals(15 * MINUTE_IN_MILLIS, mQuotaController.getEJTopAppTimeChunkSizeMs()); assertEquals(15 * MINUTE_IN_MILLIS, mQuotaController.getEJRewardTopAppMs()); @@ -3498,6 +3506,37 @@ public class QuotaControllerTest { } @Test + public void testGetRemainingEJExecutionTimeLocked_SpecialApp() { + doReturn(new String[]{SOURCE_PACKAGE}).when(mPackageManagerInternal) + .getKnownPackageNames(eq(PackageManagerInternal.PACKAGE_VERIFIER), anyInt()); + mQuotaController.onSystemServicesReady(); + + final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); + mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, + createTimingSession(now - mQcConstants.EJ_WINDOW_SIZE_MS, MINUTE_IN_MILLIS, 5), + true); + mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, + createTimingSession(now - 40 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 5), true); + mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, + createTimingSession(now - 30 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 5), true); + mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, + createTimingSession(now - 20 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 5), true); + mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, + createTimingSession(now - 10 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 5), true); + + final long[] limits = mQuotaController.getEJLimitsMs(); + for (int i = 0; i < limits.length; ++i) { + setStandbyBucket(i); + assertEquals("Got wrong remaining EJ execution time for bucket #" + i, + i == NEVER_INDEX ? 0 + : (limits[i] + mQuotaController.getEjLimitSpecialAdditionMs() + - 5 * MINUTE_IN_MILLIS), + mQuotaController.getRemainingEJExecutionTimeLocked( + SOURCE_USER_ID, SOURCE_PACKAGE)); + } + } + + @Test public void testGetRemainingEJExecutionTimeLocked_OneSessionStraddlesEdge() { final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); final long[] limits = mQuotaController.getEJLimitsMs(); diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/TimeControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/TimeControllerTest.java index 9d847153661f..d64c1b31e343 100644 --- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/TimeControllerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/TimeControllerTest.java @@ -711,25 +711,31 @@ public class TimeControllerTest { .set(anyInt(), anyLong(), anyLong(), anyLong(), anyString(), any(), any(), any()); // Test evaluating something before the current deadline. + doReturn(false).when(mTimeController) + .wouldBeReadyWithConstraintLocked(eq(jobEarliest), anyInt()); + mTimeController.evaluateStateLocked(jobEarliest); + inOrder.verify(mAlarmManager, never()) + .set(anyInt(), anyLong(), anyLong(), anyLong(), eq(TAG_DELAY), any(), any(), any()); doReturn(true).when(mTimeController) .wouldBeReadyWithConstraintLocked(eq(jobEarliest), anyInt()); mTimeController.evaluateStateLocked(jobEarliest); inOrder.verify(mAlarmManager, times(1)) .set(anyInt(), eq(now + 5 * MINUTE_IN_MILLIS), anyLong(), anyLong(), eq(TAG_DELAY), any(), any(), any()); - // Job goes back to not being ready. Middle is still true, so use that alarm. + // Job goes back to not being ready. Middle is still true, but we don't check and actively + // defer alarm. doReturn(false).when(mTimeController) .wouldBeReadyWithConstraintLocked(eq(jobEarliest), anyInt()); mTimeController.evaluateStateLocked(jobEarliest); - inOrder.verify(mAlarmManager, times(1)) - .set(anyInt(), eq(now + 30 * MINUTE_IN_MILLIS), anyLong(), anyLong(), + inOrder.verify(mAlarmManager, never()) + .set(anyInt(), anyLong(), anyLong(), anyLong(), eq(TAG_DELAY), any(), any(), any()); - // Turn middle off. Latest is true, so use that alarm. + // Turn middle off. Latest is true, but we don't check and actively defer alarm. doReturn(false).when(mTimeController) .wouldBeReadyWithConstraintLocked(eq(jobMiddle), anyInt()); mTimeController.evaluateStateLocked(jobMiddle); - inOrder.verify(mAlarmManager, times(1)) - .set(anyInt(), eq(now + HOUR_IN_MILLIS), anyLong(), anyLong(), + inOrder.verify(mAlarmManager, never()) + .set(anyInt(), anyLong(), anyLong(), anyLong(), eq(TAG_DELAY), any(), any(), any()); } @@ -768,25 +774,32 @@ public class TimeControllerTest { .set(anyInt(), anyLong(), anyLong(), anyLong(), anyString(), any(), any(), any()); // Test evaluating something before the current deadline. + doReturn(false).when(mTimeController) + .wouldBeReadyWithConstraintLocked(eq(jobEarliest), anyInt()); + mTimeController.evaluateStateLocked(jobEarliest); + inOrder.verify(mAlarmManager, never()) + .set(anyInt(), anyLong(), anyLong(), anyLong(), + eq(TAG_DEADLINE), any(), any(), any()); doReturn(true).when(mTimeController) .wouldBeReadyWithConstraintLocked(eq(jobEarliest), anyInt()); mTimeController.evaluateStateLocked(jobEarliest); inOrder.verify(mAlarmManager, times(1)) .set(anyInt(), eq(now + 5 * MINUTE_IN_MILLIS), anyLong(), anyLong(), eq(TAG_DEADLINE), any(), any(), any()); - // Job goes back to not being ready. Middle is still true, so use that alarm. + // Job goes back to not being ready. Middle is still true, but we don't check and actively + // defer alarm. doReturn(false).when(mTimeController) .wouldBeReadyWithConstraintLocked(eq(jobEarliest), anyInt()); mTimeController.evaluateStateLocked(jobEarliest); - inOrder.verify(mAlarmManager, times(1)) - .set(anyInt(), eq(now + 30 * MINUTE_IN_MILLIS), anyLong(), anyLong(), + inOrder.verify(mAlarmManager, never()) + .set(anyInt(), anyLong(), anyLong(), anyLong(), eq(TAG_DEADLINE), any(), any(), any()); - // Turn middle off. Latest is true, so use that alarm. + // Turn middle off. Latest is true, but we don't check and actively defer alarm. doReturn(false).when(mTimeController) .wouldBeReadyWithConstraintLocked(eq(jobMiddle), anyInt()); mTimeController.evaluateStateLocked(jobMiddle); - inOrder.verify(mAlarmManager, times(1)) - .set(anyInt(), eq(now + HOUR_IN_MILLIS), anyLong(), anyLong(), + inOrder.verify(mAlarmManager, never()) + .set(anyInt(), anyLong(), anyLong(), anyLong(), eq(TAG_DEADLINE), any(), any(), any()); } } diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java index 24e7d7d19e8d..1fc075156349 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java @@ -218,7 +218,7 @@ public class BiometricSchedulerTest { @NonNull LazyDaemon<Object> lazyDaemon, int cookie) { super(context, lazyDaemon, token /* token */, null /* listener */, 0 /* userId */, TAG, cookie, TEST_SENSOR_ID, 0 /* statsModality */, - 0 /* statsAction */, 0 /* statsClient */); + 0 /* statsAction */, 0 /* statsClient */, true /* shouldLogMetrics */); } diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java index 1c7da3b98930..c1b1133dbb22 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java @@ -116,7 +116,9 @@ import org.hamcrest.BaseMatcher; import org.hamcrest.Description; import org.hamcrest.Matcher; import org.junit.After; +import org.junit.AfterClass; import org.junit.Before; +import org.junit.BeforeClass; import org.junit.Test; import org.mockito.Mockito; import org.mockito.internal.util.collections.Sets; @@ -206,6 +208,16 @@ public class DevicePolicyManagerTest extends DpmTestBase { private static final String PROFILE_OFF_SUSPENSION_TEXT = "suspension_text"; private static final String PROFILE_OFF_SUSPENSION_SOON_TEXT = "suspension_tomorrow_text"; + @BeforeClass + public static void setUpClass() { + Notification.DevFlags.sForceDefaults = true; + } + + @AfterClass + public static void tearDownClass() { + Notification.DevFlags.sForceDefaults = false; + } + @Before public void setUp() throws Exception { diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java index 1d2dcaecf978..6068fdf9b5b5 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java @@ -17,6 +17,7 @@ package com.android.server.devicepolicy; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; import android.annotation.Nullable; import android.app.AppOpsManager; @@ -33,6 +34,7 @@ import android.os.Handler; import android.os.UserHandle; import android.test.mock.MockContext; import android.util.ArrayMap; +import android.util.DisplayMetrics; import android.util.ExceptionUtils; import androidx.annotation.NonNull; @@ -174,6 +176,11 @@ public class DpmMockContext extends MockContext { binder = new MockBinder(); resources = mock(Resources.class); spiedContext = mock(Context.class); + + // Set up density for notification building + DisplayMetrics displayMetrics = mock(DisplayMetrics.class); + displayMetrics.density = 2.25f; + when(resources.getDisplayMetrics()).thenReturn(displayMetrics); } @Override diff --git a/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java b/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java index ec747acd376d..23a4c2f417c5 100644 --- a/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java @@ -53,21 +53,29 @@ public class AutomaticBrightnessControllerTest { private static final int DARKENING_LIGHT_DEBOUNCE_CONFIG = 0; private static final float DOZE_SCALE_FACTOR = 0.0f; private static final boolean RESET_AMBIENT_LUX_AFTER_WARMUP_CONFIG = false; + private static final int DISPLAY_ID = 0; + private static final int LAYER_STACK = 0; private Context mContext; + private LogicalDisplay mLogicalDisplay; + @Mock SensorManager mSensorManager; @Mock BrightnessMappingStrategy mBrightnessMappingStrategy; @Mock HysteresisLevels mAmbientBrightnessThresholds; @Mock HysteresisLevels mScreenBrightnessThresholds; - @Mock Handler mNoopHandler; + @Mock Handler mNoOpHandler; @Mock DisplayDeviceConfig mDisplayDeviceConfig; + @Mock DisplayDevice mDisplayDevice; private static final int LIGHT_SENSOR_WARMUP_TIME = 0; @Before public void setUp() { + // Share classloader to allow package private access. + System.setProperty("dexmaker.share_classloader", "true"); MockitoAnnotations.initMocks(this); mContext = InstrumentationRegistry.getContext(); + mLogicalDisplay = new LogicalDisplay(DISPLAY_ID, LAYER_STACK, mDisplayDevice); } private AutomaticBrightnessController setupController(Sensor lightSensor) { @@ -75,7 +83,7 @@ public class AutomaticBrightnessControllerTest { new AutomaticBrightnessController.Injector() { @Override public Handler getBackgroundThreadHandler() { - return mNoopHandler; + return mNoOpHandler; } }, () -> { }, mContext.getMainLooper(), mSensorManager, lightSensor, @@ -83,7 +91,7 @@ public class AutomaticBrightnessControllerTest { BRIGHTNESS_MAX_FLOAT, DOZE_SCALE_FACTOR, LIGHT_SENSOR_RATE, INITIAL_LIGHT_SENSOR_RATE, BRIGHTENING_LIGHT_DEBOUNCE_CONFIG, DARKENING_LIGHT_DEBOUNCE_CONFIG, RESET_AMBIENT_LUX_AFTER_WARMUP_CONFIG, - mAmbientBrightnessThresholds, mScreenBrightnessThresholds, mContext + mAmbientBrightnessThresholds, mScreenBrightnessThresholds, mLogicalDisplay, mContext ); controller.setLoggingEnabled(true); diff --git a/services/tests/servicestests/src/com/android/server/location/timezone/ControllerImplTest.java b/services/tests/servicestests/src/com/android/server/location/timezone/ControllerImplTest.java index 23365f70b2f4..5cff2081e59e 100644 --- a/services/tests/servicestests/src/com/android/server/location/timezone/ControllerImplTest.java +++ b/services/tests/servicestests/src/com/android/server/location/timezone/ControllerImplTest.java @@ -28,6 +28,7 @@ import static com.android.server.location.timezone.TimeZoneProviderEvent.createU import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; @@ -923,6 +924,74 @@ public class ControllerImplTest { assertFalse(controllerImpl.isUncertaintyTimeoutSet()); } + @Test + public void stateRecording() { + ControllerImpl controllerImpl = new ControllerImpl(mTestThreadingDomain, + mTestPrimaryLocationTimeZoneProvider, mTestSecondaryLocationTimeZoneProvider); + TestEnvironment testEnvironment = new TestEnvironment( + mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_ENABLED); + + // Initialize and check initial state. + controllerImpl.initialize(testEnvironment, mTestCallback); + + { + LocationTimeZoneManagerServiceState state = controllerImpl.getStateForTests(); + assertNull(state.getLastSuggestion()); + assertTrue(state.getPrimaryProviderStates().isEmpty()); + assertTrue(state.getSecondaryProviderStates().isEmpty()); + } + + // State recording and simulate some provider behavior that will show up in the state + // recording. + controllerImpl.setProviderStateRecordingEnabled(true); + + // Simulate an uncertain event from the primary. This will start the secondary. + mTestPrimaryLocationTimeZoneProvider.simulateTimeZoneProviderEvent( + USER1_UNCERTAIN_LOCATION_TIME_ZONE_EVENT); + + { + LocationTimeZoneManagerServiceState state = controllerImpl.getStateForTests(); + assertNull(state.getLastSuggestion()); + List<LocationTimeZoneProvider.ProviderState> primaryProviderStates = + state.getPrimaryProviderStates(); + assertEquals(1, primaryProviderStates.size()); + assertEquals(PROVIDER_STATE_STARTED_UNCERTAIN, + primaryProviderStates.get(0).stateEnum); + List<LocationTimeZoneProvider.ProviderState> secondaryProviderStates = + state.getSecondaryProviderStates(); + assertEquals(1, secondaryProviderStates.size()); + assertEquals(PROVIDER_STATE_STARTED_INITIALIZING, + secondaryProviderStates.get(0).stateEnum); + } + + // Simulate an uncertain event from the primary. This will start the secondary. + mTestSecondaryLocationTimeZoneProvider.simulateTimeZoneProviderEvent( + USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1); + + { + LocationTimeZoneManagerServiceState state = controllerImpl.getStateForTests(); + assertEquals(USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1.getSuggestion().getTimeZoneIds(), + state.getLastSuggestion().getZoneIds()); + List<LocationTimeZoneProvider.ProviderState> primaryProviderStates = + state.getPrimaryProviderStates(); + assertEquals(1, primaryProviderStates.size()); + assertEquals(PROVIDER_STATE_STARTED_UNCERTAIN, primaryProviderStates.get(0).stateEnum); + List<LocationTimeZoneProvider.ProviderState> secondaryProviderStates = + state.getSecondaryProviderStates(); + assertEquals(2, secondaryProviderStates.size()); + assertEquals(PROVIDER_STATE_STARTED_CERTAIN, secondaryProviderStates.get(1).stateEnum); + } + + controllerImpl.setProviderStateRecordingEnabled(false); + { + LocationTimeZoneManagerServiceState state = controllerImpl.getStateForTests(); + assertEquals(USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1.getSuggestion().getTimeZoneIds(), + state.getLastSuggestion().getZoneIds()); + assertTrue(state.getPrimaryProviderStates().isEmpty()); + assertTrue(state.getSecondaryProviderStates().isEmpty()); + } + } + private static void assertUncertaintyTimeoutSet( LocationTimeZoneProviderController.Environment environment, LocationTimeZoneProviderController controller) { diff --git a/services/tests/servicestests/src/com/android/server/location/timezone/LocationTimeZoneProviderTest.java b/services/tests/servicestests/src/com/android/server/location/timezone/LocationTimeZoneProviderTest.java index 49c67ea1b8f1..cb292db50115 100644 --- a/services/tests/servicestests/src/com/android/server/location/timezone/LocationTimeZoneProviderTest.java +++ b/services/tests/servicestests/src/com/android/server/location/timezone/LocationTimeZoneProviderTest.java @@ -18,6 +18,7 @@ package com.android.server.location.timezone; import static android.service.timezone.TimeZoneProviderService.TEST_COMMAND_RESULT_ERROR_KEY; import static android.service.timezone.TimeZoneProviderService.TEST_COMMAND_RESULT_SUCCESS_KEY; +import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_DESTROYED; import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_CERTAIN; import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_INITIALIZING; import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_UNCERTAIN; @@ -49,6 +50,7 @@ import org.junit.Test; import java.time.Duration; import java.util.Arrays; +import java.util.List; import java.util.concurrent.atomic.AtomicReference; /** @@ -174,6 +176,48 @@ public class LocationTimeZoneProviderTest { assertNotNull(result.getString(TEST_COMMAND_RESULT_ERROR_KEY)); } + @Test + public void stateRecording() { + String providerName = "primary"; + TestLocationTimeZoneProvider provider = + new TestLocationTimeZoneProvider(mTestThreadingDomain, providerName); + provider.setStateChangeRecordingEnabled(true); + + // initialize() + provider.initialize(mProviderListener); + provider.assertLatestRecordedState(PROVIDER_STATE_STOPPED); + + // startUpdates() + ConfigurationInternal config = USER1_CONFIG_GEO_DETECTION_ENABLED; + Duration arbitraryInitializationTimeout = Duration.ofMinutes(5); + Duration arbitraryInitializationTimeoutFuzz = Duration.ofMinutes(2); + provider.startUpdates(config, arbitraryInitializationTimeout, + arbitraryInitializationTimeoutFuzz); + provider.assertLatestRecordedState(PROVIDER_STATE_STARTED_INITIALIZING); + + // Simulate a suggestion event being received. + TimeZoneProviderSuggestion suggestion = new TimeZoneProviderSuggestion.Builder() + .setElapsedRealtimeMillis(ARBITRARY_ELAPSED_REALTIME_MILLIS) + .setTimeZoneIds(Arrays.asList("Europe/London")) + .build(); + TimeZoneProviderEvent event = TimeZoneProviderEvent.createSuggestionEvent(suggestion); + provider.simulateProviderEventReceived(event); + provider.assertLatestRecordedState(PROVIDER_STATE_STARTED_CERTAIN); + + // Simulate an uncertain event being received. + event = TimeZoneProviderEvent.createUncertainEvent(); + provider.simulateProviderEventReceived(event); + provider.assertLatestRecordedState(PROVIDER_STATE_STARTED_UNCERTAIN); + + // stopUpdates() + provider.stopUpdates(); + provider.assertLatestRecordedState(PROVIDER_STATE_STOPPED); + + // destroy() + provider.destroy(); + provider.assertLatestRecordedState(PROVIDER_STATE_DESTROYED); + } + /** A test stand-in for the real {@link LocationTimeZoneProviderController}'s listener. */ private static class TestProviderListener implements ProviderListener { @@ -257,5 +301,11 @@ public class LocationTimeZoneProviderTest { void assertOnDestroyCalled() { assertTrue(mOnDestroyCalled); } + + void assertLatestRecordedState(@ProviderState.ProviderStateEnum int expectedStateEnum) { + List<ProviderState> recordedStates = getRecordedStates(); + assertEquals(expectedStateEnum, + recordedStates.get(recordedStates.size() - 1).stateEnum); + } } } diff --git a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java index 205548cc8d3c..9a52643b57f2 100644 --- a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java @@ -209,7 +209,10 @@ public class AppsFilterTest { final AppsFilter appsFilter = new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, mMockExecutor); + final WatchableTester watcher = new WatchableTester(appsFilter, "onChange"); + watcher.register(); appsFilter.onSystemReady(); + watcher.verifyChangeReported("systemReady"); verify(mFeatureConfigMock).onSystemReady(); } @@ -218,45 +221,60 @@ public class AppsFilterTest { final AppsFilter appsFilter = new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, mMockExecutor); + final WatchableTester watcher = new WatchableTester(appsFilter, "onChange"); + watcher.register(); simulateAddBasicAndroid(appsFilter); + watcher.verifyChangeReported("addBasicAndroid"); appsFilter.onSystemReady(); + watcher.verifyChangeReported("systemReady"); PackageSetting target = simulateAddPackage(appsFilter, pkg("com.some.package", new IntentFilter("TEST_ACTION")), DUMMY_TARGET_APPID); + watcher.verifyChangeReported("add package"); PackageSetting calling = simulateAddPackage(appsFilter, pkg("com.some.other.package", new Intent("TEST_ACTION")), DUMMY_CALLING_APPID); + watcher.verifyChangeReported("add package"); assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, calling, target, SYSTEM_USER)); + watcher.verifyNoChangeReported("shouldFilterAplication"); } - @Test public void testQueriesProtectedAction_FilterDoesNotMatch() throws Exception { final AppsFilter appsFilter = new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, mMockExecutor); + final WatchableTester watcher = new WatchableTester(appsFilter, "onChange"); + watcher.register(); final Signature frameworkSignature = Mockito.mock(Signature.class); final PackageParser.SigningDetails frameworkSigningDetails = new PackageParser.SigningDetails(new Signature[]{frameworkSignature}, 1); final ParsingPackage android = pkg("android"); + watcher.verifyNoChangeReported("prepare"); android.addProtectedBroadcast("TEST_ACTION"); simulateAddPackage(appsFilter, android, 1000, b -> b.setSigningDetails(frameworkSigningDetails)); + watcher.verifyChangeReported("addPackage"); appsFilter.onSystemReady(); + watcher.verifyChangeReported("systemReady"); final int activityUid = DUMMY_TARGET_APPID; PackageSetting targetActivity = simulateAddPackage(appsFilter, pkg("com.target.activity", new IntentFilter("TEST_ACTION")), activityUid); + watcher.verifyChangeReported("addPackage"); final int receiverUid = DUMMY_TARGET_APPID + 1; PackageSetting targetReceiver = simulateAddPackage(appsFilter, pkgWithReceiver("com.target.receiver", new IntentFilter("TEST_ACTION")), receiverUid); + watcher.verifyChangeReported("addPackage"); final int callingUid = DUMMY_CALLING_APPID; PackageSetting calling = simulateAddPackage(appsFilter, pkg("com.calling.action", new Intent("TEST_ACTION")), callingUid); + watcher.verifyChangeReported("addPackage"); final int wildcardUid = DUMMY_CALLING_APPID + 1; PackageSetting callingWildCard = simulateAddPackage(appsFilter, pkg("com.calling.wildcard", new Intent("*")), wildcardUid); + watcher.verifyChangeReported("addPackage"); assertFalse(appsFilter.shouldFilterApplication(callingUid, calling, targetActivity, SYSTEM_USER)); @@ -267,6 +285,7 @@ public class AppsFilterTest { wildcardUid, callingWildCard, targetActivity, SYSTEM_USER)); assertTrue(appsFilter.shouldFilterApplication( wildcardUid, callingWildCard, targetReceiver, SYSTEM_USER)); + watcher.verifyNoChangeReported("shouldFilterApplication"); } @Test @@ -274,17 +293,24 @@ public class AppsFilterTest { final AppsFilter appsFilter = new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, mMockExecutor); + final WatchableTester watcher = new WatchableTester(appsFilter, "onChange"); + watcher.register(); simulateAddBasicAndroid(appsFilter); + watcher.verifyChangeReported("addPackage"); appsFilter.onSystemReady(); + watcher.verifyChangeReported("systemReady"); PackageSetting target = simulateAddPackage(appsFilter, pkgWithProvider("com.some.package", "com.some.authority"), DUMMY_TARGET_APPID); + watcher.verifyChangeReported("addPackage"); PackageSetting calling = simulateAddPackage(appsFilter, pkgQueriesProvider("com.some.other.package", "com.some.authority"), DUMMY_CALLING_APPID); + watcher.verifyChangeReported("addPackage"); assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, calling, target, SYSTEM_USER)); + watcher.verifyNoChangeReported("shouldFilterApplication"); } @Test @@ -292,17 +318,24 @@ public class AppsFilterTest { final AppsFilter appsFilter = new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, mMockExecutor); + final WatchableTester watcher = new WatchableTester(appsFilter, "onChange"); + watcher.register(); simulateAddBasicAndroid(appsFilter); + watcher.verifyChangeReported("addPackage"); appsFilter.onSystemReady(); + watcher.verifyChangeReported("systemReady"); PackageSetting target = simulateAddPackage(appsFilter, pkgWithProvider("com.some.package", "com.some.authority"), DUMMY_TARGET_APPID); + watcher.verifyChangeReported("addPackage"); PackageSetting calling = simulateAddPackage(appsFilter, pkgQueriesProvider("com.some.other.package", "com.some.other.authority"), DUMMY_CALLING_APPID); + watcher.verifyChangeReported("addPackage"); assertTrue(appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, calling, target, SYSTEM_USER)); + watcher.verifyNoChangeReported("shouldFilterApplication"); } @Test @@ -779,16 +812,23 @@ public class AppsFilterTest { final AppsFilter appsFilter = new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, mMockExecutor); + final WatchableTester watcher = new WatchableTester(appsFilter, "onChange"); + watcher.register(); simulateAddBasicAndroid(appsFilter); + watcher.verifyChangeReported("addBasicAndroid"); appsFilter.onSystemReady(); + watcher.verifyChangeReported("systemReady"); PackageSetting target = simulateAddPackage(appsFilter, pkg("com.some.package"), DUMMY_TARGET_APPID); + watcher.verifyChangeReported("add package"); PackageSetting calling = simulateAddPackage(appsFilter, pkg("com.some.other.package"), DUMMY_CALLING_APPID, withInstallSource(null, target.name, null, null, false)); + watcher.verifyChangeReported("add package"); assertTrue(appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, calling, target, SYSTEM_USER)); + watcher.verifyNoChangeReported("shouldFilterAplication"); } @Test @@ -796,16 +836,23 @@ public class AppsFilterTest { final AppsFilter appsFilter = new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, mMockExecutor); + final WatchableTester watcher = new WatchableTester(appsFilter, "onChange"); + watcher.register(); simulateAddBasicAndroid(appsFilter); + watcher.verifyChangeReported("addBasicAndroid"); appsFilter.onSystemReady(); + watcher.verifyChangeReported("systemReady"); PackageSetting target = simulateAddPackage(appsFilter, pkg("com.some.package"), DUMMY_TARGET_APPID); + watcher.verifyChangeReported("add package"); PackageSetting calling = simulateAddPackage(appsFilter, pkg("com.some.other.package"), DUMMY_CALLING_APPID, withInstallSource(null, null, target.name, null, false)); + watcher.verifyChangeReported("add package"); assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, calling, target, SYSTEM_USER)); + watcher.verifyNoChangeReported("shouldFilterAplication"); } @Test @@ -813,15 +860,20 @@ public class AppsFilterTest { final AppsFilter appsFilter = new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, mMockExecutor); + final WatchableTester watcher = new WatchableTester(appsFilter, "onChange"); + watcher.register(); simulateAddBasicAndroid(appsFilter); + watcher.verifyChangeReported("addBasicAndroid"); appsFilter.onSystemReady(); - + watcher.verifyChangeReported("systemReady"); PackageSetting target = simulateAddPackage(appsFilter, pkg("com.some.package"), DUMMY_TARGET_APPID); + watcher.verifyChangeReported("add package"); PackageSetting instrumentation = simulateAddPackage(appsFilter, pkgWithInstrumentation("com.some.other.package", "com.some.package"), DUMMY_CALLING_APPID); + watcher.verifyChangeReported("add package"); assertFalse( appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, instrumentation, target, @@ -829,6 +881,7 @@ public class AppsFilterTest { assertFalse( appsFilter.shouldFilterApplication(DUMMY_TARGET_APPID, target, instrumentation, SYSTEM_USER)); + watcher.verifyNoChangeReported("shouldFilterAplication"); } @Test @@ -836,8 +889,12 @@ public class AppsFilterTest { final AppsFilter appsFilter = new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, mMockExecutor); + final WatchableTester watcher = new WatchableTester(appsFilter, "onChange"); + watcher.register(); simulateAddBasicAndroid(appsFilter); + watcher.verifyChangeReported("addBasicAndroid"); appsFilter.onSystemReady(); + watcher.verifyChangeReported("systemReady"); final int systemAppId = Process.FIRST_APPLICATION_UID - 1; final int seesNothingAppId = Process.FIRST_APPLICATION_UID; @@ -845,25 +902,34 @@ public class AppsFilterTest { final int queriesProviderAppId = Process.FIRST_APPLICATION_UID + 2; PackageSetting system = simulateAddPackage(appsFilter, pkg("some.system.pkg"), systemAppId); + watcher.verifyChangeReported("add package"); PackageSetting seesNothing = simulateAddPackage(appsFilter, pkg("com.some.package"), seesNothingAppId); + watcher.verifyChangeReported("add package"); PackageSetting hasProvider = simulateAddPackage(appsFilter, pkgWithProvider("com.some.other.package", "com.some.authority"), hasProviderAppId); + watcher.verifyChangeReported("add package"); PackageSetting queriesProvider = simulateAddPackage(appsFilter, pkgQueriesProvider("com.yet.some.other.package", "com.some.authority"), queriesProviderAppId); + watcher.verifyChangeReported("add package"); final SparseArray<int[]> systemFilter = appsFilter.getVisibilityAllowList(system, USER_ARRAY, mExisting); + watcher.verifyNoChangeReported("getVisibility"); assertThat(toList(systemFilter.get(SYSTEM_USER)), contains(seesNothingAppId, hasProviderAppId, queriesProviderAppId)); + watcher.verifyNoChangeReported("getVisibility"); final SparseArray<int[]> seesNothingFilter = appsFilter.getVisibilityAllowList(seesNothing, USER_ARRAY, mExisting); + watcher.verifyNoChangeReported("getVisibility"); assertThat(toList(seesNothingFilter.get(SYSTEM_USER)), contains(seesNothingAppId)); + watcher.verifyNoChangeReported("getVisibility"); assertThat(toList(seesNothingFilter.get(SECONDARY_USER)), contains(seesNothingAppId)); + watcher.verifyNoChangeReported("getVisibility"); final SparseArray<int[]> hasProviderFilter = appsFilter.getVisibilityAllowList(hasProvider, USER_ARRAY, mExisting); @@ -872,17 +938,22 @@ public class AppsFilterTest { SparseArray<int[]> queriesProviderFilter = appsFilter.getVisibilityAllowList(queriesProvider, USER_ARRAY, mExisting); + watcher.verifyNoChangeReported("getVisibility"); assertThat(toList(queriesProviderFilter.get(SYSTEM_USER)), contains(queriesProviderAppId)); + watcher.verifyNoChangeReported("getVisibility"); // provider read appsFilter.grantImplicitAccess(hasProviderAppId, queriesProviderAppId); + watcher.verifyChangeReported("grantImplicitAccess"); // ensure implicit access is included in the filter queriesProviderFilter = appsFilter.getVisibilityAllowList(queriesProvider, USER_ARRAY, mExisting); + watcher.verifyNoChangeReported("getVisibility"); assertThat(toList(queriesProviderFilter.get(SYSTEM_USER)), contains(hasProviderAppId, queriesProviderAppId)); + watcher.verifyNoChangeReported("getVisibility"); } @Test diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java index 282047afaa51..333ec9295b93 100644 --- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java +++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java @@ -1215,7 +1215,7 @@ public class PackageManagerSettingsTests { private void verifyKeySetMetaData(Settings settings) throws ReflectiveOperationException, IllegalAccessException { ArrayMap<String, PackageSetting> packages = - settings.mPackages.untrackedMap(); + settings.mPackages.untrackedStorage(); KeySetManagerService ksms = settings.mKeySetManagerService; /* verify keyset and public key ref counts */ diff --git a/services/tests/servicestests/src/com/android/server/utils/WatcherTest.java b/services/tests/servicestests/src/com/android/server/utils/WatcherTest.java index 9bea9d4cedbd..7c65dc03a57e 100644 --- a/services/tests/servicestests/src/com/android/server/utils/WatcherTest.java +++ b/services/tests/servicestests/src/com/android/server/utils/WatcherTest.java @@ -20,12 +20,20 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import android.util.ArrayMap; +import android.util.ArraySet; +import android.util.SparseArray; +import android.util.SparseBooleanArray; +import android.util.SparseIntArray; + import androidx.test.filters.SmallTest; import org.junit.After; import org.junit.Before; import org.junit.Test; +import java.util.ArrayList; + /** * Test class for {@link Watcher}, {@link Watchable}, {@link WatchableImpl}, * {@link WatchedArrayMap}, {@link WatchedSparseArray}, and @@ -40,7 +48,7 @@ public class WatcherTest { // A counter to generate unique IDs for Leaf elements. private int mLeafId = 0; - // Useful indices used int the tests. + // Useful indices used in the tests. private static final int INDEX_A = 1; private static final int INDEX_B = 2; private static final int INDEX_C = 3; @@ -171,6 +179,7 @@ public class WatcherTest { @Test public void testWatchedArrayMap() { + final String name = "WatchedArrayMap"; WatchableTester tester; // Create a few leaves @@ -183,7 +192,7 @@ public class WatcherTest { WatchedArrayMap<Integer, Leaf> array = new WatchedArrayMap<>(); array.put(INDEX_A, leafA); array.put(INDEX_B, leafB); - tester = new WatchableTester(array, "WatchedArrayMap"); + tester = new WatchableTester(array, name); tester.verify(0, "Initial array - no registration"); leafA.tick(); tester.verify(0, "Updates with no registration"); @@ -231,20 +240,20 @@ public class WatcherTest { final WatchedArrayMap<Integer, Leaf> arraySnap = array.snapshot(); tester.verify(14, "Generate snapshot (no changes)"); // Verify that the snapshot is a proper copy of the source. - assertEquals("WatchedArrayMap snap same size", + assertEquals(name + " snap same size", array.size(), arraySnap.size()); for (int i = 0; i < array.size(); i++) { for (int j = 0; j < arraySnap.size(); j++) { - assertTrue("WatchedArrayMap elements differ", + assertTrue(name + " elements differ", array.valueAt(i) != arraySnap.valueAt(j)); } - assertTrue("WatchedArrayMap element copy", + assertTrue(name + " element copy", array.valueAt(i).equals(arraySnap.valueAt(i))); } leafD.tick(); tester.verify(15, "Tick after snapshot"); // Verify that the snapshot is sealed - verifySealed("WatchedArrayMap", ()->arraySnap.put(INDEX_A, leafA)); + verifySealed(name, ()->arraySnap.put(INDEX_A, leafA)); } // Recreate the snapshot since the test corrupted it. { @@ -253,10 +262,235 @@ public class WatcherTest { final Leaf arraySnapElement = arraySnap.valueAt(0); verifySealed("ArraySnapshotElement", ()->arraySnapElement.tick()); } + // Verify copy-in/out + { + final String msg = name + " copy-in/out failed"; + ArrayMap<Integer, Leaf> base = new ArrayMap<>(); + array.copyTo(base); + WatchedArrayMap<Integer, Leaf> copy = new WatchedArrayMap<>(); + copy.copyFrom(base); + if (!array.equals(copy)) { + fail(msg); + } + } + } + + @Test + public void testWatchedArraySet() { + final String name = "WatchedArraySet"; + WatchableTester tester; + + // Create a few leaves + Leaf leafA = new Leaf(); + Leaf leafB = new Leaf(); + Leaf leafC = new Leaf(); + Leaf leafD = new Leaf(); + + // Test WatchedArraySet + WatchedArraySet<Leaf> array = new WatchedArraySet<>(); + array.add(leafA); + array.add(leafB); + tester = new WatchableTester(array, name); + tester.verify(0, "Initial array - no registration"); + leafA.tick(); + tester.verify(0, "Updates with no registration"); + tester.register(); + tester.verify(0, "Updates with no registration"); + leafA.tick(); + tester.verify(1, "Updates with registration"); + leafB.tick(); + tester.verify(2, "Updates with registration"); + array.remove(leafB); + tester.verify(3, "Removed b"); + leafB.tick(); + tester.verify(3, "Updates with b not watched"); + array.add(leafB); + array.add(leafB); + tester.verify(5, "Added b once"); + leafB.tick(); + tester.verify(6, "Changed b - single notification"); + array.remove(leafB); + tester.verify(7, "Removed b"); + leafB.tick(); + tester.verify(7, "Changed b - not watched"); + array.remove(leafB); + tester.verify(7, "Removed non-existent b"); + array.clear(); + tester.verify(8, "Cleared array"); + leafA.tick(); + tester.verify(8, "Change to a not in array"); + + // Special methods + array.add(leafA); + array.add(leafB); + array.add(leafC); + tester.verify(11, "Added a, b, c"); + leafC.tick(); + tester.verify(12, "Ticked c"); + array.removeAt(array.indexOf(leafC)); + tester.verify(13, "Removed c"); + leafC.tick(); + tester.verify(13, "Ticked c, not registered"); + array.append(leafC); + tester.verify(14, "Append c"); + leafC.tick(); + leafD.tick(); + tester.verify(15, "Ticked d and c"); + assertEquals("Verify three elements", 3, array.size()); + + // Snapshot + { + final WatchedArraySet<Leaf> arraySnap = array.snapshot(); + tester.verify(15, "Generate snapshot (no changes)"); + // Verify that the snapshot is a proper copy of the source. + assertEquals(name + " snap same size", + array.size(), arraySnap.size()); + for (int i = 0; i < array.size(); i++) { + for (int j = 0; j < arraySnap.size(); j++) { + assertTrue(name + " elements differ", + array.valueAt(i) != arraySnap.valueAt(j)); + } + } + leafC.tick(); + tester.verify(16, "Tick after snapshot"); + // Verify that the array snapshot is sealed + verifySealed(name, ()->arraySnap.add(leafB)); + } + // Recreate the snapshot since the test corrupted it. + { + final WatchedArraySet<Leaf> arraySnap = array.snapshot(); + // Verify that elements are also snapshots + final Leaf arraySnapElement = arraySnap.valueAt(0); + verifySealed(name + " snap element", ()->arraySnapElement.tick()); + } + // Verify copy-in/out + { + final String msg = name + " copy-in/out"; + ArraySet<Leaf> base = new ArraySet<>(); + array.copyTo(base); + WatchedArraySet<Leaf> copy = new WatchedArraySet<>(); + copy.copyFrom(base); + if (!array.equals(copy)) { + fail(msg); + } + } + } + + @Test + public void testWatchedArrayList() { + final String name = "WatchedArrayList"; + WatchableTester tester; + + // Create a few leaves + Leaf leafA = new Leaf(); + Leaf leafB = new Leaf(); + Leaf leafC = new Leaf(); + Leaf leafD = new Leaf(); + + // Redefine the indices used in the tests to be zero-based + final int indexA = 0; + final int indexB = 1; + final int indexC = 2; + final int indexD = 3; + + // Test WatchedArrayList + WatchedArrayList<Leaf> array = new WatchedArrayList<>(); + // A spacer that takes up index 0 (and is not Watchable). + array.add(indexA, leafA); + array.add(indexB, leafB); + tester = new WatchableTester(array, name); + tester.verify(0, "Initial array - no registration"); + leafA.tick(); + tester.verify(0, "Updates with no registration"); + tester.register(); + tester.verify(0, "Updates with no registration"); + leafA.tick(); + tester.verify(1, "Updates with registration"); + leafB.tick(); + tester.verify(2, "Updates with registration"); + array.remove(indexB); + tester.verify(3, "Removed b"); + leafB.tick(); + tester.verify(3, "Updates with b not watched"); + array.add(indexB, leafB); + array.add(indexC, leafB); + tester.verify(5, "Added b twice"); + leafB.tick(); + tester.verify(6, "Changed b - single notification"); + array.remove(indexC); + tester.verify(7, "Removed first b"); + leafB.tick(); + tester.verify(8, "Changed b - single notification"); + array.remove(indexB); + tester.verify(9, "Removed second b"); + leafB.tick(); + tester.verify(9, "Updated leafB - no change"); + array.clear(); + tester.verify(10, "Cleared array"); + leafB.tick(); + tester.verify(10, "Change to b not in array"); + + // Special methods + array.add(indexA, leafA); + array.add(indexB, leafB); + array.add(indexC, leafC); + tester.verify(13, "Added c"); + leafC.tick(); + tester.verify(14, "Ticked c"); + array.set(array.indexOf(leafC), leafD); + tester.verify(15, "Replaced c with d"); + leafC.tick(); + leafD.tick(); + tester.verify(16, "Ticked d and c (c not registered)"); + array.add(leafC); + tester.verify(17, "Append c"); + leafC.tick(); + leafD.tick(); + tester.verify(19, "Ticked d and c"); + + // Snapshot + { + final WatchedArrayList<Leaf> arraySnap = array.snapshot(); + tester.verify(19, "Generate snapshot (no changes)"); + // Verify that the snapshot is a proper copy of the source. + assertEquals(name + " snap same size", + array.size(), arraySnap.size()); + for (int i = 0; i < array.size(); i++) { + for (int j = 0; j < arraySnap.size(); j++) { + assertTrue(name + " elements differ", + array.get(i) != arraySnap.get(j)); + } + assertTrue(name + " element copy", + array.get(i).equals(arraySnap.get(i))); + } + leafD.tick(); + tester.verify(20, "Tick after snapshot"); + // Verify that the array snapshot is sealed + verifySealed(name, ()->arraySnap.add(indexA, leafB)); + } + // Recreate the snapshot since the test corrupted it. + { + final WatchedArrayList<Leaf> arraySnap = array.snapshot(); + // Verify that elements are also snapshots + final Leaf arraySnapElement = arraySnap.get(0); + verifySealed("ArraySnapshotElement", ()->arraySnapElement.tick()); + } + // Verify copy-in/out + { + final String msg = name + " copy-in/out"; + ArrayList<Leaf> base = new ArrayList<>(); + array.copyTo(base); + WatchedArrayList<Leaf> copy = new WatchedArrayList<>(); + copy.copyFrom(base); + if (!array.equals(copy)) { + fail(msg); + } + } } @Test public void testWatchedSparseArray() { + final String name = "WatchedSparseArray"; WatchableTester tester; // Create a few leaves @@ -269,7 +503,7 @@ public class WatcherTest { WatchedSparseArray<Leaf> array = new WatchedSparseArray<>(); array.put(INDEX_A, leafA); array.put(INDEX_B, leafB); - tester = new WatchableTester(array, "WatchedSparseArray"); + tester = new WatchableTester(array, name); tester.verify(0, "Initial array - no registration"); leafA.tick(); tester.verify(0, "Updates with no registration"); @@ -338,20 +572,20 @@ public class WatcherTest { final WatchedSparseArray<Leaf> arraySnap = array.snapshot(); tester.verify(22, "Generate snapshot (no changes)"); // Verify that the snapshot is a proper copy of the source. - assertEquals("WatchedSparseArray snap same size", + assertEquals(name + " snap same size", array.size(), arraySnap.size()); for (int i = 0; i < array.size(); i++) { for (int j = 0; j < arraySnap.size(); j++) { - assertTrue("WatchedSparseArray elements differ", + assertTrue(name + " elements differ", array.valueAt(i) != arraySnap.valueAt(j)); } - assertTrue("WatchedArrayMap element copy", + assertTrue(name + " element copy", array.valueAt(i).equals(arraySnap.valueAt(i))); } leafD.tick(); tester.verify(23, "Tick after snapshot"); // Verify that the array snapshot is sealed - verifySealed("WatchedSparseArray", ()->arraySnap.put(INDEX_A, leafB)); + verifySealed(name, ()->arraySnap.put(INDEX_A, leafB)); } // Recreate the snapshot since the test corrupted it. { @@ -360,15 +594,30 @@ public class WatcherTest { final Leaf arraySnapElement = arraySnap.valueAt(0); verifySealed("ArraySnapshotElement", ()->arraySnapElement.tick()); } + // Verify copy-in/out + { + final String msg = name + " copy-in/out"; + SparseArray<Leaf> base = new SparseArray<>(); + array.copyTo(base); + WatchedSparseArray<Leaf> copy = new WatchedSparseArray<>(); + copy.copyFrom(base); + final int end = array.size(); + assertTrue(msg + " size mismatch " + end + " " + copy.size(), end == copy.size()); + for (int i = 0; i < end; i++) { + final int key = array.keyAt(i); + assertTrue(msg, array.get(i) == copy.get(i)); + } + } } @Test public void testWatchedSparseBooleanArray() { + final String name = "WatchedSparseBooleanArray"; WatchableTester tester; // Test WatchedSparseBooleanArray WatchedSparseBooleanArray array = new WatchedSparseBooleanArray(); - tester = new WatchableTester(array, "WatchedSparseBooleanArray"); + tester = new WatchableTester(array, name); tester.verify(0, "Initial array - no registration"); array.put(INDEX_A, true); tester.verify(0, "Updates with no registration"); @@ -376,14 +625,10 @@ public class WatcherTest { tester.verify(0, "Updates with no registration"); array.put(INDEX_B, true); tester.verify(1, "Updates with registration"); - array.put(INDEX_B, true); - tester.verify(1, "Null update"); array.put(INDEX_B, false); array.put(INDEX_C, true); tester.verify(3, "Updates with registration"); // Special methods - array.put(INDEX_C, true); - tester.verify(3, "Added true, no change"); array.setValueAt(array.indexOfKey(INDEX_C), false); tester.verify(4, "Replaced true with false"); array.append(INDEX_D, true); @@ -403,7 +648,77 @@ public class WatcherTest { array.put(INDEX_D, false); tester.verify(6, "Tick after snapshot"); // Verify that the array is sealed - verifySealed("WatchedSparseBooleanArray", ()->arraySnap.put(INDEX_D, false)); + verifySealed(name, ()->arraySnap.put(INDEX_D, false)); + } + // Verify copy-in/out + { + final String msg = name + " copy-in/out"; + SparseBooleanArray base = new SparseBooleanArray(); + array.copyTo(base); + WatchedSparseBooleanArray copy = new WatchedSparseBooleanArray(); + copy.copyFrom(base); + final int end = array.size(); + assertTrue(msg + " size mismatch/2 " + end + " " + copy.size(), end == copy.size()); + for (int i = 0; i < end; i++) { + final int key = array.keyAt(i); + assertTrue(msg + " element", array.get(i) == copy.get(i)); + } + } + } + + @Test + public void testWatchedSparseIntArray() { + final String name = "WatchedSparseIntArray"; + WatchableTester tester; + + // Test WatchedSparseIntArray + WatchedSparseIntArray array = new WatchedSparseIntArray(); + tester = new WatchableTester(array, name); + tester.verify(0, "Initial array - no registration"); + array.put(INDEX_A, 1); + tester.verify(0, "Updates with no registration"); + tester.register(); + tester.verify(0, "Updates with no registration"); + array.put(INDEX_B, 2); + tester.verify(1, "Updates with registration"); + array.put(INDEX_B, 4); + array.put(INDEX_C, 5); + tester.verify(3, "Updates with registration"); + // Special methods + array.setValueAt(array.indexOfKey(INDEX_C), 7); + tester.verify(4, "Replaced 6 with 7"); + array.append(INDEX_D, 8); + tester.verify(5, "Append 8"); + + // Snapshot + { + WatchedSparseIntArray arraySnap = array.snapshot(); + tester.verify(5, "Generate snapshot"); + // Verify that the snapshot is a proper copy of the source. + assertEquals("WatchedSparseIntArray snap same size", + array.size(), arraySnap.size()); + for (int i = 0; i < array.size(); i++) { + assertEquals(name + " element copy", + array.valueAt(i), arraySnap.valueAt(i)); + } + array.put(INDEX_D, 9); + tester.verify(6, "Tick after snapshot"); + // Verify that the array is sealed + verifySealed(name, ()->arraySnap.put(INDEX_D, 10)); + } + // Verify copy-in/out + { + final String msg = name + " copy-in/out"; + SparseIntArray base = new SparseIntArray(); + array.copyTo(base); + WatchedSparseIntArray copy = new WatchedSparseIntArray(); + copy.copyFrom(base); + final int end = array.size(); + assertTrue(msg + " size mismatch " + end + " " + copy.size(), end == copy.size()); + for (int i = 0; i < end; i++) { + final int key = array.keyAt(i); + assertTrue(msg, array.get(i) == copy.get(i)); + } } } } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java index a093e0d4a740..afcf08ef1562 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java @@ -80,7 +80,7 @@ public class NotificationListenersTest extends UiServiceTestCase { @Test public void testReadExtraTag() throws Exception { - String xml = "<requested_listeners>" + String xml = "<req_listeners>" + "<listener component=\"" + mCn1.flattenToString() + "\" user=\"0\">" + "<allowed types=\"7\" />" + "<disallowed pkgs=\"\" />" @@ -89,13 +89,13 @@ public class NotificationListenersTest extends UiServiceTestCase { + "<allowed types=\"4\" />" + "<disallowed pkgs=\"something\" />" + "</listener>" - + "</requested_listeners>"; + + "</req_listeners>"; TypedXmlPullParser parser = Xml.newFastPullParser(); parser.setInput(new BufferedInputStream( new ByteArrayInputStream(xml.getBytes())), null); parser.nextTag(); - mListeners.readExtraTag("requested_listeners", parser); + mListeners.readExtraTag("req_listeners", parser); validateListenersFromXml(); } @@ -120,7 +120,7 @@ public class NotificationListenersTest extends UiServiceTestCase { parser.setInput(new BufferedInputStream( new ByteArrayInputStream(baos.toByteArray())), null); parser.nextTag(); - mListeners.readExtraTag("requested_listeners", parser); + mListeners.readExtraTag("req_listeners", parser); validateListenersFromXml(); } @@ -192,7 +192,7 @@ public class NotificationListenersTest extends UiServiceTestCase { assertThat(mListeners.getNotificationListenerFilter( Pair.create(si.getComponentName(), 0)).getTypes()) - .isEqualTo(7); + .isEqualTo(15); assertThat(mListeners.getNotificationListenerFilter(Pair.create(si.getComponentName(), 0)) .getDisallowedPackages()) .isEmpty(); diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java index 6c824d6c87dc..ce5fc4021eac 100644 --- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java @@ -83,7 +83,7 @@ public class AppTransitionControllerTest extends WindowTestsBase { assertEquals(WindowManager.TRANSIT_OLD_TRANSLUCENT_ACTIVITY_OPEN, AppTransitionController.getTransitCompatType(mDisplayContent.mAppTransition, mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps, - null, null)); + null, null, false)); } @Test @@ -99,7 +99,7 @@ public class AppTransitionControllerTest extends WindowTestsBase { assertEquals(WindowManager.TRANSIT_OLD_TRANSLUCENT_ACTIVITY_CLOSE, AppTransitionController.getTransitCompatType(mDisplayContent.mAppTransition, mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps, - null, null)); + null, null, false)); } @Test @@ -117,7 +117,7 @@ public class AppTransitionControllerTest extends WindowTestsBase { assertEquals(TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE, AppTransitionController.getTransitCompatType(mDisplayContent.mAppTransition, mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps, - null, null)); + null, null, false)); } @Test diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java index 8cc515e83342..f1e36098d84e 100644 --- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java @@ -23,6 +23,7 @@ import static android.view.WindowManager.TRANSIT_FLAG_APP_CRASHED; import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY; import static android.view.WindowManager.TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE; import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY; +import static android.view.WindowManager.TRANSIT_OLD_UNSET; import static android.view.WindowManager.TRANSIT_OPEN; import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; @@ -81,7 +82,8 @@ public class AppTransitionTests extends WindowTestsBase { assertEquals(TRANSIT_OLD_KEYGUARD_GOING_AWAY, AppTransitionController.getTransitCompatType(mDc.mAppTransition, mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps, - null /* wallpaperTarget */, null /* oldWallpaper */)); + null /* wallpaperTarget */, null /* oldWallpaper */, + false /*skipAppTransitionAnimation*/)); } @Test @@ -95,7 +97,8 @@ public class AppTransitionTests extends WindowTestsBase { assertEquals(TRANSIT_OLD_KEYGUARD_GOING_AWAY, AppTransitionController.getTransitCompatType(mDc.mAppTransition, mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps, - null /* wallpaperTarget */, null /* oldWallpaper */)); + null /* wallpaperTarget */, null /* oldWallpaper */, + false /*skipAppTransitionAnimation*/)); } @Test @@ -109,7 +112,8 @@ public class AppTransitionTests extends WindowTestsBase { assertEquals(TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE, AppTransitionController.getTransitCompatType(mDc.mAppTransition, mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps, - null /* wallpaperTarget */, null /* oldWallpaper */)); + null /* wallpaperTarget */, null /* oldWallpaper */, + false /*skipAppTransitionAnimation*/)); } @Test @@ -123,7 +127,23 @@ public class AppTransitionTests extends WindowTestsBase { assertEquals(TRANSIT_OLD_KEYGUARD_GOING_AWAY, AppTransitionController.getTransitCompatType(mDc.mAppTransition, mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps, - null /* wallpaperTarget */, null /* oldWallpaper */)); + null /* wallpaperTarget */, null /* oldWallpaper */, + false /*skipAppTransitionAnimation*/)); + } + + @Test + public void testSkipTransitionAnimation() { + final DisplayContent dc = createNewDisplay(Display.STATE_ON); + final ActivityRecord activity = createActivityRecord(dc); + + mDc.prepareAppTransition(TRANSIT_OPEN); + mDc.prepareAppTransition(TRANSIT_CLOSE); + mDc.mClosingApps.add(activity); + assertEquals(TRANSIT_OLD_UNSET, + AppTransitionController.getTransitCompatType(mDc.mAppTransition, + mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps, + null /* wallpaperTarget */, null /* oldWallpaper */, + true /*skipAppTransitionAnimation*/)); } @Test diff --git a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java index 74248a950a38..401ace03c554 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java @@ -77,6 +77,7 @@ public class TransitionTests extends WindowTestsBase { changes.put(oldTask, new Transition.ChangeInfo(true /* vis */, true /* exChg */)); changes.put(opening, new Transition.ChangeInfo(false /* vis */, true /* exChg */)); changes.put(closing, new Transition.ChangeInfo(true /* vis */, true /* exChg */)); + fillChangeMap(changes, newTask); // End states. closing.mVisibleRequested = false; opening.mVisibleRequested = true; @@ -141,6 +142,7 @@ public class TransitionTests extends WindowTestsBase { changes.put(opening, new Transition.ChangeInfo(false /* vis */, true /* exChg */)); changes.put(opening2, new Transition.ChangeInfo(false /* vis */, true /* exChg */)); changes.put(closing, new Transition.ChangeInfo(true /* vis */, true /* exChg */)); + fillChangeMap(changes, newTask); // End states. closing.mVisibleRequested = false; opening.mVisibleRequested = true; @@ -189,6 +191,8 @@ public class TransitionTests extends WindowTestsBase { changes.put(tda, new Transition.ChangeInfo(false /* vis */, true /* exChg */)); changes.put(showing, new Transition.ChangeInfo(false /* vis */, true /* exChg */)); changes.put(showing2, new Transition.ChangeInfo(false /* vis */, true /* exChg */)); + fillChangeMap(changes, tda); + // End states. showing.mVisibleRequested = true; showing2.mVisibleRequested = true; @@ -338,4 +342,12 @@ public class TransitionTests extends WindowTestsBase { assertEquals(FLAG_SHOW_WALLPAPER, info.getChange( tasks[showWallpaperTask].mRemoteToken.toWindowContainerToken()).getFlags()); } + + /** Fill the change map with all the parents of top. Change maps are usually fully populated */ + private static void fillChangeMap(ArrayMap<WindowContainer, Transition.ChangeInfo> changes, + WindowContainer top) { + for (WindowContainer curr = top.getParent(); curr != null; curr = curr.getParent()) { + changes.put(curr, new Transition.ChangeInfo(true /* vis */, false /* exChg */)); + } + } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppAutoFocusHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppAutoFocusHelper.kt index d587f1e383c5..6bf2c855f08a 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppAutoFocusHelper.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppAutoFocusHelper.kt @@ -19,8 +19,21 @@ package com.android.server.wm.flicker.helpers import android.app.Instrumentation import androidx.test.uiautomator.UiDevice -class ImeAppAutoFocusHelper(instr: Instrumentation) : ImeAppHelper(instr, "ImeAppAutoFocus") { +class ImeAppAutoFocusHelper @JvmOverloads constructor( + instr: Instrumentation, + private val rotation: Int, + private val imePackageName: String = IME_PACKAGE +) : ImeAppHelper(instr, "ImeAppAutoFocus") { override fun openIME(device: UiDevice) { // do nothing (the app is focused automatically) } + + override fun open() { + val expectedPackage = if (rotation.isRotated()) { + imePackageName + } else { + packageName + } + launcherStrategy.launch(appName, expectedPackage) + } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt index b341e621d9ed..412a3c383785 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt @@ -61,10 +61,11 @@ class CloseImeAutoOpenWindowToAppTest( @JvmStatic fun getParams(): List<Array<Any>> { val instrumentation = InstrumentationRegistry.getInstrumentation() - val testApp = ImeAppAutoFocusHelper(instrumentation) return FlickerTestRunnerFactory(instrumentation) .buildTest { configuration -> + val testApp = ImeAppAutoFocusHelper(instrumentation, + configuration.startRotation) withTag { buildTestTag("imeToAppAutoOpen", testApp, configuration) } repeat { configuration.repetitions } setup { diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt index 51a4ca86681b..60a798fda5e4 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt @@ -61,10 +61,11 @@ class CloseImeAutoOpenWindowToHomeTest( @JvmStatic fun getParams(): List<Array<Any>> { val instrumentation = InstrumentationRegistry.getInstrumentation() - val testApp = ImeAppAutoFocusHelper(instrumentation) return FlickerTestRunnerFactory(instrumentation) .buildTest { configuration -> + val testApp = ImeAppAutoFocusHelper(instrumentation, + configuration.startRotation) withTestName { buildTestTag("imeToHomeAutoOpen", testApp, configuration) } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt index c7114da50117..d1842739171d 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt @@ -66,11 +66,12 @@ class ReOpenImeWindowTest( @JvmStatic fun getParams(): List<Array<Any>> { val instrumentation = InstrumentationRegistry.getInstrumentation() - val testApp = ImeAppAutoFocusHelper(instrumentation) val testAppComponentName = ActivityOptions.IME_ACTIVITY_AUTO_FOCUS_COMPONENT_NAME return FlickerTestRunnerFactory(instrumentation, repetitions = 5) .buildTest { configuration -> + val testApp = ImeAppAutoFocusHelper(instrumentation, + configuration.startRotation) withTestName { buildTestTag("reOpenImeAutoFocus", testApp, configuration) } repeat { configuration.repetitions } setup { diff --git a/tests/net/java/com/android/server/connectivity/NetworkNotificationManagerTest.java b/tests/net/java/com/android/server/connectivity/NetworkNotificationManagerTest.java index b47be97ed002..cd4cfcf18804 100644 --- a/tests/net/java/com/android/server/connectivity/NetworkNotificationManagerTest.java +++ b/tests/net/java/com/android/server/connectivity/NetworkNotificationManagerTest.java @@ -20,6 +20,7 @@ import static com.android.server.connectivity.NetworkNotificationManager.Notific import static org.mockito.Mockito.any; import static org.mockito.Mockito.anyInt; +import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.eq; import static org.mockito.Mockito.mock; @@ -39,6 +40,7 @@ import android.net.NetworkCapabilities; import android.net.NetworkInfo; import android.os.UserHandle; import android.telephony.TelephonyManager; +import android.util.DisplayMetrics; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; @@ -46,7 +48,9 @@ import androidx.test.runner.AndroidJUnit4; import com.android.internal.R; import com.android.server.connectivity.NetworkNotificationManager.NotificationType; +import org.junit.AfterClass; import org.junit.Before; +import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.AdditionalAnswers; @@ -82,6 +86,7 @@ public class NetworkNotificationManagerTest { @Mock Context mCtx; @Mock Resources mResources; + @Mock DisplayMetrics mDisplayMetrics; @Mock PackageManager mPm; @Mock TelephonyManager mTelephonyManager; @Mock NotificationManager mNotificationManager; @@ -93,6 +98,17 @@ public class NetworkNotificationManagerTest { NetworkNotificationManager mManager; + + @BeforeClass + public static void setUpClass() { + Notification.DevFlags.sForceDefaults = true; + } + + @AfterClass + public static void tearDownClass() { + Notification.DevFlags.sForceDefaults = false; + } + @Before public void setUp() { MockitoAnnotations.initMocks(this); @@ -103,6 +119,7 @@ public class NetworkNotificationManagerTest { mCellNai.networkInfo = mNetworkInfo; mVpnNai.networkCapabilities = VPN_CAPABILITIES; mVpnNai.networkInfo = mNetworkInfo; + mDisplayMetrics.density = 2.275f; doReturn(true).when(mVpnNai).isVPN(); when(mCtx.getResources()).thenReturn(mResources); when(mCtx.getPackageManager()).thenReturn(mPm); @@ -114,6 +131,7 @@ public class NetworkNotificationManagerTest { .thenReturn(mNotificationManager); when(mNetworkInfo.getExtraInfo()).thenReturn("extra"); when(mResources.getColor(anyInt(), any())).thenReturn(0xFF607D8B); + when(mResources.getDisplayMetrics()).thenReturn(mDisplayMetrics); mManager = new NetworkNotificationManager(mCtx, mTelephonyManager); } @@ -136,15 +154,15 @@ public class NetworkNotificationManagerTest { public void testTitleOfPrivateDnsBroken() { // Test the title of mobile data. verifyTitleByNetwork(100, mCellNai, R.string.mobile_no_internet); - reset(mResources); + clearInvocations(mResources); // Test the title of wifi. verifyTitleByNetwork(101, mWifiNai, R.string.wifi_no_internet); - reset(mResources); + clearInvocations(mResources); // Test the title of other networks. verifyTitleByNetwork(102, mVpnNai, R.string.other_networks_no_internet); - reset(mResources); + clearInvocations(mResources); } @Test diff --git a/tests/net/java/com/android/server/net/NetworkStatsCollectionTest.java b/tests/net/java/com/android/server/net/NetworkStatsCollectionTest.java index 89146f945e1f..435c3c0af817 100644 --- a/tests/net/java/com/android/server/net/NetworkStatsCollectionTest.java +++ b/tests/net/java/com/android/server/net/NetworkStatsCollectionTest.java @@ -64,7 +64,6 @@ import org.junit.runner.RunWith; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; -import java.io.DataOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.InputStream; @@ -124,7 +123,7 @@ public class NetworkStatsCollectionTest { // now export into a unified format final ByteArrayOutputStream bos = new ByteArrayOutputStream(); - collection.write(new DataOutputStream(bos)); + collection.write(bos); // clear structure completely collection.reset(); @@ -152,7 +151,7 @@ public class NetworkStatsCollectionTest { // now export into a unified format final ByteArrayOutputStream bos = new ByteArrayOutputStream(); - collection.write(new DataOutputStream(bos)); + collection.write(bos); // clear structure completely collection.reset(); @@ -180,7 +179,7 @@ public class NetworkStatsCollectionTest { // now export into a unified format final ByteArrayOutputStream bos = new ByteArrayOutputStream(); - collection.write(new DataOutputStream(bos)); + collection.write(bos); // clear structure completely collection.reset(); |