diff options
629 files changed, 15204 insertions, 5165 deletions
diff --git a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java index 45588e831cb9..9eb7bb7149ef 100644 --- a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java +++ b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java @@ -3941,6 +3941,10 @@ public class DeviceIdleController extends SystemService if (idleUntil) { mAlarmManager.setIdleUntil(AlarmManager.ELAPSED_REALTIME_WAKEUP, mNextAlarmTime, "DeviceIdleController.deep", mDeepAlarmListener, mHandler); + } else if (mState == STATE_LOCATING) { + // Use setExact so we don't keep the GPS active for too long. + mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, + mNextAlarmTime, "DeviceIdleController.deep", mDeepAlarmListener, mHandler); } else { if (mConstants.USE_WINDOW_ALARMS) { mAlarmManager.setWindow(AlarmManager.ELAPSED_REALTIME_WAKEUP, diff --git a/apex/media/framework/Android.bp b/apex/media/framework/Android.bp index 1bf732ba33f2..d963e68d80ec 100644 --- a/apex/media/framework/Android.bp +++ b/apex/media/framework/Android.bp @@ -120,7 +120,7 @@ filegroup { srcs: [ "java/android/media/ApplicationMediaCapabilities.java", "java/android/media/MediaFeature.java", - "java/android/media/MediaTranscodeManager.java", + "java/android/media/MediaTranscodingManager.java", ], path: "java", } diff --git a/apex/media/framework/api/system-current.txt b/apex/media/framework/api/system-current.txt index ce68447df051..6eea769d9f57 100644 --- a/apex/media/framework/api/system-current.txt +++ b/apex/media/framework/api/system-current.txt @@ -1,15 +1,15 @@ // Signature format: 2.0 package android.media { - public final class MediaTranscodeManager { - method @Nullable public android.media.MediaTranscodeManager.TranscodingSession enqueueRequest(@NonNull android.media.MediaTranscodeManager.TranscodingRequest, @NonNull java.util.concurrent.Executor, @NonNull android.media.MediaTranscodeManager.OnTranscodingFinishedListener); + public final class MediaTranscodingManager { + method @Nullable public android.media.MediaTranscodingManager.TranscodingSession enqueueRequest(@NonNull android.media.MediaTranscodingManager.TranscodingRequest, @NonNull java.util.concurrent.Executor, @NonNull android.media.MediaTranscodingManager.OnTranscodingFinishedListener); } - @java.lang.FunctionalInterface public static interface MediaTranscodeManager.OnTranscodingFinishedListener { - method public void onTranscodingFinished(@NonNull android.media.MediaTranscodeManager.TranscodingSession); + @java.lang.FunctionalInterface public static interface MediaTranscodingManager.OnTranscodingFinishedListener { + method public void onTranscodingFinished(@NonNull android.media.MediaTranscodingManager.TranscodingSession); } - public abstract static class MediaTranscodeManager.TranscodingRequest { + public abstract static class MediaTranscodingManager.TranscodingRequest { method public int getClientPid(); method public int getClientUid(); method @Nullable public android.os.ParcelFileDescriptor getDestinationFileDescriptor(); @@ -18,13 +18,13 @@ package android.media { method @NonNull public android.net.Uri getSourceUri(); } - public static class MediaTranscodeManager.TranscodingRequest.VideoFormatResolver { - ctor public MediaTranscodeManager.TranscodingRequest.VideoFormatResolver(@NonNull android.media.ApplicationMediaCapabilities, @NonNull android.media.MediaFormat); + public static class MediaTranscodingManager.TranscodingRequest.VideoFormatResolver { + ctor public MediaTranscodingManager.TranscodingRequest.VideoFormatResolver(@NonNull android.media.ApplicationMediaCapabilities, @NonNull android.media.MediaFormat); method @Nullable public android.media.MediaFormat resolveVideoFormat(); method public boolean shouldTranscode(); } - public static final class MediaTranscodeManager.TranscodingSession { + public static final class MediaTranscodingManager.TranscodingSession { method public boolean addClientUid(int); method public void cancel(); method @NonNull public java.util.List<java.lang.Integer> getClientUids(); @@ -33,7 +33,7 @@ package android.media { method public int getResult(); method public int getSessionId(); method public int getStatus(); - method public void setOnProgressUpdateListener(@NonNull java.util.concurrent.Executor, @Nullable android.media.MediaTranscodeManager.TranscodingSession.OnProgressUpdateListener); + method public void setOnProgressUpdateListener(@NonNull java.util.concurrent.Executor, @Nullable android.media.MediaTranscodingManager.TranscodingSession.OnProgressUpdateListener); field public static final int ERROR_DROPPED_BY_SERVICE = 1; // 0x1 field public static final int ERROR_NONE = 0; // 0x0 field public static final int ERROR_SERVICE_DIED = 2; // 0x2 @@ -47,21 +47,21 @@ package android.media { field public static final int STATUS_RUNNING = 2; // 0x2 } - @java.lang.FunctionalInterface public static interface MediaTranscodeManager.TranscodingSession.OnProgressUpdateListener { - method public void onProgressUpdate(@NonNull android.media.MediaTranscodeManager.TranscodingSession, @IntRange(from=0, to=100) int); + @java.lang.FunctionalInterface public static interface MediaTranscodingManager.TranscodingSession.OnProgressUpdateListener { + method public void onProgressUpdate(@NonNull android.media.MediaTranscodingManager.TranscodingSession, @IntRange(from=0, to=100) int); } - public static final class MediaTranscodeManager.VideoTranscodingRequest extends android.media.MediaTranscodeManager.TranscodingRequest { + public static final class MediaTranscodingManager.VideoTranscodingRequest extends android.media.MediaTranscodingManager.TranscodingRequest { method @NonNull public android.media.MediaFormat getVideoTrackFormat(); } - public static final class MediaTranscodeManager.VideoTranscodingRequest.Builder { - ctor public MediaTranscodeManager.VideoTranscodingRequest.Builder(@NonNull android.net.Uri, @NonNull android.net.Uri, @NonNull android.media.MediaFormat); - method @NonNull public android.media.MediaTranscodeManager.VideoTranscodingRequest build(); - method @NonNull public android.media.MediaTranscodeManager.VideoTranscodingRequest.Builder setClientPid(int); - method @NonNull public android.media.MediaTranscodeManager.VideoTranscodingRequest.Builder setClientUid(int); - method @NonNull public android.media.MediaTranscodeManager.VideoTranscodingRequest.Builder setDestinationFileDescriptor(@NonNull android.os.ParcelFileDescriptor); - method @NonNull public android.media.MediaTranscodeManager.VideoTranscodingRequest.Builder setSourceFileDescriptor(@NonNull android.os.ParcelFileDescriptor); + public static final class MediaTranscodingManager.VideoTranscodingRequest.Builder { + ctor public MediaTranscodingManager.VideoTranscodingRequest.Builder(@NonNull android.net.Uri, @NonNull android.net.Uri, @NonNull android.media.MediaFormat); + method @NonNull public android.media.MediaTranscodingManager.VideoTranscodingRequest build(); + method @NonNull public android.media.MediaTranscodingManager.VideoTranscodingRequest.Builder setClientPid(int); + method @NonNull public android.media.MediaTranscodingManager.VideoTranscodingRequest.Builder setClientUid(int); + method @NonNull public android.media.MediaTranscodingManager.VideoTranscodingRequest.Builder setDestinationFileDescriptor(@NonNull android.os.ParcelFileDescriptor); + method @NonNull public android.media.MediaTranscodingManager.VideoTranscodingRequest.Builder setSourceFileDescriptor(@NonNull android.os.ParcelFileDescriptor); } } diff --git a/apex/media/framework/java/android/media/MediaFrameworkInitializer.java b/apex/media/framework/java/android/media/MediaFrameworkInitializer.java index de2924e160b6..75a56b7231d9 100644 --- a/apex/media/framework/java/android/media/MediaFrameworkInitializer.java +++ b/apex/media/framework/java/android/media/MediaFrameworkInitializer.java @@ -75,8 +75,8 @@ public class MediaFrameworkInitializer { public static void registerServiceWrappers() { SystemServiceRegistry.registerContextAwareService( Context.MEDIA_TRANSCODING_SERVICE, - MediaTranscodeManager.class, - context -> new MediaTranscodeManager(context) + MediaTranscodingManager.class, + context -> new MediaTranscodingManager(context) ); if (SdkLevel.isAtLeastS()) { SystemServiceRegistry.registerContextAwareService( diff --git a/apex/media/framework/java/android/media/MediaTranscodeManager.java b/apex/media/framework/java/android/media/MediaTranscodingManager.java index 5742d43f83b7..93d58d07f81a 100644 --- a/apex/media/framework/java/android/media/MediaTranscodeManager.java +++ b/apex/media/framework/java/android/media/MediaTranscodingManager.java @@ -54,7 +54,7 @@ import java.util.concurrent.Executors; /** Android 12 introduces Compatible media transcoding feature. See <a href="https://developer.android.com/about/versions/12/features#compatible_media_transcoding"> - Compatible media transcoding</a>. MediaTranscodeManager provides an interface to the system's media + Compatible media transcoding</a>. MediaTranscodingManager provides an interface to the system's media transcoding service and can be used to transcode media files, e.g. transcoding a video from HEVC to AVC. @@ -69,7 +69,7 @@ import java.util.concurrent.Executors; <p> To transcode a media file, first create a {@link TranscodingRequest} through its builder class {@link VideoTranscodingRequest.Builder}. Transcode requests are then enqueue to the manager through - {@link MediaTranscodeManager#enqueueRequest( + {@link MediaTranscodingManager#enqueueRequest( TranscodingRequest, Executor, OnTranscodingFinishedListener)} TranscodeRequest are processed based on client process's priority and request priority. When a transcode operation is completed the caller is notified via its @@ -87,8 +87,8 @@ import java.util.concurrent.Executors; */ @MinSdk(Build.VERSION_CODES.S) @SystemApi -public final class MediaTranscodeManager { - private static final String TAG = "MediaTranscodeManager"; +public final class MediaTranscodingManager { + private static final String TAG = "MediaTranscodingManager"; /** Maximum number of retry to connect to the service. */ private static final int CONNECT_SERVICE_RETRY_COUNT = 100; @@ -127,7 +127,7 @@ public final class MediaTranscodeManager { private final Object mLock = new Object(); @GuardedBy("mLock") @NonNull private ITranscodingClient mTranscodingClient = null; - private static MediaTranscodeManager sMediaTranscodeManager; + private static MediaTranscodingManager sMediaTranscodingManager; private void handleTranscodingFinished(int sessionId, TranscodingResultParcel result) { synchronized (mPendingTranscodingSessions) { @@ -306,7 +306,7 @@ public final class MediaTranscodeManager { } try { - // Do not set hasRetried for retry initiated by MediaTranscodeManager. + // Do not set hasRetried for retry initiated by MediaTranscodingManager. session.retryInternal(false /*setHasRetried*/); } catch (Exception re) { // TODO(hkuang): Return correct error code to the client. @@ -423,7 +423,7 @@ public final class MediaTranscodeManager { /** * @hide */ - public MediaTranscodeManager(@NonNull Context context) { + public MediaTranscodingManager(@NonNull Context context) { mContext = context; mContentResolver = mContext.getContentResolver(); mPackageName = mContext.getPackageName(); @@ -1348,7 +1348,7 @@ public final class MediaTranscodeManager { @IntRange(from = 0, to = 100) int progress); } - private final MediaTranscodeManager mManager; + private final MediaTranscodingManager mManager; private Executor mListenerExecutor; private OnTranscodingFinishedListener mListener; private int mSessionId = -1; @@ -1374,7 +1374,7 @@ public final class MediaTranscodeManager { private final TranscodingRequest mRequest; private TranscodingSession( - @NonNull MediaTranscodeManager manager, + @NonNull MediaTranscodingManager manager, @NonNull TranscodingRequest request, @NonNull TranscodingSessionParcel parcel, @NonNull @CallbackExecutor Executor executor, @@ -1675,10 +1675,10 @@ public final class MediaTranscodeManager { /** * Enqueues a TranscodingRequest for execution. - * <p> Upon successfully accepting the request, MediaTranscodeManager will return a + * <p> Upon successfully accepting the request, MediaTranscodingManager will return a * {@link TranscodingSession} to the client. Client should use {@link TranscodingSession} to * track the progress and get the result. - * <p> MediaTranscodeManager will return null if fails to accept the request due to service + * <p> MediaTranscodingManager will return null if fails to accept the request due to service * rebooting. Client could retry again after receiving null. * * @param transcodingRequest The TranscodingRequest to enqueue. diff --git a/cmds/bootanimation/Android.bp b/cmds/bootanimation/Android.bp index b2b66c27f795..3534624a58a2 100644 --- a/cmds/bootanimation/Android.bp +++ b/cmds/bootanimation/Android.bp @@ -71,7 +71,7 @@ cc_library_shared { "libui", "libjnigraphics", "libEGL", - "libGLESv1_CM", + "libGLESv2", "libgui", ], } diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp index 3109c5c1e075..98e447cf996f 100644 --- a/cmds/bootanimation/BootAnimation.cpp +++ b/cmds/bootanimation/BootAnimation.cpp @@ -52,9 +52,8 @@ #include <gui/DisplayEventReceiver.h> #include <gui/Surface.h> #include <gui/SurfaceComposerClient.h> - -#include <GLES/gl.h> -#include <GLES/glext.h> +#include <GLES2/gl2.h> +#include <GLES2/gl2ext.h> #include <EGL/eglext.h> #include "BootAnimation.h" @@ -108,6 +107,82 @@ static const char PROGRESS_PROP_NAME[] = "service.bootanim.progress"; static const char DISPLAYS_PROP_NAME[] = "persist.service.bootanim.displays"; static const int ANIM_ENTRY_NAME_MAX = ANIM_PATH_MAX + 1; static constexpr size_t TEXT_POS_LEN_MAX = 16; +static const int DYNAMIC_COLOR_COUNT = 4; +static const char U_TEXTURE[] = "uTexture"; +static const char U_FADE[] = "uFade"; +static const char U_CROP_AREA[] = "uCropArea"; +static const char U_START_COLOR_PREFIX[] = "uStartColor"; +static const char U_END_COLOR_PREFIX[] = "uEndColor"; +static const char U_COLOR_PROGRESS[] = "uColorProgress"; +static const char A_UV[] = "aUv"; +static const char A_POSITION[] = "aPosition"; +static const char VERTEX_SHADER_SOURCE[] = R"( + precision mediump float; + attribute vec4 aPosition; + attribute highp vec2 aUv; + varying highp vec2 vUv; + void main() { + gl_Position = aPosition; + vUv = aUv; + })"; +static const char IMAGE_FRAG_DYNAMIC_COLORING_SHADER_SOURCE[] = R"( + precision mediump float; + uniform sampler2D uTexture; + uniform float uFade; + uniform float uColorProgress; + uniform vec4 uStartColor0; + uniform vec4 uStartColor1; + uniform vec4 uStartColor2; + uniform vec4 uStartColor3; + uniform vec4 uEndColor0; + uniform vec4 uEndColor1; + uniform vec4 uEndColor2; + uniform vec4 uEndColor3; + varying highp vec2 vUv; + void main() { + vec4 mask = texture2D(uTexture, vUv); + vec4 color = mask.r * mix(uStartColor0, uEndColor0, uColorProgress) + + mask.g * mix(uStartColor1, uEndColor1, uColorProgress) + + mask.b * mix(uStartColor2, uEndColor2, uColorProgress) + + mask.a * mix(uStartColor3, uEndColor3, uColorProgress); + gl_FragColor = vec4(color.x, color.y, color.z, (1.0 - uFade)) * color.a; + })"; +static const char IMAGE_FRAG_SHADER_SOURCE[] = R"( + precision mediump float; + uniform sampler2D uTexture; + uniform float uFade; + varying highp vec2 vUv; + void main() { + vec4 color = texture2D(uTexture, vUv); + gl_FragColor = vec4(color.x, color.y, color.z, (1.0 - uFade)) * color.a; + })"; +static const char TEXT_FRAG_SHADER_SOURCE[] = R"( + precision mediump float; + uniform sampler2D uTexture; + uniform vec4 uCropArea; + varying highp vec2 vUv; + void main() { + vec2 uv = vec2(mix(uCropArea.x, uCropArea.z, vUv.x), + mix(uCropArea.y, uCropArea.w, vUv.y)); + gl_FragColor = texture2D(uTexture, uv); + })"; + +static GLfloat quadPositions[] = { + -0.5f, -0.5f, + +0.5f, -0.5f, + +0.5f, +0.5f, + +0.5f, +0.5f, + -0.5f, +0.5f, + -0.5f, -0.5f +}; +static GLfloat quadUVs[] = { + 0.0f, 1.0f, + 1.0f, 1.0f, + 1.0f, 0.0f, + 1.0f, 0.0f, + 0.0f, 0.0f, + 0.0f, 1.0f +}; // --------------------------------------------------------------------------- @@ -163,7 +238,8 @@ void BootAnimation::binderDied(const wp<IBinder>&) { requestExit(); } -static void* decodeImage(const void* encodedData, size_t dataLength, AndroidBitmapInfo* outInfo) { +static void* decodeImage(const void* encodedData, size_t dataLength, AndroidBitmapInfo* outInfo, + bool premultiplyAlpha) { AImageDecoder* decoder = nullptr; AImageDecoder_createFromBuffer(encodedData, dataLength, &decoder); if (!decoder) { @@ -177,6 +253,10 @@ static void* decodeImage(const void* encodedData, size_t dataLength, AndroidBitm outInfo->stride = AImageDecoder_getMinimumStride(decoder); outInfo->flags = 0; + if (!premultiplyAlpha) { + AImageDecoder_setUnpremultipliedRequired(decoder, true); + } + const size_t size = outInfo->stride * outInfo->height; void* pixels = malloc(size); int result = AImageDecoder_decodeImage(decoder, pixels, outInfo->stride, size); @@ -190,13 +270,14 @@ static void* decodeImage(const void* encodedData, size_t dataLength, AndroidBitm } status_t BootAnimation::initTexture(Texture* texture, AssetManager& assets, - const char* name) { + const char* name, bool premultiplyAlpha) { Asset* asset = assets.open(name, Asset::ACCESS_BUFFER); if (asset == nullptr) return NO_INIT; AndroidBitmapInfo bitmapInfo; - void* pixels = decodeImage(asset->getBuffer(false), asset->getLength(), &bitmapInfo); + void* pixels = decodeImage(asset->getBuffer(false), asset->getLength(), &bitmapInfo, + premultiplyAlpha); auto pixelDeleter = std::unique_ptr<void, decltype(free)*>{ pixels, free }; asset->close(); @@ -209,7 +290,6 @@ status_t BootAnimation::initTexture(Texture* texture, AssetManager& assets, const int w = bitmapInfo.width; const int h = bitmapInfo.height; - GLint crop[4] = { 0, h, w, -h }; texture->w = w; texture->h = h; @@ -237,18 +317,19 @@ status_t BootAnimation::initTexture(Texture* texture, AssetManager& assets, break; } - glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, crop); - glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); - glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); return NO_ERROR; } -status_t BootAnimation::initTexture(FileMap* map, int* width, int* height) { +status_t BootAnimation::initTexture(FileMap* map, int* width, int* height, + bool premultiplyAlpha) { AndroidBitmapInfo bitmapInfo; - void* pixels = decodeImage(map->getDataPtr(), map->getDataLength(), &bitmapInfo); + void* pixels = decodeImage(map->getDataPtr(), map->getDataLength(), &bitmapInfo, + premultiplyAlpha); auto pixelDeleter = std::unique_ptr<void, decltype(free)*>{ pixels, free }; // FileMap memory is never released until application exit. @@ -263,7 +344,6 @@ status_t BootAnimation::initTexture(FileMap* map, int* width, int* height) { const int w = bitmapInfo.width; const int h = bitmapInfo.height; - GLint crop[4] = { 0, h, w, -h }; int tw = 1 << (31 - __builtin_clz(w)); int th = 1 << (31 - __builtin_clz(h)); if (tw < w) tw <<= 1; @@ -297,7 +377,10 @@ status_t BootAnimation::initTexture(FileMap* map, int* width, int* height) { break; } - glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, crop); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); *width = w; *height = h; @@ -470,7 +553,9 @@ status_t BootAnimation::readyToRun() { eglInitialize(display, nullptr, nullptr); EGLConfig config = getEglConfig(display); EGLSurface surface = eglCreateWindowSurface(display, config, s.get(), nullptr); - EGLContext context = eglCreateContext(display, config, nullptr, nullptr); + // Initialize egl context with client version number 2.0. + EGLint contextAttributes[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE}; + EGLContext context = eglCreateContext(display, config, nullptr, contextAttributes); EGLint w, h; eglQuerySurface(display, surface, EGL_WIDTH, &w); eglQuerySurface(display, surface, EGL_HEIGHT, &h); @@ -503,11 +588,6 @@ status_t BootAnimation::readyToRun() { void BootAnimation::projectSceneToWindow() { glViewport(0, 0, mWidth, mHeight); glScissor(0, 0, mWidth, mHeight); - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - glOrthof(0, static_cast<float>(mWidth), 0, static_cast<float>(mHeight), -1, 1); - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); } void BootAnimation::resizeSurface(int newWidth, int newHeight) { @@ -600,8 +680,71 @@ void BootAnimation::findBootAnimationFile() { } } +GLuint compileShader(GLenum shaderType, const GLchar *source) { + GLuint shader = glCreateShader(shaderType); + glShaderSource(shader, 1, &source, 0); + glCompileShader(shader); + GLint isCompiled = 0; + glGetShaderiv(shader, GL_COMPILE_STATUS, &isCompiled); + if (isCompiled == GL_FALSE) { + SLOGE("Compile shader failed. Shader type: %d", shaderType); + return 0; + } + return shader; +} + +GLuint linkShader(GLuint vertexShader, GLuint fragmentShader) { + GLuint program = glCreateProgram(); + glAttachShader(program, vertexShader); + glAttachShader(program, fragmentShader); + glLinkProgram(program); + GLint isLinked = 0; + glGetProgramiv(program, GL_LINK_STATUS, (int *)&isLinked); + if (isLinked == GL_FALSE) { + SLOGE("Linking shader failed. Shader handles: vert %d, frag %d", + vertexShader, fragmentShader); + return 0; + } + return program; +} + +void BootAnimation::initShaders() { + bool dynamicColoringEnabled = mAnimation != nullptr && mAnimation->dynamicColoringEnabled; + GLuint vertexShader = compileShader(GL_VERTEX_SHADER, (const GLchar *)VERTEX_SHADER_SOURCE); + GLuint imageFragmentShader = + compileShader(GL_FRAGMENT_SHADER, dynamicColoringEnabled + ? (const GLchar *)IMAGE_FRAG_DYNAMIC_COLORING_SHADER_SOURCE + : (const GLchar *)IMAGE_FRAG_SHADER_SOURCE); + GLuint textFragmentShader = + compileShader(GL_FRAGMENT_SHADER, (const GLchar *)TEXT_FRAG_SHADER_SOURCE); + + // Initialize image shader. + mImageShader = linkShader(vertexShader, imageFragmentShader); + GLint positionLocation = glGetAttribLocation(mImageShader, A_POSITION); + GLint uvLocation = glGetAttribLocation(mImageShader, A_UV); + mImageTextureLocation = glGetUniformLocation(mImageShader, U_TEXTURE); + mImageFadeLocation = glGetUniformLocation(mImageShader, U_FADE); + glEnableVertexAttribArray(positionLocation); + glVertexAttribPointer(positionLocation, 2, GL_FLOAT, GL_FALSE, 0, quadPositions); + glVertexAttribPointer(uvLocation, 2, GL_FLOAT, GL_FALSE, 0, quadUVs); + glEnableVertexAttribArray(uvLocation); + + // Initialize text shader. + mTextShader = linkShader(vertexShader, textFragmentShader); + positionLocation = glGetAttribLocation(mTextShader, A_POSITION); + uvLocation = glGetAttribLocation(mTextShader, A_UV); + mTextTextureLocation = glGetUniformLocation(mTextShader, U_TEXTURE); + mTextCropAreaLocation = glGetUniformLocation(mTextShader, U_CROP_AREA); + glEnableVertexAttribArray(positionLocation); + glVertexAttribPointer(positionLocation, 2, GL_FLOAT, GL_FALSE, 0, quadPositions); + glVertexAttribPointer(uvLocation, 2, GL_FLOAT, GL_FALSE, 0, quadUVs); + glEnableVertexAttribArray(uvLocation); +} + bool BootAnimation::threadLoop() { bool result; + initShaders(); + // We have no bootanimation file, so we use the stock android logo // animation. if (mZipFileName.isEmpty()) { @@ -623,6 +766,8 @@ bool BootAnimation::threadLoop() { } bool BootAnimation::android() { + glActiveTexture(GL_TEXTURE0); + SLOGD("%sAnimationShownTiming start time: %" PRId64 "ms", mShuttingDown ? "Shutdown" : "Boot", elapsedRealtime()); initTexture(&mAndroid[0], mAssets, "images/android-logo-mask.png"); @@ -631,19 +776,14 @@ bool BootAnimation::android() { mCallbacks->init({}); // clear screen - glShadeModel(GL_FLAT); glDisable(GL_DITHER); glDisable(GL_SCISSOR_TEST); glClearColor(0,0,0,1); glClear(GL_COLOR_BUFFER_BIT); eglSwapBuffers(mDisplay, mSurface); - glEnable(GL_TEXTURE_2D); - glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); - // Blend state glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); const nsecs_t startTime = systemTime(); do { @@ -666,12 +806,12 @@ bool BootAnimation::android() { glEnable(GL_SCISSOR_TEST); glDisable(GL_BLEND); glBindTexture(GL_TEXTURE_2D, mAndroid[1].name); - glDrawTexiOES(x, yc, 0, mAndroid[1].w, mAndroid[1].h); - glDrawTexiOES(x + mAndroid[1].w, yc, 0, mAndroid[1].w, mAndroid[1].h); + drawTexturedQuad(x, yc, mAndroid[1].w, mAndroid[1].h); + drawTexturedQuad(x + mAndroid[1].w, yc, mAndroid[1].w, mAndroid[1].h); glEnable(GL_BLEND); glBindTexture(GL_TEXTURE_2D, mAndroid[0].name); - glDrawTexiOES(xc, yc, 0, mAndroid[0].w, mAndroid[0].h); + drawTexturedQuad(xc, yc, mAndroid[0].w, mAndroid[0].h); EGLBoolean res = eglSwapBuffers(mDisplay, mSurface); if (res == EGL_FALSE) @@ -766,6 +906,20 @@ static bool parseColor(const char str[7], float color[3]) { return true; } +// Parse a color represented as a signed decimal int string. +// E.g. "-2757722" (whose hex 2's complement is 0xFFD5EBA6). +// If the input color string is empty, set color with values in defaultColor. +static void parseColorDecimalString(const std::string& colorString, + float color[3], float defaultColor[3]) { + if (colorString == "") { + memcpy(color, defaultColor, sizeof(float) * 3); + return; + } + int colorInt = atoi(colorString.c_str()); + color[0] = ((float)((colorInt >> 16) & 0xFF)) / 0xFF; // r + color[1] = ((float)((colorInt >> 8) & 0xFF)) / 0xFF; // g + color[2] = ((float)(colorInt & 0xFF)) / 0xFF; // b +} static bool readFile(ZipFileRO* zip, const char* name, String8& outString) { ZipEntryRO entry = zip->findEntryByName(name); @@ -798,10 +952,10 @@ status_t BootAnimation::initFont(Font* font, const char* fallback) { status = initTexture(font->map, &font->texture.w, &font->texture.h); - glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); - glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); } else if (fallback != nullptr) { status = initTexture(&font->texture, mAssets, fallback); } else { @@ -816,40 +970,11 @@ status_t BootAnimation::initFont(Font* font, const char* fallback) { return status; } -void BootAnimation::fadeFrame(const int frameLeft, const int frameBottom, const int frameWidth, - const int frameHeight, const Animation::Part& part, - const int fadedFramesCount) { - glEnable(GL_BLEND); - glEnableClientState(GL_VERTEX_ARRAY); - glDisable(GL_TEXTURE_2D); - // avoid creating a hole due to mixing result alpha with GL_REPLACE texture - glBlendFuncSeparateOES(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ONE); - - const float alpha = static_cast<float>(fadedFramesCount) / part.framesToFadeCount; - glColor4f(part.backgroundColor[0], part.backgroundColor[1], part.backgroundColor[2], alpha); - - const float frameStartX = static_cast<float>(frameLeft); - const float frameStartY = static_cast<float>(frameBottom); - const float frameEndX = frameStartX + frameWidth; - const float frameEndY = frameStartY + frameHeight; - const GLfloat frameRect[] = { - frameStartX, frameStartY, - frameEndX, frameStartY, - frameEndX, frameEndY, - frameStartX, frameEndY - }; - glVertexPointer(2, GL_FLOAT, 0, frameRect); - glDrawArrays(GL_TRIANGLE_FAN, 0, 4); - - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glEnable(GL_TEXTURE_2D); - glDisableClientState(GL_VERTEX_ARRAY); - glDisable(GL_BLEND); -} - void BootAnimation::drawText(const char* str, const Font& font, bool bold, int* x, int* y) { glEnable(GL_BLEND); // Allow us to draw on top of the animation glBindTexture(GL_TEXTURE_2D, font.texture.name); + glUseProgram(mTextShader); + glUniform1i(mTextTextureLocation, 0); const int len = strlen(str); const int strWidth = font.char_width * len; @@ -865,8 +990,6 @@ void BootAnimation::drawText(const char* str, const Font& font, bool bold, int* *y = mHeight + *y - font.char_height; } - int cropRect[4] = { 0, 0, font.char_width, -font.char_height }; - for (int i = 0; i < len; i++) { char c = str[i]; @@ -878,13 +1001,13 @@ void BootAnimation::drawText(const char* str, const Font& font, bool bold, int* const int charPos = (c - FONT_BEGIN_CHAR); // Position in the list of valid characters const int row = charPos / FONT_NUM_COLS; const int col = charPos % FONT_NUM_COLS; - cropRect[0] = col * font.char_width; // Left of column - cropRect[1] = row * font.char_height * 2; // Top of row - // Move down to bottom of regular (one char_heigh) or bold (two char_heigh) line - cropRect[1] += bold ? 2 * font.char_height : font.char_height; - glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, cropRect); - - glDrawTexiOES(*x, *y, 0, font.char_width, font.char_height); + // Bold fonts are expected in the second half of each row. + float v0 = (row + (bold ? 0.5f : 0.0f)) / FONT_NUM_ROWS; + float u0 = ((float)col) / FONT_NUM_COLS; + float v1 = v0 + 1.0f / FONT_NUM_ROWS / 2; + float u1 = u0 + 1.0f / FONT_NUM_COLS; + glUniform4f(mTextCropAreaLocation, u0, v0, u1, v1); + drawTexturedQuad(*x, *y, font.char_width, font.char_height); *x += font.char_width; } @@ -938,6 +1061,8 @@ bool BootAnimation::parseAnimationDesc(Animation& animation) { return false; } char const* s = desString.string(); + std::string dynamicColoringPartName = ""; + bool postDynamicColoring = false; // Parse the description file for (;;) { @@ -956,7 +1081,13 @@ bool BootAnimation::parseAnimationDesc(Animation& animation) { char color[7] = "000000"; // default to black if unspecified char clockPos1[TEXT_POS_LEN_MAX + 1] = ""; char clockPos2[TEXT_POS_LEN_MAX + 1] = ""; + char dynamicColoringPartNameBuffer[ANIM_ENTRY_NAME_MAX]; char pathType; + // start colors default to black if unspecified + char start_color_0[7] = "000000"; + char start_color_1[7] = "000000"; + char start_color_2[7] = "000000"; + char start_color_3[7] = "000000"; int nextReadPos; @@ -971,6 +1102,15 @@ bool BootAnimation::parseAnimationDesc(Animation& animation) { } else { animation.progressEnabled = false; } + } else if (sscanf(l, "dynamic_colors %" STRTO(ANIM_PATH_MAX) "s #%6s #%6s #%6s #%6s", + dynamicColoringPartNameBuffer, + start_color_0, start_color_1, start_color_2, start_color_3)) { + animation.dynamicColoringEnabled = true; + parseColor(start_color_0, animation.startColors[0]); + parseColor(start_color_1, animation.startColors[1]); + parseColor(start_color_2, animation.startColors[2]); + parseColor(start_color_3, animation.startColors[3]); + dynamicColoringPartName = std::string(dynamicColoringPartNameBuffer); } else if (sscanf(l, "%c %d %d %" STRTO(ANIM_PATH_MAX) "s%n", &pathType, &count, &pause, path, &nextReadPos) >= 4) { if (pathType == 'f') { @@ -983,6 +1123,16 @@ bool BootAnimation::parseAnimationDesc(Animation& animation) { // "clockPos1=%s, clockPos2=%s", // pathType, count, pause, path, framesToFadeCount, color, clockPos1, clockPos2); Animation::Part part; + if (path == dynamicColoringPartName) { + // Part is specified to use dynamic coloring. + part.useDynamicColoring = true; + part.postDynamicColoring = false; + postDynamicColoring = true; + } else { + // Part does not use dynamic coloring. + part.useDynamicColoring = false; + part.postDynamicColoring = postDynamicColoring; + } part.playUntilComplete = pathType == 'c'; part.framesToFadeCount = framesToFadeCount; part.count = count; @@ -1166,19 +1316,16 @@ bool BootAnimation::movie() { // Blend required to draw time on top of animation frames. glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glShadeModel(GL_FLAT); glDisable(GL_DITHER); glDisable(GL_SCISSOR_TEST); glDisable(GL_BLEND); - glBindTexture(GL_TEXTURE_2D, 0); glEnable(GL_TEXTURE_2D); - glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); - glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); - glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); - glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - + glBindTexture(GL_TEXTURE_2D, 0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); bool clockFontInitialized = false; if (mClockEnabled) { clockFontInitialized = @@ -1193,6 +1340,10 @@ bool BootAnimation::movie() { mTimeCheckThread->run("BootAnimation::TimeCheckThread", PRIORITY_NORMAL); } + if (mAnimation != nullptr && mAnimation->dynamicColoringEnabled) { + initDynamicColors(); + } + playAnimation(*mAnimation); if (mTimeCheckThread != nullptr) { @@ -1218,6 +1369,55 @@ bool BootAnimation::shouldStopPlayingPart(const Animation::Part& part, (lastDisplayedProgress == 0 || lastDisplayedProgress == 100); } +// Linear mapping from range <a1, a2> to range <b1, b2> +float mapLinear(float x, float a1, float a2, float b1, float b2) { + return b1 + ( x - a1 ) * ( b2 - b1 ) / ( a2 - a1 ); +} + +void BootAnimation::drawTexturedQuad(float xStart, float yStart, float width, float height) { + // Map coordinates from screen space to world space. + float x0 = mapLinear(xStart, 0, mWidth, -1, 1); + float y0 = mapLinear(yStart, 0, mHeight, -1, 1); + float x1 = mapLinear(xStart + width, 0, mWidth, -1, 1); + float y1 = mapLinear(yStart + height, 0, mHeight, -1, 1); + // Update quad vertex positions. + quadPositions[0] = x0; + quadPositions[1] = y0; + quadPositions[2] = x1; + quadPositions[3] = y0; + quadPositions[4] = x1; + quadPositions[5] = y1; + quadPositions[6] = x1; + quadPositions[7] = y1; + quadPositions[8] = x0; + quadPositions[9] = y1; + quadPositions[10] = x0; + quadPositions[11] = y0; + glDrawArrays(GL_TRIANGLES, 0, + sizeof(quadPositions) / sizeof(quadPositions[0]) / 2); +} + +void BootAnimation::initDynamicColors() { + for (int i = 0; i < DYNAMIC_COLOR_COUNT; i++) { + parseColorDecimalString( + android::base::GetProperty("persist.bootanim.color" + std::to_string(i + 1), ""), + mAnimation->endColors[i], mAnimation->startColors[i]); + } + glUseProgram(mImageShader); + SLOGI("[BootAnimation] Dynamically coloring boot animation."); + for (int i = 0; i < DYNAMIC_COLOR_COUNT; i++) { + float *startColor = mAnimation->startColors[i]; + float *endColor = mAnimation->endColors[i]; + glUniform4f(glGetUniformLocation(mImageShader, + (U_START_COLOR_PREFIX + std::to_string(i)).c_str()), + startColor[0], startColor[1], startColor[2], 1 /* alpha */); + glUniform4f(glGetUniformLocation(mImageShader, + (U_END_COLOR_PREFIX + std::to_string(i)).c_str()), + endColor[0], endColor[1], endColor[2], 1 /* alpha */); + } + mImageColorProgressLocation = glGetUniformLocation(mImageShader, U_COLOR_PROGRESS); +} + bool BootAnimation::playAnimation(const Animation& animation) { const size_t pcount = animation.parts.size(); nsecs_t frameDuration = s2ns(1) / animation.fps; @@ -1230,7 +1430,6 @@ bool BootAnimation::playAnimation(const Animation& animation) { for (size_t i=0 ; i<pcount ; i++) { const Animation::Part& part(animation.parts[i]); const size_t fcount = part.frames.size(); - glBindTexture(GL_TEXTURE_2D, 0); // Handle animation package if (part.animation != nullptr) { @@ -1261,6 +1460,14 @@ bool BootAnimation::playAnimation(const Animation& animation) { for (size_t j=0 ; j<fcount ; j++) { if (shouldStopPlayingPart(part, fadedFramesCount, lastDisplayedProgress)) break; + // Color progress is + // - the normalized animation progress between [0, 1] for the dynamic coloring part, + // - 0 for parts that come before, + // - 1 for parts that come after. + float colorProgress = part.useDynamicColoring + ? (float)j / fcount + : (part.postDynamicColoring ? 1 : 0); + processDisplayEvents(); const int animationX = (mWidth - animation.width) / 2; @@ -1272,44 +1479,38 @@ bool BootAnimation::playAnimation(const Animation& animation) { if (r > 0) { glBindTexture(GL_TEXTURE_2D, frame.tid); } else { - if (part.count != 1) { - glGenTextures(1, &frame.tid); - glBindTexture(GL_TEXTURE_2D, frame.tid); - glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - } + glGenTextures(1, &frame.tid); + glBindTexture(GL_TEXTURE_2D, frame.tid); int w, h; - initTexture(frame.map, &w, &h); + // Set decoding option to alpha unpremultiplied so that the R, G, B channels + // of transparent pixels are preserved. + initTexture(frame.map, &w, &h, false /* don't premultiply alpha */); } const int xc = animationX + frame.trimX; const int yc = animationY + frame.trimY; - Region clearReg(Rect(mWidth, mHeight)); - clearReg.subtractSelf(Rect(xc, yc, xc+frame.trimWidth, yc+frame.trimHeight)); - if (!clearReg.isEmpty()) { - Region::const_iterator head(clearReg.begin()); - Region::const_iterator tail(clearReg.end()); - glEnable(GL_SCISSOR_TEST); - while (head != tail) { - const Rect& r2(*head++); - glScissor(r2.left, mHeight - r2.bottom, r2.width(), r2.height()); - glClear(GL_COLOR_BUFFER_BIT); - } - glDisable(GL_SCISSOR_TEST); - } + glClear(GL_COLOR_BUFFER_BIT); // specify the y center as ceiling((mHeight - frame.trimHeight) / 2) // which is equivalent to mHeight - (yc + frame.trimHeight) const int frameDrawY = mHeight - (yc + frame.trimHeight); - glDrawTexiOES(xc, frameDrawY, 0, frame.trimWidth, frame.trimHeight); + float fade = 0; // if the part hasn't been stopped yet then continue fading if necessary if (exitPending() && part.hasFadingPhase()) { - fadeFrame(xc, frameDrawY, frame.trimWidth, frame.trimHeight, part, - ++fadedFramesCount); + fade = static_cast<float>(++fadedFramesCount) / part.framesToFadeCount; if (fadedFramesCount >= part.framesToFadeCount) { fadedFramesCount = MAX_FADED_FRAMES_COUNT; // no more fading } } + glUseProgram(mImageShader); + glUniform1i(mImageTextureLocation, 0); + glUniform1f(mImageFadeLocation, fade); + if (animation.dynamicColoringEnabled) { + glUniform1f(mImageColorProgressLocation, colorProgress); + } + glEnable(GL_BLEND); + drawTexturedQuad(xc, frameDrawY, frame.trimWidth, frame.trimHeight); + glDisable(GL_BLEND); if (mClockEnabled && mTimeIsAccurate && validClock(part)) { drawClock(animation.clockFont, part.clockPosX, part.clockPosY); diff --git a/cmds/bootanimation/BootAnimation.h b/cmds/bootanimation/BootAnimation.h index f8a31c6d8790..0e29621f0326 100644 --- a/cmds/bootanimation/BootAnimation.h +++ b/cmds/bootanimation/BootAnimation.h @@ -31,7 +31,7 @@ #include <binder/IBinder.h> #include <EGL/egl.h> -#include <GLES/gl.h> +#include <GLES2/gl2.h> namespace android { @@ -53,7 +53,7 @@ public: }; struct Font { - FileMap* map; + FileMap* map = nullptr; Texture texture; int char_width; int char_height; @@ -62,7 +62,7 @@ public: struct Animation { struct Frame { String8 name; - FileMap* map; + FileMap* map = nullptr; int trimX; int trimY; int trimWidth; @@ -90,6 +90,10 @@ public: uint8_t* audioData; int audioLength; Animation* animation; + // Controls if dynamic coloring is enabled for this part. + bool useDynamicColoring = false; + // Defines if this part is played after the dynamic coloring part. + bool postDynamicColoring = false; bool hasFadingPhase() const { return !playUntilComplete && framesToFadeCount > 0; @@ -105,6 +109,10 @@ public: ZipFileRO* zip; Font clockFont; Font progressFont; + // Controls if dynamic coloring is enabled for the whole animation. + bool dynamicColoringEnabled = false; + float startColors[4][3]; // Start colors of dynamic color transition. + float endColors[4][3]; // End colors of dynamic color transition. }; // All callbacks will be called from this class's internal thread. @@ -163,9 +171,12 @@ private: int displayEventCallback(int fd, int events, void* data); void processDisplayEvents(); - status_t initTexture(Texture* texture, AssetManager& asset, const char* name); - status_t initTexture(FileMap* map, int* width, int* height); + status_t initTexture(Texture* texture, AssetManager& asset, const char* name, + bool premultiplyAlpha = true); + status_t initTexture(FileMap* map, int* width, int* height, + bool premultiplyAlpha = true); status_t initFont(Font* font, const char* fallback); + void initShaders(); bool android(); bool movie(); void drawText(const char* str, const Font& font, bool bold, int* x, int* y); @@ -173,6 +184,7 @@ private: void drawProgress(int percent, const Font& font, const int xPos, const int yPos); void fadeFrame(int frameLeft, int frameBottom, int frameWidth, int frameHeight, const Animation::Part& part, int fadedFramesCount); + void drawTexturedQuad(float xStart, float yStart, float width, float height); bool validClock(const Animation::Part& part); Animation* loadAnimation(const String8&); bool playAnimation(const Animation&); @@ -192,6 +204,7 @@ private: void checkExit(); void handleViewport(nsecs_t timestep); + void initDynamicColors(); sp<SurfaceComposerClient> mSession; AssetManager mAssets; @@ -218,6 +231,13 @@ private: sp<TimeCheckThread> mTimeCheckThread = nullptr; sp<Callbacks> mCallbacks; Animation* mAnimation = nullptr; + GLuint mImageShader; + GLuint mTextShader; + GLuint mImageFadeLocation; + GLuint mImageTextureLocation; + GLuint mTextCropAreaLocation; + GLuint mTextTextureLocation; + GLuint mImageColorProgressLocation; }; // --------------------------------------------------------------------------- diff --git a/cmds/bootanimation/OWNERS b/cmds/bootanimation/OWNERS new file mode 100644 index 000000000000..b6fb007bea52 --- /dev/null +++ b/cmds/bootanimation/OWNERS @@ -0,0 +1,3 @@ +dupin@google.com +shanh@google.com +jreck@google.com diff --git a/core/api/current.txt b/core/api/current.txt index 03d58350d498..8f08658a76dc 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -2012,6 +2012,9 @@ package android { public static final class R.id { ctor public R.id(); field public static final int accessibilityActionContextClick = 16908348; // 0x102003c + field public static final int accessibilityActionDragCancel; + field public static final int accessibilityActionDragDrop; + field public static final int accessibilityActionDragStart; field public static final int accessibilityActionHideTooltip = 16908357; // 0x1020045 field public static final int accessibilityActionImeEnter = 16908372; // 0x1020054 field public static final int accessibilityActionMoveWindow = 16908354; // 0x1020042 @@ -20178,8 +20181,10 @@ package android.media { method public int getAllowedCapturePolicy(); method public int getContentType(); method public int getFlags(); + method public int getSpatializationBehavior(); method public int getUsage(); method public int getVolumeControlStream(); + method public boolean isContentSpatialized(); method public void writeToParcel(android.os.Parcel, int); field public static final int ALLOW_CAPTURE_BY_ALL = 1; // 0x1 field public static final int ALLOW_CAPTURE_BY_NONE = 3; // 0x3 @@ -20193,6 +20198,8 @@ package android.media { field public static final int FLAG_AUDIBILITY_ENFORCED = 1; // 0x1 field public static final int FLAG_HW_AV_SYNC = 16; // 0x10 field @Deprecated public static final int FLAG_LOW_LATENCY = 256; // 0x100 + field public static final int SPATIALIZATION_BEHAVIOR_AUTO = 0; // 0x0 + field public static final int SPATIALIZATION_BEHAVIOR_NEVER = 1; // 0x1 field public static final int USAGE_ALARM = 4; // 0x4 field public static final int USAGE_ASSISTANCE_ACCESSIBILITY = 11; // 0xb field public static final int USAGE_ASSISTANCE_NAVIGATION_GUIDANCE = 12; // 0xc @@ -20219,7 +20226,9 @@ package android.media { method public android.media.AudioAttributes.Builder setContentType(int); method public android.media.AudioAttributes.Builder setFlags(int); method @NonNull public android.media.AudioAttributes.Builder setHapticChannelsMuted(boolean); + method @NonNull public android.media.AudioAttributes.Builder setIsContentSpatialized(boolean); method public android.media.AudioAttributes.Builder setLegacyStreamType(int); + method @NonNull public android.media.AudioAttributes.Builder setSpatializationBehavior(int); method public android.media.AudioAttributes.Builder setUsage(int); } @@ -20444,6 +20453,7 @@ package android.media { method public String getProperty(String); method public int getRingerMode(); method @Deprecated public int getRouting(int); + method @Nullable public android.media.Spatializer getSpatializer(); method public int getStreamMaxVolume(int); method public int getStreamMinVolume(int); method public int getStreamVolume(int); @@ -23840,6 +23850,17 @@ package android.media { method public void onLoadComplete(android.media.SoundPool, int, int); } + public class Spatializer { + method public void addOnSpatializerEnabledChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.Spatializer.OnSpatializerEnabledChangedListener); + method public boolean canBeSpatialized(@NonNull android.media.AudioAttributes, @NonNull android.media.AudioFormat); + method public boolean isEnabled(); + method public void removeOnSpatializerEnabledChangedListener(@NonNull android.media.Spatializer.OnSpatializerEnabledChangedListener); + } + + public static interface Spatializer.OnSpatializerEnabledChangedListener { + method public void onSpatializerEnabledChanged(boolean); + } + public final class SubtitleData { ctor public SubtitleData(int, long, long, @NonNull byte[]); method @NonNull public byte[] getData(); @@ -50637,6 +50658,9 @@ package android.view.accessibility { method public void setPackageName(CharSequence); method public void writeToParcel(android.os.Parcel, int); field public static final int CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION = 4; // 0x4 + field public static final int CONTENT_CHANGE_TYPE_DRAG_CANCELLED = 512; // 0x200 + field public static final int CONTENT_CHANGE_TYPE_DRAG_DROPPED = 256; // 0x100 + field public static final int CONTENT_CHANGE_TYPE_DRAG_STARTED = 128; // 0x80 field public static final int CONTENT_CHANGE_TYPE_PANE_APPEARED = 16; // 0x10 field public static final int CONTENT_CHANGE_TYPE_PANE_DISAPPEARED = 32; // 0x20 field public static final int CONTENT_CHANGE_TYPE_PANE_TITLE = 8; // 0x8 @@ -50935,6 +50959,9 @@ package android.view.accessibility { field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_COPY; field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_CUT; field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_DISMISS; + field @NonNull public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_DRAG_CANCEL; + field @NonNull public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_DRAG_DROP; + field @NonNull public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_DRAG_START; field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_EXPAND; field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_FOCUS; field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_HIDE_TOOLTIP; diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 2d73aa67ed1a..e5008b493ceb 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -3262,11 +3262,13 @@ package android.hardware.display { public final class DisplayManager { method @RequiresPermission(android.Manifest.permission.ACCESS_AMBIENT_LIGHT_STATS) public java.util.List<android.hardware.display.AmbientBrightnessDayStats> getAmbientBrightnessStats(); method @RequiresPermission(android.Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS) public android.hardware.display.BrightnessConfiguration getBrightnessConfiguration(); + method @Nullable @RequiresPermission(android.Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS) public android.hardware.display.BrightnessConfiguration getBrightnessConfigurationForDisplay(@NonNull String); method @RequiresPermission(android.Manifest.permission.BRIGHTNESS_SLIDER_USAGE) public java.util.List<android.hardware.display.BrightnessChangeEvent> getBrightnessEvents(); method @Nullable @RequiresPermission(android.Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS) public android.hardware.display.BrightnessConfiguration getDefaultBrightnessConfiguration(); method public android.util.Pair<float[],float[]> getMinimumBrightnessCurve(); method public android.graphics.Point getStableDisplaySize(); method @RequiresPermission(android.Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS) public void setBrightnessConfiguration(android.hardware.display.BrightnessConfiguration); + method @RequiresPermission(android.Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS) public void setBrightnessConfigurationForDisplay(@NonNull android.hardware.display.BrightnessConfiguration, @NonNull String); method @Deprecated @RequiresPermission(android.Manifest.permission.CONTROL_DISPLAY_SATURATION) public void setSaturationLevel(float); } @@ -5370,6 +5372,12 @@ package android.media { field public static final android.media.RouteDiscoveryPreference EMPTY; } + public class Spatializer { + method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void addCompatibleAudioDevice(@NonNull android.media.AudioDeviceAttributes); + method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public java.util.List<android.media.AudioDeviceAttributes> getCompatibleAudioDevices(); + method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void removeCompatibleAudioDevice(@NonNull android.media.AudioDeviceAttributes); + } + } package android.media.audiofx { diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 85c4f3172775..0732c6136401 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -1133,10 +1133,10 @@ package android.hardware.camera2 { package android.hardware.devicestate { public final class DeviceStateManager { - method @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_STATE) public void cancelRequest(@NonNull android.hardware.devicestate.DeviceStateRequest); + method @RequiresPermission(value=android.Manifest.permission.CONTROL_DEVICE_STATE, conditional=true) public void cancelRequest(@NonNull android.hardware.devicestate.DeviceStateRequest); method @NonNull public int[] getSupportedStates(); method public void registerCallback(@NonNull java.util.concurrent.Executor, @NonNull android.hardware.devicestate.DeviceStateManager.DeviceStateCallback); - method @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_STATE) public void requestState(@NonNull android.hardware.devicestate.DeviceStateRequest, @Nullable java.util.concurrent.Executor, @Nullable android.hardware.devicestate.DeviceStateRequest.Callback); + method @RequiresPermission(value=android.Manifest.permission.CONTROL_DEVICE_STATE, conditional=true) public void requestState(@NonNull android.hardware.devicestate.DeviceStateRequest, @Nullable java.util.concurrent.Executor, @Nullable android.hardware.devicestate.DeviceStateRequest.Callback); method public void unregisterCallback(@NonNull android.hardware.devicestate.DeviceStateManager.DeviceStateCallback); field public static final int MAXIMUM_DEVICE_STATE = 255; // 0xff field public static final int MINIMUM_DEVICE_STATE = 0; // 0x0 @@ -3323,7 +3323,7 @@ package android.window { ctor public WindowProviderService(); method public final void attachToWindowToken(@NonNull android.os.IBinder); method @NonNull public int getInitialDisplayId(); - method @Nullable public android.os.Bundle getWindowContextOptions(); + method @CallSuper @Nullable public android.os.Bundle getWindowContextOptions(); method public abstract int getWindowType(); } diff --git a/core/java/android/app/ActivityClient.java b/core/java/android/app/ActivityClient.java index 2efdf51612c7..db7ab1a6f379 100644 --- a/core/java/android/app/ActivityClient.java +++ b/core/java/android/app/ActivityClient.java @@ -207,7 +207,8 @@ public class ActivityClient { } /** - * Returns the activity token below in the same task if it belongs to the same process. + * Returns the non-finishing activity token below in the same task if it belongs to the same + * process. */ @Nullable public IBinder getActivityTokenBelow(IBinder activityToken) { diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java index 45db0f6ff3e2..fd6fa57b9e8d 100644 --- a/core/java/android/app/Instrumentation.java +++ b/core/java/android/app/Instrumentation.java @@ -749,7 +749,8 @@ public class Instrumentation { * @see #onStartActivity(Intent) * @hide */ - public ActivityResult onStartActivity(Context who, Intent intent, Bundle options) { + public ActivityResult onStartActivity(@NonNull Context who, @NonNull Intent intent, + @NonNull Bundle options) { return onStartActivity(intent); } diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 4b054f49d910..e39636dc45fb 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -5387,8 +5387,8 @@ public class Notification implements Parcelable contentView.setInt(R.id.expand_button, "setDefaultPillColor", pillColor); // Use different highlighted colors for conversations' unread count if (p.mHighlightExpander) { - pillColor = Colors.flattenAlpha(getPrimaryAccentColor(p), bgColor); - textColor = Colors.flattenAlpha(bgColor, pillColor); + pillColor = Colors.flattenAlpha(getColors(p).getTertiaryAccentColor(), bgColor); + textColor = Colors.flattenAlpha(getColors(p).getOnAccentTextColor(), pillColor); } contentView.setInt(R.id.expand_button, "setHighlightTextColor", textColor); contentView.setInt(R.id.expand_button, "setHighlightPillColor", pillColor); @@ -6247,8 +6247,9 @@ public class Notification implements Parcelable * * @param color the color to check * @return true if the color has higher contrast with white than black + * @hide */ - private static boolean isColorDark(int color) { + public static boolean isColorDark(int color) { // as per ContrastColorUtil.shouldUseDark, this uses the color contrast midpoint. return ContrastColorUtil.calculateLuminance(color) <= 0.17912878474; } @@ -12305,6 +12306,8 @@ public class Notification implements Parcelable private int mSecondaryTextColor = COLOR_INVALID; private int mPrimaryAccentColor = COLOR_INVALID; private int mSecondaryAccentColor = COLOR_INVALID; + private int mTertiaryAccentColor = COLOR_INVALID; + private int mOnAccentTextColor = COLOR_INVALID; private int mErrorColor = COLOR_INVALID; private int mContrastColor = COLOR_INVALID; private int mRippleAlpha = 0x33; @@ -12362,7 +12365,7 @@ public class Notification implements Parcelable if (isColorized) { if (rawColor == COLOR_DEFAULT) { - int[] attrs = {R.attr.colorAccentTertiary}; + int[] attrs = {R.attr.colorAccentSecondary}; try (TypedArray ta = obtainDayNightAttributes(ctx, attrs)) { mBackgroundColor = getColor(ta, 0, Color.WHITE); } @@ -12379,6 +12382,8 @@ public class Notification implements Parcelable mContrastColor = mPrimaryTextColor; mPrimaryAccentColor = mPrimaryTextColor; mSecondaryAccentColor = mSecondaryTextColor; + mTertiaryAccentColor = flattenAlpha(mPrimaryTextColor, mBackgroundColor); + mOnAccentTextColor = mBackgroundColor; mErrorColor = mPrimaryTextColor; mRippleAlpha = 0x33; } else { @@ -12389,6 +12394,8 @@ public class Notification implements Parcelable R.attr.textColorSecondary, R.attr.colorAccent, R.attr.colorAccentSecondary, + R.attr.colorAccentTertiary, + R.attr.textColorOnAccent, R.attr.colorError, R.attr.colorControlHighlight }; @@ -12399,8 +12406,10 @@ public class Notification implements Parcelable mSecondaryTextColor = getColor(ta, 3, COLOR_INVALID); mPrimaryAccentColor = getColor(ta, 4, COLOR_INVALID); mSecondaryAccentColor = getColor(ta, 5, COLOR_INVALID); - mErrorColor = getColor(ta, 6, COLOR_INVALID); - mRippleAlpha = Color.alpha(getColor(ta, 7, 0x33ffffff)); + mTertiaryAccentColor = getColor(ta, 6, COLOR_INVALID); + mOnAccentTextColor = getColor(ta, 7, COLOR_INVALID); + mErrorColor = getColor(ta, 8, COLOR_INVALID); + mRippleAlpha = Color.alpha(getColor(ta, 9, 0x33ffffff)); } mContrastColor = calculateContrastColor(ctx, rawColor, mPrimaryAccentColor, mBackgroundColor, nightMode); @@ -12420,6 +12429,14 @@ public class Notification implements Parcelable if (mSecondaryAccentColor == COLOR_INVALID) { mSecondaryAccentColor = mContrastColor; } + if (mTertiaryAccentColor == COLOR_INVALID) { + mTertiaryAccentColor = mContrastColor; + } + if (mOnAccentTextColor == COLOR_INVALID) { + mOnAccentTextColor = ColorUtils.setAlphaComponent( + ContrastColorUtil.resolvePrimaryColor( + ctx, mTertiaryAccentColor, nightMode), 0xFF); + } if (mErrorColor == COLOR_INVALID) { mErrorColor = mPrimaryTextColor; } @@ -12485,6 +12502,16 @@ public class Notification implements Parcelable return mSecondaryAccentColor; } + /** @return the theme's tertiary accent color for colored UI elements. */ + public @ColorInt int getTertiaryAccentColor() { + return mTertiaryAccentColor; + } + + /** @return the theme's text color to be used on the tertiary accent color. */ + public @ColorInt int getOnAccentTextColor() { + return mOnAccentTextColor; + } + /** * @return the contrast-adjusted version of the color provided by the app, or the * primary text color when colorized. diff --git a/core/java/android/app/backup/BackupManager.java b/core/java/android/app/backup/BackupManager.java index dae565e12fd7..67f631f98f0b 100644 --- a/core/java/android/app/backup/BackupManager.java +++ b/core/java/android/app/backup/BackupManager.java @@ -281,6 +281,32 @@ public class BackupManager { } /** + * Convenience method for callers who need to indicate that some other package or + * some other user needs a backup pass. This can be useful in the case of groups of + * packages that share a uid and/or have user-specific data. + * <p> + * This method requires that the application hold the "android.permission.BACKUP" + * permission if the package named in the package argument does not run under the + * same uid as the caller. This method also requires that the application hold the + * "android.permission.INTERACT_ACROSS_USERS_FULL" if the user argument is not the + * same as the user the caller is running under. + * @param userId The user to back up + * @param packageName The package name identifying the application to back up. + * + * @hide + */ + public static void dataChangedForUser(int userId, String packageName) { + checkServiceBinder(); + if (sService != null) { + try { + sService.dataChangedForUser(userId, packageName); + } catch (RemoteException e) { + Log.e(TAG, "dataChanged(userId,pkg) couldn't connect"); + } + } + } + + /** * @deprecated Applications shouldn't request a restore operation using this method. In Android * P and later, this method is a no-op. * diff --git a/core/java/android/app/servertransaction/ActivityTransactionItem.java b/core/java/android/app/servertransaction/ActivityTransactionItem.java index a539812fa8c6..186f25deab67 100644 --- a/core/java/android/app/servertransaction/ActivityTransactionItem.java +++ b/core/java/android/app/servertransaction/ActivityTransactionItem.java @@ -59,36 +59,37 @@ public abstract class ActivityTransactionItem extends ClientTransactionItem { } /** - * Get the {@link ActivityClientRecord} instance that corresponds to the provided token. + * Gets the {@link ActivityClientRecord} instance that corresponds to the provided token. * @param client Target client handler. * @param token Target activity token. - * @param includeLaunching Indicate to also find the {@link ActivityClientRecord} in launching - * activity list. It should be noted that there is no activity in + * @param includeLaunching Indicate to find the {@link ActivityClientRecord} in launching + * activity list. + * <p>Note that there is no {@link android.app.Activity} instance in * {@link ActivityClientRecord} from the launching activity list. * @return The {@link ActivityClientRecord} instance that corresponds to the provided token. */ @NonNull ActivityClientRecord getActivityClientRecord( @NonNull ClientTransactionHandler client, IBinder token, boolean includeLaunching) { - ActivityClientRecord r = client.getActivityClient(token); - if (r != null) { - if (client.getActivity(token) == null) { + ActivityClientRecord r = null; + // Check launching Activity first to prevent race condition that activity instance has not + // yet set to ActivityClientRecord. + if (includeLaunching) { + r = client.getLaunchingActivity(token); + } + // Then if we don't want to find launching Activity or the ActivityClientRecord doesn't + // exist in launching Activity list. The ActivityClientRecord should have been initialized + // and put in the Activity list. + if (r == null) { + r = client.getActivityClient(token); + if (r != null && client.getActivity(token) == null) { throw new IllegalArgumentException("Activity must not be null to execute " + "transaction item"); } - return r; - } - // The activity may not be launched yet. Fallback to check launching activity. - if (includeLaunching) { - r = client.getLaunchingActivity(token); } if (r == null) { throw new IllegalArgumentException("Activity client record must not be null to execute " + "transaction item"); } - - // We don't need to check the activity of launching activity client records because they - // have not been launched yet. - return r; } } diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java index 8398be1af49a..5094498dbee5 100644 --- a/core/java/android/bluetooth/BluetoothAdapter.java +++ b/core/java/android/bluetooth/BluetoothAdapter.java @@ -64,6 +64,8 @@ import android.os.SystemProperties; import android.util.Log; import android.util.Pair; +import com.android.internal.annotations.GuardedBy; + import java.io.IOException; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -78,6 +80,7 @@ import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.UUID; +import java.util.WeakHashMap; import java.util.concurrent.Executor; import java.util.concurrent.TimeoutException; import java.util.concurrent.locks.ReentrantReadWriteLock; @@ -715,10 +718,21 @@ public final class BluetoothAdapter { private final IBluetoothManager mManagerService; private final AttributionSource mAttributionSource; + // Yeah, keeping both mService and sService isn't pretty, but it's too late + // in the current release for a major refactoring, so we leave them both + // intact until this can be cleaned up in a future release + @UnsupportedAppUsage + @GuardedBy("mServiceLock") private IBluetooth mService; private final ReentrantReadWriteLock mServiceLock = new ReentrantReadWriteLock(); + @GuardedBy("sServiceLock") + private static boolean sServiceRegistered; + @GuardedBy("sServiceLock") + private static IBluetooth sService; + private static final Object sServiceLock = new Object(); + private final Object mLock = new Object(); private final Map<LeScanCallback, ScanCallback> mLeScanClients; private final Map<BluetoothDevice, List<Pair<OnMetadataChangedListener, Executor>>> @@ -792,19 +806,11 @@ public final class BluetoothAdapter { * Use {@link #getDefaultAdapter} to get the BluetoothAdapter instance. */ BluetoothAdapter(IBluetoothManager managerService, AttributionSource attributionSource) { - if (managerService == null) { - throw new IllegalArgumentException("bluetooth manager service is null"); - } - try { - mServiceLock.writeLock().lock(); - mService = managerService.registerAdapter(mManagerCallback); - } catch (RemoteException e) { - Log.e(TAG, "", e); - } finally { - mServiceLock.writeLock().unlock(); - } mManagerService = Objects.requireNonNull(managerService); mAttributionSource = Objects.requireNonNull(attributionSource); + synchronized (mServiceLock.writeLock()) { + mService = getBluetoothService(mManagerCallback); + } mLeScanClients = new HashMap<LeScanCallback, ScanCallback>(); mToken = new Binder(DESCRIPTOR); } @@ -3154,21 +3160,16 @@ public final class BluetoothAdapter { } } - @SuppressLint("AndroidFrameworkBluetoothPermission") - private final IBluetoothManagerCallback mManagerCallback = + private static final IBluetoothManagerCallback sManagerCallback = new IBluetoothManagerCallback.Stub() { - @SuppressLint("AndroidFrameworkRequiresPermission") public void onBluetoothServiceUp(IBluetooth bluetoothService) { if (DBG) { Log.d(TAG, "onBluetoothServiceUp: " + bluetoothService); } - mServiceLock.writeLock().lock(); - mService = bluetoothService; - mServiceLock.writeLock().unlock(); - - synchronized (mProxyServiceStateCallbacks) { - for (IBluetoothManagerCallback cb : mProxyServiceStateCallbacks) { + synchronized (sServiceLock) { + sService = bluetoothService; + for (IBluetoothManagerCallback cb : sProxyServiceStateCallbacks.keySet()) { try { if (cb != null) { cb.onBluetoothServiceUp(bluetoothService); @@ -3180,6 +3181,56 @@ public final class BluetoothAdapter { } } } + } + + public void onBluetoothServiceDown() { + if (DBG) { + Log.d(TAG, "onBluetoothServiceDown"); + } + + synchronized (sServiceLock) { + sService = null; + for (IBluetoothManagerCallback cb : sProxyServiceStateCallbacks.keySet()) { + try { + if (cb != null) { + cb.onBluetoothServiceDown(); + } else { + Log.d(TAG, "onBluetoothServiceDown: cb is null!"); + } + } catch (Exception e) { + Log.e(TAG, "", e); + } + } + } + } + + public void onBrEdrDown() { + if (VDBG) { + Log.i(TAG, "onBrEdrDown"); + } + + synchronized (sServiceLock) { + for (IBluetoothManagerCallback cb : sProxyServiceStateCallbacks.keySet()) { + try { + if (cb != null) { + cb.onBrEdrDown(); + } else { + Log.d(TAG, "onBrEdrDown: cb is null!"); + } + } catch (Exception e) { + Log.e(TAG, "", e); + } + } + } + } + }; + + private final IBluetoothManagerCallback mManagerCallback = + new IBluetoothManagerCallback.Stub() { + public void onBluetoothServiceUp(IBluetooth bluetoothService) { + synchronized (mServiceLock.writeLock()) { + mService = bluetoothService; + } synchronized (mMetadataListeners) { mMetadataListeners.forEach((device, pair) -> { try { @@ -3204,12 +3255,7 @@ public final class BluetoothAdapter { } public void onBluetoothServiceDown() { - if (DBG) { - Log.d(TAG, "onBluetoothServiceDown: " + mService); - } - - try { - mServiceLock.writeLock().lock(); + synchronized (mServiceLock.writeLock()) { mService = null; if (mLeScanClients != null) { mLeScanClients.clear(); @@ -3220,29 +3266,10 @@ public final class BluetoothAdapter { if (mBluetoothLeScanner != null) { mBluetoothLeScanner.cleanup(); } - } finally { - mServiceLock.writeLock().unlock(); - } - - synchronized (mProxyServiceStateCallbacks) { - for (IBluetoothManagerCallback cb : mProxyServiceStateCallbacks) { - try { - if (cb != null) { - cb.onBluetoothServiceDown(); - } else { - Log.d(TAG, "onBluetoothServiceDown: cb is null!"); - } - } catch (Exception e) { - Log.e(TAG, "", e); - } - } } } public void onBrEdrDown() { - if (VDBG) { - Log.i(TAG, "onBrEdrDown: " + mService); - } } }; @@ -3477,15 +3504,12 @@ public final class BluetoothAdapter { protected void finalize() throws Throwable { try { - mManagerService.unregisterAdapter(mManagerCallback); - } catch (RemoteException e) { - Log.e(TAG, "", e); + removeServiceStateCallback(mManagerCallback); } finally { super.finalize(); } } - /** * Validate a String Bluetooth address, such as "00:43:A8:23:10:F0" * <p>Alphabetic characters must be uppercase to be valid. @@ -3549,24 +3573,64 @@ public final class BluetoothAdapter { return mAttributionSource; } - private final ArrayList<IBluetoothManagerCallback> mProxyServiceStateCallbacks = - new ArrayList<IBluetoothManagerCallback>(); + @GuardedBy("sServiceLock") + private static final WeakHashMap<IBluetoothManagerCallback, Void> sProxyServiceStateCallbacks = + new WeakHashMap<>(); + + /*package*/ IBluetooth getBluetoothService() { + synchronized (sServiceLock) { + if (sProxyServiceStateCallbacks.isEmpty()) { + throw new IllegalStateException( + "Anonymous service access requires at least one lifecycle in process"); + } + return sService; + } + } @UnsupportedAppUsage /*package*/ IBluetooth getBluetoothService(IBluetoothManagerCallback cb) { - synchronized (mProxyServiceStateCallbacks) { - if (cb == null) { - Log.w(TAG, "getBluetoothService() called with no BluetoothManagerCallback"); - } else if (!mProxyServiceStateCallbacks.contains(cb)) { - mProxyServiceStateCallbacks.add(cb); - } + Objects.requireNonNull(cb); + synchronized (sServiceLock) { + sProxyServiceStateCallbacks.put(cb, null); + registerOrUnregisterAdapterLocked(); + return sService; } - return mService; } /*package*/ void removeServiceStateCallback(IBluetoothManagerCallback cb) { - synchronized (mProxyServiceStateCallbacks) { - mProxyServiceStateCallbacks.remove(cb); + Objects.requireNonNull(cb); + synchronized (sServiceLock) { + sProxyServiceStateCallbacks.remove(cb); + registerOrUnregisterAdapterLocked(); + } + } + + /** + * Handle registering (or unregistering) a single process-wide + * {@link IBluetoothManagerCallback} based on the presence of local + * {@link #sProxyServiceStateCallbacks} clients. + */ + @GuardedBy("sServiceLock") + private void registerOrUnregisterAdapterLocked() { + final boolean isRegistered = sServiceRegistered; + final boolean wantRegistered = !sProxyServiceStateCallbacks.isEmpty(); + + if (isRegistered != wantRegistered) { + if (wantRegistered) { + try { + sService = mManagerService.registerAdapter(sManagerCallback); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } else { + try { + mManagerService.unregisterAdapter(sManagerCallback); + sService = null; + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + sServiceRegistered = wantRegistered; } } diff --git a/core/java/android/bluetooth/BluetoothSocket.java b/core/java/android/bluetooth/BluetoothSocket.java index bb409d5360f9..1655b62bbfec 100644 --- a/core/java/android/bluetooth/BluetoothSocket.java +++ b/core/java/android/bluetooth/BluetoothSocket.java @@ -399,7 +399,7 @@ public final class BluetoothSocket implements Closeable { try { if (mSocketState == SocketState.CLOSED) throw new IOException("socket closed"); IBluetooth bluetoothProxy = - BluetoothAdapter.getDefaultAdapter().getBluetoothService(null); + BluetoothAdapter.getDefaultAdapter().getBluetoothService(); if (bluetoothProxy == null) throw new IOException("Bluetooth is off"); mPfd = bluetoothProxy.getSocketManager().connectSocket(mDevice, mType, mUuid, mPort, getSecurityFlags()); @@ -438,7 +438,7 @@ public final class BluetoothSocket implements Closeable { /*package*/ int bindListen() { int ret; if (mSocketState == SocketState.CLOSED) return EBADFD; - IBluetooth bluetoothProxy = BluetoothAdapter.getDefaultAdapter().getBluetoothService(null); + IBluetooth bluetoothProxy = BluetoothAdapter.getDefaultAdapter().getBluetoothService(); if (bluetoothProxy == null) { Log.e(TAG, "bindListen fail, reason: bluetooth is off"); return -1; @@ -706,7 +706,7 @@ public final class BluetoothSocket implements Closeable { throw new IOException("socket closed"); } IBluetooth bluetoothProxy = - BluetoothAdapter.getDefaultAdapter().getBluetoothService(null); + BluetoothAdapter.getDefaultAdapter().getBluetoothService(); if (bluetoothProxy == null) { throw new IOException("Bluetooth is off"); } diff --git a/core/java/android/content/ClipDescription.java b/core/java/android/content/ClipDescription.java index f49362e9300e..2ecd71bc1f06 100644 --- a/core/java/android/content/ClipDescription.java +++ b/core/java/android/content/ClipDescription.java @@ -124,6 +124,16 @@ public class ClipDescription implements Parcelable { */ public static final String EXTRA_ACTIVITY_OPTIONS = "android.intent.extra.ACTIVITY_OPTIONS"; + /** + * An instance id used for logging. + * <p> + * Type: {@link com.android.internal.logging.InstanceId} + * </p> + * @hide + */ + public static final String EXTRA_LOGGING_INSTANCE_ID = + "android.intent.extra.LOGGING_INSTANCE_ID"; + /** @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef(value = diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index c02dcfd3d681..27027721109d 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -4464,11 +4464,11 @@ public abstract class Context { /** * Use with {@link #getSystemService(String)} to retrieve a {@link - * android.media.MediaTranscodeManager} for transcoding media. + * android.media.MediaTranscodingManager} for transcoding media. * * @hide * @see #getSystemService(String) - * @see android.media.MediaTranscodeManager + * @see android.media.MediaTranscodingManager */ @SystemApi public static final String MEDIA_TRANSCODING_SERVICE = "media_transcoding"; diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java index af48b71f9962..3c1ec3e629a9 100644 --- a/core/java/android/hardware/camera2/CameraDevice.java +++ b/core/java/android/hardware/camera2/CameraDevice.java @@ -807,6 +807,23 @@ public abstract class CameraDevice implements AutoCloseable { * The same logic applies to other hardware levels and capabilities. * </p> * + * <p> Devices with the ULTRA_HIGH_RESOLUTION_SENSOR capability have some additional guarantees + * which clients can take advantage of : </p> + * <table> + * <tr><th colspan="10">Additional guaranteed combinations for ULTRA_HIGH_RESOLUTION sensors</th></tr> + * <tr> <th colspan="3" id="rb">Target 1</th> <th colspan="3" id="rb">Target 2</th> <th colspan="3" id="rb">Target 3</th> <th rowspan="2">Sample use case(s)</th> </tr> + * <tr> <th>Type</th><th id="rb"> SC Map</th><th id="rb">Max size</th> <th>Type</th><th id="rb"> SC Map</th><th id="rb">Max size</th> <th>Type</th><th id="rb"> SC Map</th><th id="rb">Max size</th></tr> + * <tr> <td>{@code YUV / JPEG / RAW}</td><td id="rb">{@code MAX_RES}</td><td id="rb">{@code MAX}</td><td id="rb">{@code PRIV / YUV}</td><td id="rb">{@code DEFAULT}</td><td id="rb">{@code PREVIEW}</td><td colspan="3" id="rb"></td> <td>Ultra high res still image capture with preview</td> </tr> + * <tr> <td>{@code YUV / JPEG / RAW}</td><td id="rb">{@code MAX_RES}</td><td id="rb">{@code MAX}</td><td id="rb">{@code PRIV}</td><td id="rb">{@code DEFAULT}</td><td id="rb">{@code PREVIEW}</td><td id="rb">{@code PRIV / YUV}</td><td id="rb">{@code DEFAULT}</td><td id="rb">{@code RECORD}</td> <td>Ultra high res still capture with preview + app based RECORD size analysis</td> </tr> + * <tr> <td>{@code YUV / JPEG / RAW}</td><td id="rb">{@code MAX_RES}</td><td id="rb">{@code MAX}</td><td id="rb">{@code PRIV}</td><td id="rb">{@code DEFAULT}</td><td id="rb">{@code PREVIEW}</td><td id="rb">{@code JPEG / YUV / RAW}</td><td id="rb">{@code DEFAULT}</td><td id="rb">{@code MAX}</td> <td>Ultra high res still image capture with preview + default sensor pixel mode analysis stream</td> </tr> + * </table><br> + * + * <p> Here, SC Map, refers to the {@link StreamConfigurationMap}, the target stream sizes must + * be chosen from. {@code DEFAULT} refers to the default sensor pixel mode {@link + * StreamConfigurationMap} and {@code MAX_RES} refers to the maximum resolution {@link + * StreamConfigurationMap}. The same capture request must not mix targets from + * {@link StreamConfigurationMap}s corresponding to different sensor pixel modes. + * * <p>Since the capabilities of camera devices vary greatly, a given camera device may support * target combinations with sizes outside of these guarantees, but this can only be tested for * by calling {@link #isSessionConfigurationSupported} or attempting to create a session with @@ -989,6 +1006,16 @@ public abstract class CameraDevice implements AutoCloseable { * <tr> <td>{@code PRIV}</td><td id="rb">{@code MULTI_RES}</td> <td>{@code PRIV}</td><td id="rb">{@code MULTI_RES}</td> <td>{@code YUV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV}</td><td id="rb">{@code MULTI_RES}</td> <td></td><td id="rb"></td> <td>Maximum-resolution two-input ZSL in-app processing.</td> </tr> * <tr> <td>{@code PRIV}/{@code YUV}</td><td id="rb">{@code MULTI_RES}</td> <td>Same as input</td><td id="rb">{@code MULTI_RES}</td> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code JPEG}</td><td id="rb">{@code MULTI_RES}</td> <td>ZSL still capture and in-app processing.</td> </tr> * </table><br> + * <p> Devices with the ULTRA_HIGH_RESOLUTION_SENSOR capability have some additional guarantees + * which clients can take advantage of : </p> + * <table> + * <tr><th colspan="13">Additional guaranteed combinations for ULTRA_HIGH_RESOLUTION sensors (YUV / PRIV inputs are guaranteed only if YUV / PRIVATE reprocessing are supported)</th></tr> + * <tr> <th colspan="3" id="rb">Input</th> <th colspan="3" id="rb">Target 1</th> <th colspan="3" id="rb">Target 2</th> <th colspan="3" id="rb">Target 3</th> <th rowspan="2">Sample use case(s)</th> </tr> + * <tr> <th>Type</th><th id="rb"> SC Map</th><th id="rb">Max size</th><th>Type</th><th id="rb"> SC Map</th><th id="rb">Max size</th> <th>Type</th><th id="rb"> SC Map</th><th id="rb">Max size</th> <th>Type</th><th id="rb"> SC Map</th><th id="rb">Max size</th></tr> + * <tr> <td>{@code RAW}</td><td id="rb">{@code MAX_RES}</td><td id="rb">{@code MAX}</td><td>{@code RAW}</td><td id="rb">{@code MAX_RES}</td><td id="rb">{@code MAX}</td><td id="rb">{@code PRIV / YUV}</td><td id="rb">{@code DEFAULT}</td><td id="rb">{@code PREVIEW}</td><td colspan="3" id="rb"></td> <td>RAW remosaic reprocessing with seperate preview</td> </tr> + * <tr> <td>{@code RAW}</td><td id="rb">{@code MAX_RES}</td><td id="rb">{@code MAX}</td><td>{@code RAW}</td><td id="rb">{@code MAX_RES}</td><td id="rb">{@code MAX}</td><td id="rb">{@code PRIV / YUV}</td><td id="rb">{@code DEFAULT}</td><td id="rb">{@code PREVIEW}</td><td id="rb">{@code JPEG / YUV}</td><td id="rb">{@code MAX_RES}</td><td id="rb">{@code MAX}</td> <td>Ultra high res RAW -> JPEG / YUV with seperate preview</td> </tr> + * <tr> <td>{@code YUV / PRIV}</td><td id="rb">{@code MAX_RES}</td><td id="rb">{@code MAX}</td> <td>{@code YUV / PRIV}</td><td id="rb">{@code MAX_RES}</td><td id="rb">{@code MAX}</td><td id="rb">{@code YUV / PRIV}</td><td id="rb">{@code DEFAULT}</td><td id="rb">{@code PREVIEW}</td><td id="rb">{@code JPEG }</td><td id="rb">{@code MAX_RES}</td><td id="rb">{@code MAX}</td> <td> Ultra high res PRIV / YUV -> YUV / JPEG reprocessing with seperate preview</td> </tr> + * </table><br> * No additional mandatory stream combinations for RAW capability and LEVEL-3 hardware level. * </p> * diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java index d9fa56e1cbaa..4b35294826db 100644 --- a/core/java/android/hardware/camera2/CameraMetadata.java +++ b/core/java/android/hardware/camera2/CameraMetadata.java @@ -1138,10 +1138,14 @@ public abstract class CameraMetadata<TKey> { * <code>{@link CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP android.scaler.streamConfigurationMap}</code> describes the streams supported in 'default' * mode. * The stream configurations supported in 'max resolution' mode are described by - * <code>{@link CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP_MAXIMUM_RESOLUTION android.scaler.streamConfigurationMapMaximumResolution}</code>.</p> + * <code>{@link CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP_MAXIMUM_RESOLUTION android.scaler.streamConfigurationMapMaximumResolution}</code>. + * The maximum resolution mode pixel array size of a camera device + * (<code>{@link CameraCharacteristics#SENSOR_INFO_PIXEL_ARRAY_SIZE android.sensor.info.pixelArraySize}</code>) with this capability, + * will be at least 24 megapixels.</p> * * @see CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP * @see CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP_MAXIMUM_RESOLUTION + * @see CameraCharacteristics#SENSOR_INFO_PIXEL_ARRAY_SIZE * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES */ public static final int REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR = 16; diff --git a/core/java/android/hardware/camera2/extension/IRequestProcessorImpl.aidl b/core/java/android/hardware/camera2/extension/IRequestProcessorImpl.aidl index 52595a8c5a2d..88b03a472c41 100644 --- a/core/java/android/hardware/camera2/extension/IRequestProcessorImpl.aidl +++ b/core/java/android/hardware/camera2/extension/IRequestProcessorImpl.aidl @@ -24,9 +24,9 @@ import android.hardware.camera2.extension.Request; interface IRequestProcessorImpl { void setImageProcessor(in OutputConfigId outputConfigId, in IImageProcessorImpl imageProcessor); - boolean submit(in Request request, in IRequestCallback callback); - boolean submitBurst(in List<Request> requests, in IRequestCallback callback); - boolean setRepeating(in Request request, in IRequestCallback callback); + int submit(in Request request, in IRequestCallback callback); + int submitBurst(in List<Request> requests, in IRequestCallback callback); + int setRepeating(in Request request, in IRequestCallback callback); void abortCaptures(); void stopRepeating(); } diff --git a/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java index 02245e49e611..8da6551f3d15 100644 --- a/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java @@ -864,14 +864,15 @@ public final class CameraAdvancedExtensionSessionImpl extends CameraExtensionSes } @Override - public boolean submit(Request request, IRequestCallback callback) { + public int submit(Request request, IRequestCallback callback) { ArrayList<Request> captureList = new ArrayList<>(); captureList.add(request); return submitBurst(captureList, callback); } @Override - public boolean submitBurst(List<Request> requests, IRequestCallback callback) { + public int submitBurst(List<Request> requests, IRequestCallback callback) { + int seqId = -1; synchronized (mInterfaceLock) { try { CaptureCallbackHandler captureCallback = new CaptureCallbackHandler(callback); @@ -880,37 +881,36 @@ public final class CameraAdvancedExtensionSessionImpl extends CameraExtensionSes captureRequests.add(initializeCaptureRequest(mCameraDevice, request, mCameraConfigMap)); } - mCaptureSession.captureBurstRequests(captureRequests, + seqId = mCaptureSession.captureBurstRequests(captureRequests, new CameraExtensionUtils.HandlerExecutor(mHandler), captureCallback); } catch (CameraAccessException e) { Log.e(TAG, "Failed to submit capture requests!"); - return false; } catch (IllegalStateException e) { Log.e(TAG, "Capture session closed!"); } } - return true; + return seqId; } @Override - public boolean setRepeating(Request request, IRequestCallback callback) { + public int setRepeating(Request request, IRequestCallback callback) { + int seqId = -1; synchronized (mInterfaceLock) { try { CaptureRequest repeatingRequest = initializeCaptureRequest(mCameraDevice, request, mCameraConfigMap); CaptureCallbackHandler captureCallback = new CaptureCallbackHandler(callback); - mCaptureSession.setSingleRepeatingRequest(repeatingRequest, + seqId = mCaptureSession.setSingleRepeatingRequest(repeatingRequest, new CameraExtensionUtils.HandlerExecutor(mHandler), captureCallback); } catch (CameraAccessException e) { Log.e(TAG, "Failed to enable repeating request!"); - return false; } catch (IllegalStateException e) { Log.e(TAG, "Capture session closed!"); } } - return true; + return seqId; } @Override diff --git a/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java index ecd24914c566..71047af69b87 100644 --- a/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java @@ -251,6 +251,7 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession { CameraExtensionCharacteristics.PROCESSING_INPUT_FORMAT); mPreviewImageProcessor.onResolutionUpdate(new Size(repeatingSurfaceInfo.mWidth, repeatingSurfaceInfo.mHeight)); + mPreviewImageProcessor.onOutputSurface(null, -1); mRepeatingRequestImageReader = ImageReader.newInstance(repeatingSurfaceInfo.mWidth, repeatingSurfaceInfo.mHeight, CameraExtensionCharacteristics.PROCESSING_INPUT_FORMAT, PREVIEW_QUEUE_SIZE, diff --git a/core/java/android/hardware/devicestate/DeviceStateManager.java b/core/java/android/hardware/devicestate/DeviceStateManager.java index 52dad3efefb8..95892aa7af81 100644 --- a/core/java/android/hardware/devicestate/DeviceStateManager.java +++ b/core/java/android/hardware/devicestate/DeviceStateManager.java @@ -75,23 +75,24 @@ public final class DeviceStateManager { /** * Submits a {@link DeviceStateRequest request} to modify the device state. * <p> - * By default, the request is kept active until a call to - * {@link #cancelRequest(DeviceStateRequest)} or until one of the following occurs: + * By default, the request is kept active until one of the following occurs: * <ul> + * <li>The system deems the request can no longer be honored, for example if the requested + * state becomes unsupported. + * <li>A call to {@link #cancelRequest(DeviceStateRequest)}. * <li>Another processes submits a request succeeding this request in which case the request * will be suspended until the interrupting request is canceled. - * <li>The requested state has become unsupported. - * <li>The process submitting the request dies. * </ul> * However, this behavior can be changed by setting flags on the {@link DeviceStateRequest}. * * @throws IllegalArgumentException if the requested state is unsupported. - * @throws SecurityException if the {@link android.Manifest.permission#CONTROL_DEVICE_STATE} - * permission is not held. + * @throws SecurityException if the caller is neither the current top-focused activity nor if + * the {@link android.Manifest.permission#CONTROL_DEVICE_STATE} permission is held. * * @see DeviceStateRequest */ - @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_STATE) + @RequiresPermission(value = android.Manifest.permission.CONTROL_DEVICE_STATE, + conditional = true) public void requestState(@NonNull DeviceStateRequest request, @Nullable @CallbackExecutor Executor executor, @Nullable DeviceStateRequest.Callback callback) { @@ -105,10 +106,11 @@ public final class DeviceStateManager { * This method is noop if the {@code request} has not been submitted with a call to * {@link #requestState(DeviceStateRequest, Executor, DeviceStateRequest.Callback)}. * - * @throws SecurityException if the {@link android.Manifest.permission#CONTROL_DEVICE_STATE} - * permission is not held. + * @throws SecurityException if the caller is neither the current top-focused activity nor if + * the {@link android.Manifest.permission#CONTROL_DEVICE_STATE} permission is held. */ - @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_STATE) + @RequiresPermission(value = android.Manifest.permission.CONTROL_DEVICE_STATE, + conditional = true) public void cancelRequest(@NonNull DeviceStateRequest request) { mGlobal.cancelRequest(request); } diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java index e13a7b6eac65..5034ef1dfec5 100644 --- a/core/java/android/hardware/display/DisplayManager.java +++ b/core/java/android/hardware/display/DisplayManager.java @@ -940,6 +940,34 @@ public final class DisplayManager { } /** + * Sets the brightness configuration for the specified display. + * If the specified display doesn't exist, then this will return and do nothing. + * + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS) + public void setBrightnessConfigurationForDisplay(@NonNull BrightnessConfiguration c, + @NonNull String uniqueId) { + mGlobal.setBrightnessConfigurationForDisplay(c, uniqueId, mContext.getUserId(), + mContext.getPackageName()); + } + + /** + * Gets the brightness configuration for the specified display and default user. + * Returns the default configuration if unset or display is invalid. + * + * @hide + */ + @Nullable + @SystemApi + @RequiresPermission(Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS) + public BrightnessConfiguration getBrightnessConfigurationForDisplay( + @NonNull String uniqueId) { + return mGlobal.getBrightnessConfigurationForDisplay(uniqueId, mContext.getUserId()); + } + + /** * Sets the global display brightness configuration for a specific user. * * Note this requires the INTERACT_ACROSS_USERS permission if setting the configuration for a diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java index a9b95fce8777..3e15c0ee6ebd 100644 --- a/core/java/android/hardware/display/DisplayManagerGlobal.java +++ b/core/java/android/hardware/display/DisplayManagerGlobal.java @@ -705,6 +705,34 @@ public final class DisplayManagerGlobal { } /** + * Sets the brightness configuration for a given display. + * + * @hide + */ + public void setBrightnessConfigurationForDisplay(BrightnessConfiguration c, + String uniqueDisplayId, int userId, String packageName) { + try { + mDm.setBrightnessConfigurationForDisplay(c, uniqueDisplayId, userId, packageName); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); + } + } + + /** + * Gets the brightness configuration for a given display or null if one hasn't been set. + * + * @hide + */ + public BrightnessConfiguration getBrightnessConfigurationForDisplay(String uniqueDisplayId, + int userId) { + try { + return mDm.getBrightnessConfigurationForDisplay(uniqueDisplayId, userId); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); + } + } + + /** * Gets the global brightness configuration for a given user or null if one hasn't been set. * * @hide diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl index 2303353ad101..116214625e26 100644 --- a/core/java/android/hardware/display/IDisplayManager.aidl +++ b/core/java/android/hardware/display/IDisplayManager.aidl @@ -118,6 +118,16 @@ interface IDisplayManager { void setBrightnessConfigurationForUser(in BrightnessConfiguration c, int userId, String packageName); + // Sets the global brightness configuration for a given display. Requires + // CONFIGURE_DISPLAY_BRIGHTNESS. + void setBrightnessConfigurationForDisplay(in BrightnessConfiguration c, String uniqueDisplayId, + int userId, String packageName); + + // Gets the brightness configuration for a given display. Requires + // CONFIGURE_DISPLAY_BRIGHTNESS. + BrightnessConfiguration getBrightnessConfigurationForDisplay(String uniqueDisplayId, + int userId); + // Gets the global brightness configuration for a given user. Requires // CONFIGURE_DISPLAY_BRIGHTNESS, and INTERACT_ACROSS_USER if the user is not // the same as the calling user. diff --git a/core/java/android/hardware/face/FaceManager.java b/core/java/android/hardware/face/FaceManager.java index 3c3ba595f3fb..385ad2d3577f 100644 --- a/core/java/android/hardware/face/FaceManager.java +++ b/core/java/android/hardware/face/FaceManager.java @@ -790,6 +790,11 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan } } } + + // This is used as a last resort in case a vendor string is missing + // It should not happen for anything other than FACE_ERROR_VENDOR, but + // warn and use the default if all else fails. + // TODO(b/196639965): update string Slog.w(TAG, "Invalid error message: " + errMsg + ", " + vendorCode); return ""; } diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java index dc1a50fa6616..87d45b9de745 100644 --- a/core/java/android/hardware/fingerprint/FingerprintManager.java +++ b/core/java/android/hardware/fingerprint/FingerprintManager.java @@ -1386,8 +1386,13 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing } } } + + // This is used as a last resort in case a vendor string is missing + // It should not happen for anything other than FINGERPRINT_ERROR_VENDOR, but + // warn and use the default if all else fails. + // TODO(b/196639965): update string Slog.w(TAG, "Invalid error message: " + errMsg + ", " + vendorCode); - return null; + return ""; } /** diff --git a/core/java/android/inputmethodservice/AbstractInputMethodService.java b/core/java/android/inputmethodservice/AbstractInputMethodService.java index 17d4ae6205ca..f8f0970ecfe4 100644 --- a/core/java/android/inputmethodservice/AbstractInputMethodService.java +++ b/core/java/android/inputmethodservice/AbstractInputMethodService.java @@ -309,7 +309,7 @@ public abstract class AbstractInputMethodService extends WindowProviderService @Override @Nullable public final Bundle getWindowContextOptions() { - return null; + return super.getWindowContextOptions(); } /** @hide */ diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index dc4a74766ed9..94d19bc1c869 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -14981,6 +14981,16 @@ public final class Settings { "power_button_long_press"; /** + * Override internal R.integer.config_longPressOnPowerDurationMs. It determines the length + * of power button press to be considered a long press in milliseconds. + * Used by PhoneWindowManager. + * @hide + */ + @Readable + public static final String POWER_BUTTON_LONG_PRESS_DURATION_MS = + "power_button_long_press_duration_ms"; + + /** * Overrides internal R.integer.config_veryLongPressOnPowerBehavior. * Allowable values detailed in frameworks/base/core/res/res/values/config.xml. * Used by PhoneWindowManager. diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java index 6c3c38359957..3d39fbeea7d8 100644 --- a/core/java/android/util/FeatureFlagUtils.java +++ b/core/java/android/util/FeatureFlagUtils.java @@ -51,6 +51,9 @@ public class FeatureFlagUtils { /** @hide */ public static final String SETTINGS_ENABLE_SECURITY_HUB = "settings_enable_security_hub"; + /** @hide */ + public static final String SETTINGS_SUPPORT_LARGE_SCREEN = "settings_support_large_screen"; + private static final Map<String, String> DEFAULT_FLAGS; static { @@ -72,12 +75,14 @@ public class FeatureFlagUtils { DEFAULT_FLAGS.put(SETTINGS_PROVIDER_MODEL, "true"); DEFAULT_FLAGS.put(SETTINGS_USE_NEW_BACKUP_ELIGIBILITY_RULES, "true"); DEFAULT_FLAGS.put(SETTINGS_ENABLE_SECURITY_HUB, "true"); + DEFAULT_FLAGS.put(SETTINGS_SUPPORT_LARGE_SCREEN, "false"); } private static final Set<String> PERSISTENT_FLAGS; static { PERSISTENT_FLAGS = new HashSet<>(); PERSISTENT_FLAGS.add(SETTINGS_PROVIDER_MODEL); + PERSISTENT_FLAGS.add(SETTINGS_SUPPORT_LARGE_SCREEN); } /** diff --git a/core/java/android/view/InputWindowHandle.java b/core/java/android/view/InputWindowHandle.java index 1cb1439f4032..139bff4b0118 100644 --- a/core/java/android/view/InputWindowHandle.java +++ b/core/java/android/view/InputWindowHandle.java @@ -19,6 +19,7 @@ package android.view; import static android.view.Display.INVALID_DISPLAY; import android.annotation.Nullable; +import android.graphics.Matrix; import android.graphics.Region; import android.gui.TouchOcclusionMode; import android.os.IBinder; @@ -43,6 +44,17 @@ public final class InputWindowHandle { // channel and the server input channel will both contain this token. public IBinder token; + /** + * The {@link IWindow} handle if InputWindowHandle is associated with a window, null otherwise. + */ + @Nullable + private IBinder windowToken; + /** + * Used to cache IWindow from the windowToken so we don't need to convert every time getWindow + * is called. + */ + private IWindow window; + // The window name. public String name; @@ -122,6 +134,12 @@ public final class InputWindowHandle { */ public boolean replaceTouchableRegionWithCrop; + /** + * The transform that should be applied to the Window to get it from screen coordinates to + * window coordinates + */ + public Matrix transform; + private native void nativeDispose(); public InputWindowHandle(InputApplicationHandle inputApplicationHandle, int displayId) { @@ -136,6 +154,9 @@ public final class InputWindowHandle { .append(frameRight).append(",").append(frameBottom).append("]") .append(", touchableRegion=").append(touchableRegion) .append(", visible=").append(visible) + .append(", scaleFactor=").append(scaleFactor) + .append(", transform=").append(transform) + .append(", windowToken=").append(windowToken) .toString(); } @@ -167,4 +188,17 @@ public final class InputWindowHandle { public void setTouchableRegionCrop(@Nullable SurfaceControl bounds) { touchableRegionSurfaceControl = new WeakReference<>(bounds); } + + public void setWindowToken(IWindow iwindow) { + windowToken = iwindow.asBinder(); + window = iwindow; + } + + public IWindow getWindow() { + if (window != null) { + return window; + } + window = IWindow.Stub.asInterface(windowToken); + return window; + } } diff --git a/core/java/android/view/ScrollCaptureResponse.java b/core/java/android/view/ScrollCaptureResponse.java index 8808827b248a..758f9ab935cf 100644 --- a/core/java/android/view/ScrollCaptureResponse.java +++ b/core/java/android/view/ScrollCaptureResponse.java @@ -53,6 +53,10 @@ public class ScrollCaptureResponse implements Parcelable { @Nullable private String mWindowTitle = null; + /** The package which owns the window. */ + @Nullable + private String mPackageName = null; + /** Carries additional logging and debugging information when enabled. */ @NonNull @DataClass.PluralOf("message") @@ -77,7 +81,7 @@ public class ScrollCaptureResponse implements Parcelable { - // Code below generated by codegen v1.0.22. + // Code below generated by codegen v1.0.23. // // DO NOT MODIFY! // CHECKSTYLE:OFF Generated code @@ -97,6 +101,7 @@ public class ScrollCaptureResponse implements Parcelable { @Nullable Rect windowBounds, @Nullable Rect boundsInWindow, @Nullable String windowTitle, + @Nullable String packageName, @NonNull ArrayList<String> messages) { this.mDescription = description; com.android.internal.util.AnnotationValidations.validate( @@ -105,6 +110,7 @@ public class ScrollCaptureResponse implements Parcelable { this.mWindowBounds = windowBounds; this.mBoundsInWindow = boundsInWindow; this.mWindowTitle = windowTitle; + this.mPackageName = packageName; this.mMessages = messages; com.android.internal.util.AnnotationValidations.validate( NonNull.class, null, mMessages); @@ -153,6 +159,14 @@ public class ScrollCaptureResponse implements Parcelable { } /** + * The package name of the process the window is owned by. + */ + @DataClass.Generated.Member + public @Nullable String getPackageName() { + return mPackageName; + } + + /** * Carries additional logging and debugging information when enabled. */ @DataClass.Generated.Member @@ -172,6 +186,7 @@ public class ScrollCaptureResponse implements Parcelable { "windowBounds = " + mWindowBounds + ", " + "boundsInWindow = " + mBoundsInWindow + ", " + "windowTitle = " + mWindowTitle + ", " + + "packageName = " + mPackageName + ", " + "messages = " + mMessages + " }"; } @@ -187,12 +202,14 @@ public class ScrollCaptureResponse implements Parcelable { if (mWindowBounds != null) flg |= 0x4; if (mBoundsInWindow != null) flg |= 0x8; if (mWindowTitle != null) flg |= 0x10; + if (mPackageName != null) flg |= 0x20; dest.writeByte(flg); dest.writeString(mDescription); if (mConnection != null) dest.writeStrongInterface(mConnection); if (mWindowBounds != null) dest.writeTypedObject(mWindowBounds, flags); if (mBoundsInWindow != null) dest.writeTypedObject(mBoundsInWindow, flags); if (mWindowTitle != null) dest.writeString(mWindowTitle); + if (mPackageName != null) dest.writeString(mPackageName); dest.writeStringList(mMessages); } @@ -213,6 +230,7 @@ public class ScrollCaptureResponse implements Parcelable { Rect windowBounds = (flg & 0x4) == 0 ? null : (Rect) in.readTypedObject(Rect.CREATOR); Rect boundsInWindow = (flg & 0x8) == 0 ? null : (Rect) in.readTypedObject(Rect.CREATOR); String windowTitle = (flg & 0x10) == 0 ? null : in.readString(); + String packageName = (flg & 0x20) == 0 ? null : in.readString(); ArrayList<String> messages = new ArrayList<>(); in.readStringList(messages); @@ -223,6 +241,7 @@ public class ScrollCaptureResponse implements Parcelable { this.mWindowBounds = windowBounds; this.mBoundsInWindow = boundsInWindow; this.mWindowTitle = windowTitle; + this.mPackageName = packageName; this.mMessages = messages; com.android.internal.util.AnnotationValidations.validate( NonNull.class, null, mMessages); @@ -256,6 +275,7 @@ public class ScrollCaptureResponse implements Parcelable { private @Nullable Rect mWindowBounds; private @Nullable Rect mBoundsInWindow; private @Nullable String mWindowTitle; + private @Nullable String mPackageName; private @NonNull ArrayList<String> mMessages; private long mBuilderFieldsSet = 0L; @@ -319,12 +339,23 @@ public class ScrollCaptureResponse implements Parcelable { } /** + * The package name of the process the window is owned by. + */ + @DataClass.Generated.Member + public @NonNull Builder setPackageName(@NonNull String value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x20; + mPackageName = value; + return this; + } + + /** * Carries additional logging and debugging information when enabled. */ @DataClass.Generated.Member public @NonNull Builder setMessages(@NonNull ArrayList<String> value) { checkNotUsed(); - mBuilderFieldsSet |= 0x20; + mBuilderFieldsSet |= 0x40; mMessages = value; return this; } @@ -340,7 +371,7 @@ public class ScrollCaptureResponse implements Parcelable { /** Builds the instance. This builder should not be touched after calling this! */ public @NonNull ScrollCaptureResponse build() { checkNotUsed(); - mBuilderFieldsSet |= 0x40; // Mark builder used + mBuilderFieldsSet |= 0x80; // Mark builder used if ((mBuilderFieldsSet & 0x1) == 0) { mDescription = ""; @@ -358,6 +389,9 @@ public class ScrollCaptureResponse implements Parcelable { mWindowTitle = null; } if ((mBuilderFieldsSet & 0x20) == 0) { + mPackageName = null; + } + if ((mBuilderFieldsSet & 0x40) == 0) { mMessages = new ArrayList<>(); } ScrollCaptureResponse o = new ScrollCaptureResponse( @@ -366,12 +400,13 @@ public class ScrollCaptureResponse implements Parcelable { mWindowBounds, mBoundsInWindow, mWindowTitle, + mPackageName, mMessages); return o; } private void checkNotUsed() { - if ((mBuilderFieldsSet & 0x40) != 0) { + if ((mBuilderFieldsSet & 0x80) != 0) { throw new IllegalStateException( "This Builder should not be reused. Use a new Builder instance instead"); } @@ -379,10 +414,10 @@ public class ScrollCaptureResponse implements Parcelable { } @DataClass.Generated( - time = 1614833185795L, - codegenVersion = "1.0.22", + time = 1628630366187L, + codegenVersion = "1.0.23", sourceFile = "frameworks/base/core/java/android/view/ScrollCaptureResponse.java", - inputSignatures = "private @android.annotation.NonNull java.lang.String mDescription\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.MaySetToNull android.view.IScrollCaptureConnection mConnection\nprivate @android.annotation.Nullable android.graphics.Rect mWindowBounds\nprivate @android.annotation.Nullable android.graphics.Rect mBoundsInWindow\nprivate @android.annotation.Nullable java.lang.String mWindowTitle\nprivate @android.annotation.NonNull @com.android.internal.util.DataClass.PluralOf(\"message\") java.util.ArrayList<java.lang.String> mMessages\npublic boolean isConnected()\npublic void close()\nclass ScrollCaptureResponse extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genGetters=true)") + inputSignatures = "private @android.annotation.NonNull java.lang.String mDescription\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.MaySetToNull android.view.IScrollCaptureConnection mConnection\nprivate @android.annotation.Nullable android.graphics.Rect mWindowBounds\nprivate @android.annotation.Nullable android.graphics.Rect mBoundsInWindow\nprivate @android.annotation.Nullable java.lang.String mWindowTitle\nprivate @android.annotation.Nullable java.lang.String mPackageName\nprivate @android.annotation.NonNull @com.android.internal.util.DataClass.PluralOf(\"message\") java.util.ArrayList<java.lang.String> mMessages\npublic boolean isConnected()\npublic void close()\nclass ScrollCaptureResponse extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genGetters=true)") @Deprecated private void __metadata() {} diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 7d75d998c253..04d65c094bf3 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -9517,6 +9517,7 @@ public final class ViewRootImpl implements ViewParent, ScrollCaptureResponse.Builder response = new ScrollCaptureResponse.Builder(); response.setWindowTitle(getTitle().toString()); + response.setPackageName(mContext.getPackageName()); StringWriter writer = new StringWriter(); IndentingPrintWriter pw = new IndentingPrintWriter(writer); diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index eb410ab8c3ed..329ea4a02145 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -320,6 +320,19 @@ public interface WindowManager extends ViewManager { int TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE = 27; /** + * A window in a new task fragment is being opened. + * @hide + */ + int TRANSIT_OLD_TASK_FRAGMENT_OPEN = 28; + + /** + * A window in the top-most activity of task fragment is being closed to reveal the activity + * below. + * @hide + */ + int TRANSIT_OLD_TASK_FRAGMENT_CLOSE = 29; + + /** * @hide */ @IntDef(prefix = { "TRANSIT_OLD_" }, value = { @@ -344,7 +357,9 @@ public interface WindowManager extends ViewManager { TRANSIT_OLD_TRANSLUCENT_ACTIVITY_OPEN, TRANSIT_OLD_TRANSLUCENT_ACTIVITY_CLOSE, TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE, - TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE + TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE, + TRANSIT_OLD_TASK_FRAGMENT_OPEN, + TRANSIT_OLD_TASK_FRAGMENT_CLOSE }) @Retention(RetentionPolicy.SOURCE) @interface TransitionOldType {} @@ -381,8 +396,11 @@ public interface WindowManager extends ViewManager { int TRANSIT_CHANGE = 6; /** * The keyguard was visible and has been dismissed. + * @deprecated use {@link #TRANSIT_TO_BACK} + {@link #TRANSIT_FLAG_KEYGUARD_GOING_AWAY} for + * keyguard going away with Shell transition. * @hide */ + @Deprecated int TRANSIT_KEYGUARD_GOING_AWAY = 7; /** * A window is appearing above a locked keyguard. @@ -487,6 +505,12 @@ public interface WindowManager extends ViewManager { int TRANSIT_FLAG_IS_RECENTS = 0x80; /** + * Transition flag: Indicates that keyguard should go away with this transition. + * @hide + */ + int TRANSIT_FLAG_KEYGUARD_GOING_AWAY = 0x100; + + /** * @hide */ @IntDef(flag = true, prefix = { "TRANSIT_FLAG_" }, value = { @@ -497,7 +521,8 @@ public interface WindowManager extends ViewManager { TRANSIT_FLAG_APP_CRASHED, TRANSIT_FLAG_OPEN_BEHIND, TRANSIT_FLAG_KEYGUARD_LOCKED, - TRANSIT_FLAG_IS_RECENTS + TRANSIT_FLAG_IS_RECENTS, + TRANSIT_FLAG_KEYGUARD_GOING_AWAY }) @Retention(RetentionPolicy.SOURCE) @interface TransitionFlags {} diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java index f800991944ac..a2d3e3447354 100644 --- a/core/java/android/view/WindowManagerImpl.java +++ b/core/java/android/view/WindowManagerImpl.java @@ -19,9 +19,12 @@ package android.view; import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN; import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION; import static android.view.View.SYSTEM_UI_FLAG_VISIBLE; +import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW; import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR; import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN; +import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW; import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING; +import static android.window.WindowProviderService.isWindowProviderService; import android.annotation.CallbackExecutor; import android.annotation.NonNull; @@ -36,7 +39,9 @@ import android.graphics.Region; import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; +import android.os.StrictMode; import android.window.WindowContext; +import android.window.WindowProvider; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.os.IResultReceiver; @@ -145,6 +150,7 @@ public final class WindowManagerImpl implements WindowManager { throw new IllegalArgumentException("Params must be WindowManager.LayoutParams"); } final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params; + assertWindowContextTypeMatches(wparams.type); // Only use the default token if we don't have a parent window and a token. if (mDefaultToken != null && mParentWindow == null && wparams.token == null) { wparams.token = mDefaultToken; @@ -152,6 +158,34 @@ public final class WindowManagerImpl implements WindowManager { wparams.mWindowContextToken = mWindowContextToken; } + private void assertWindowContextTypeMatches(@LayoutParams.WindowType int windowType) { + if (!(mContext instanceof WindowProvider)) { + return; + } + // Don't need to check sub-window type because sub window should be allowed to be attached + // to the parent window. + if (windowType >= FIRST_SUB_WINDOW && windowType <= LAST_SUB_WINDOW) { + return; + } + final WindowProvider windowProvider = (WindowProvider) mContext; + if (windowProvider.getWindowType() == windowType) { + return; + } + IllegalArgumentException exception = new IllegalArgumentException("Window type mismatch." + + " Window Context's window type is " + windowProvider.getWindowType() + + ", while LayoutParams' type is set to " + windowType + "." + + " Please create another Window Context via" + + " createWindowContext(getDisplay(), " + windowType + ", null)" + + " to add window with type:" + windowType); + if (!isWindowProviderService(windowProvider.getWindowContextOptions())) { + throw exception; + } + // Throw IncorrectCorrectViolation if the Window Context is allowed to provide multiple + // window types. Usually it's because the Window Context is a WindowProviderService. + StrictMode.onIncorrectContextUsed("WindowContext's window type must" + + " match type in WindowManager.LayoutParams", exception); + } + @Override public void removeView(View view) { mGlobal.removeView(view, false); diff --git a/core/java/android/view/WindowManagerPolicyConstants.java b/core/java/android/view/WindowManagerPolicyConstants.java index bbef3e6aeefa..e634d601c1f1 100644 --- a/core/java/android/view/WindowManagerPolicyConstants.java +++ b/core/java/android/view/WindowManagerPolicyConstants.java @@ -209,4 +209,41 @@ public interface WindowManagerPolicyConstants { return Integer.toString(why); } } + + /** + * How much to multiply the policy's type layer, to reserve room + * for multiple windows of the same type and Z-ordering adjustment + * with TYPE_LAYER_OFFSET. + */ + int TYPE_LAYER_MULTIPLIER = 10000; + + /** + * Offset from TYPE_LAYER_MULTIPLIER for moving a group of windows above + * or below others in the same layer. + */ + int TYPE_LAYER_OFFSET = 1000; + + /** + * How much to increment the layer for each window, to reserve room + * for effect surfaces between them. + */ + int WINDOW_LAYER_MULTIPLIER = 5; + + /** + * Animation thumbnail is as far as possible below the window above + * the thumbnail (or in other words as far as possible above the window + * below it). + */ + int LAYER_OFFSET_THUMBNAIL = WINDOW_LAYER_MULTIPLIER - 1; + + int SPLIT_DIVIDER_LAYER = TYPE_LAYER_MULTIPLIER * 3; + int WATERMARK_LAYER = TYPE_LAYER_MULTIPLIER * 100; + int STRICT_MODE_LAYER = TYPE_LAYER_MULTIPLIER * 101; + int WINDOW_FREEZE_LAYER = TYPE_LAYER_MULTIPLIER * 200; + + /** + * Layers for screen rotation animation. We put these layers above + * WINDOW_FREEZE_LAYER so that screen freeze will cover all windows. + */ + int SCREEN_FREEZE_LAYER_BASE = WINDOW_FREEZE_LAYER + TYPE_LAYER_MULTIPLIER; } diff --git a/core/java/android/view/accessibility/AccessibilityEvent.java b/core/java/android/view/accessibility/AccessibilityEvent.java index f6d6fde6435f..52d3612b6f77 100644 --- a/core/java/android/view/accessibility/AccessibilityEvent.java +++ b/core/java/android/view/accessibility/AccessibilityEvent.java @@ -613,6 +613,36 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par public static final int CONTENT_CHANGE_TYPE_STATE_DESCRIPTION = 0x00000040; /** + * Change type for {@link #TYPE_WINDOW_CONTENT_CHANGED} event: + * A drag has started while accessibility is enabled. This is either via an + * AccessibilityAction, or via touch events. This is sent from the source that initiated the + * drag. + * + * @see AccessibilityNodeInfo.AccessibilityAction#ACTION_DRAG_START + */ + public static final int CONTENT_CHANGE_TYPE_DRAG_STARTED = 0x00000080; + + /** + * Change type for {@link #TYPE_WINDOW_CONTENT_CHANGED} event: + * A drag in with accessibility enabled has ended. This means the content has been + * successfully dropped. This is sent from the target that accepted the dragged content. + * + * @see AccessibilityNodeInfo.AccessibilityAction#ACTION_DRAG_DROP + */ + public static final int CONTENT_CHANGE_TYPE_DRAG_DROPPED = 0x00000100; + + /** + * Change type for {@link #TYPE_WINDOW_CONTENT_CHANGED} event: + * A drag in with accessibility enabled has ended. This means the content has been + * unsuccessfully dropped, the user has canceled the action via an AccessibilityAction, or + * no drop has been detected within a timeout and the drag was automatically cancelled. This is + * sent from the source that initiated the drag. + * + * @see AccessibilityNodeInfo.AccessibilityAction#ACTION_DRAG_CANCEL + */ + public static final int CONTENT_CHANGE_TYPE_DRAG_CANCELLED = 0x0000200; + + /** * Change type for {@link #TYPE_WINDOWS_CHANGED} event: * The window was added. */ diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java index 085eb81182f1..587a27074544 100644 --- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java +++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java @@ -27,6 +27,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.TestApi; import android.compat.annotation.UnsupportedAppUsage; +import android.content.ClipData; import android.graphics.Rect; import android.graphics.Region; import android.os.Build; @@ -4353,6 +4354,14 @@ public class AccessibilityNodeInfo implements Parcelable { case R.id.accessibilityActionImeEnter: return "ACTION_IME_ENTER"; default: + // TODO(197520937): Use finalized constants in switch + if (action == R.id.accessibilityActionDragStart) { + return "ACTION_DRAG"; + } else if (action == R.id.accessibilityActionDragCancel) { + return "ACTION_CANCEL_DRAG"; + } else if (action == R.id.accessibilityActionDragDrop) { + return "ACTION_DROP"; + } return "ACTION_UNKNOWN"; } } @@ -4995,6 +5004,46 @@ public class AccessibilityNodeInfo implements Parcelable { @NonNull public static final AccessibilityAction ACTION_IME_ENTER = new AccessibilityAction(R.id.accessibilityActionImeEnter); + /** + * Action to start a drag. + * <p> + * This action initiates a drag & drop within the system. The source's dragged content is + * prepared before the drag begins. In View, this action should prepare the arguments to + * {@link View#startDragAndDrop(ClipData, View.DragShadowBuilder, Object, int)} and then + * call {@link View#startDragAndDrop(ClipData, View.DragShadowBuilder, Object, int)}. The + * equivalent should be performed for other UI toolkits. + * </p> + * + * @see AccessibilityEvent#CONTENT_CHANGE_TYPE_DRAG_STARTED + */ + @NonNull public static final AccessibilityAction ACTION_DRAG_START = + new AccessibilityAction(R.id.accessibilityActionDragStart); + + /** + * Action to trigger a drop of the content being dragged. + * <p> + * This action is added to potential drop targets if the source started a drag with + * {@link #ACTION_DRAG_START}. In View, these targets are Views that accepted + * {@link android.view.DragEvent#ACTION_DRAG_STARTED} and have an + * {@link View.OnDragListener}. + * </p> + * + * @see AccessibilityEvent#CONTENT_CHANGE_TYPE_DRAG_DROPPED + */ + @NonNull public static final AccessibilityAction ACTION_DRAG_DROP = + new AccessibilityAction(R.id.accessibilityActionDragDrop); + + /** + * Action to cancel a drag. + * <p> + * This action is added to the source that started a drag with {@link #ACTION_DRAG_START}. + * </p> + * + * @see AccessibilityEvent#CONTENT_CHANGE_TYPE_DRAG_CANCELLED + */ + @NonNull public static final AccessibilityAction ACTION_DRAG_CANCEL = + new AccessibilityAction(R.id.accessibilityActionDragCancel); + private final int mActionId; private final CharSequence mLabel; diff --git a/core/java/android/view/translation/UiTranslationController.java b/core/java/android/view/translation/UiTranslationController.java index a8335918cc84..442d099f0678 100644 --- a/core/java/android/view/translation/UiTranslationController.java +++ b/core/java/android/view/translation/UiTranslationController.java @@ -356,7 +356,11 @@ public class UiTranslationController { } for (int i = 0; i < translatedResult.size(); i++) { final AutofillId autofillId = new AutofillId(translatedResult.keyAt(i)); - final View view = mViews.get(autofillId).get(); + final WeakReference<View> viewRef = mViews.get(autofillId); + if (viewRef == null) { + continue; + } + final View view = viewRef.get(); if (view == null) { Log.w(TAG, "onTranslationCompleted: the view for autofill id " + autofillId + " may be gone."); @@ -416,7 +420,11 @@ public class UiTranslationController { Log.w(TAG, "No AutofillId is set in ViewTranslationResponse"); continue; } - final View view = mViews.get(autofillId).get(); + final WeakReference<View> viewRef = mViews.get(autofillId); + if (viewRef == null) { + continue; + } + final View view = viewRef.get(); if (view == null) { Log.w(TAG, "onTranslationCompleted: the view for autofill id " + autofillId + " may be gone."); diff --git a/core/java/android/view/translation/UiTranslationManager.java b/core/java/android/view/translation/UiTranslationManager.java index b9ed32ce248b..3012e9344a1b 100644 --- a/core/java/android/view/translation/UiTranslationManager.java +++ b/core/java/android/view/translation/UiTranslationManager.java @@ -33,6 +33,7 @@ import android.util.ArrayMap; import android.util.Log; import android.view.View; import android.view.autofill.AutofillId; +import android.widget.TextView; import com.android.internal.annotations.GuardedBy; @@ -42,11 +43,50 @@ import java.util.List; import java.util.Map; import java.util.Objects; import java.util.concurrent.Executor; +import java.util.function.Consumer; -// TODO(b/178044703): Describe what UI Translation is. /** - * The {@link UiTranslationManager} class provides ways for apps to use the ui translation + * <p>The {@link UiTranslationManager} class provides ways for apps to use the ui translation * function in framework. + * + * <p> The UI translation provides ways for apps to support inline translation for the views. For + * example the system supports text translation for {@link TextView}. To support UI translation for + * your views, you should override the following methods to provide the content to be translated + * and deal with the translated result. Here is an example for {@link TextView}-like views: + * + * <pre><code> + * public class MyTextView extends View { + * public MyTextView(...) { + * // implements how to show the translated result in your View in + * // ViewTranslationCallback and set it by setViewTranslationCallback() + * setViewTranslationCallback(new MyViewTranslationCallback()); + * } + * + * public void onCreateViewTranslationRequest(int[] supportedFormats, + * Consumer<ViewTranslationRequest> requestsCollector) { + * // collect the information that needs to be translated + * ViewTranslationRequest.Builder requestBuilder = + * new ViewTranslationRequest.Builder(getAutofillId()); + * requestBuilder.setValue(ViewTranslationRequest.ID_TEXT, + * TranslationRequestValue.forText(etText())); + * requestsCollector.accept(requestBuilder.build()); + * } + * + * public void onProvideContentCaptureStructure( + * ViewStructure structure, int flags) { + * // set ViewTranslationResponse + * super.onViewTranslationResponse(response); + * } + * } + * </code></pre> + * + * <p>If your view provides its own virtual hierarchy (for example, if it's a browser that draws the + * HTML using {@link android.graphics.Canvas} or native libraries in a different render process), + * you must override {@link View#onCreateVirtualViewTranslationRequests(long[], int[], Consumer)} to + * provide the content to be translated and implement + * {@link View#onVirtualViewTranslationResponses(android.util.LongSparseArray)} for the translated + * result. You also need to implement {@link android.view.translation.ViewTranslationCallback} to + * handle the translated information show or hide in your {@link View}. */ public final class UiTranslationManager { @@ -248,14 +288,14 @@ public final class UiTranslationManager { } } - // TODO(b/178044703): Fix the View API link when it becomes public. /** * Register for notifications of UI Translation state changes on the foreground activity. This * is available to the owning application itself and also the current input method. * <p> * The application whose UI is being translated can use this to customize the UI Translation * behavior in ways that aren't made easy by methods like - * View#onCreateTranslationRequest(). + * {@link View#onCreateViewTranslationRequest(int[], Consumer)}. + * * <p> * Input methods can use this to offer complementary features to UI Translation; for example, * enabling outgoing message translation when the system is translating incoming messages in a diff --git a/core/java/android/view/translation/ViewTranslationCallback.java b/core/java/android/view/translation/ViewTranslationCallback.java index 6efd621c4caa..a0756622ca69 100644 --- a/core/java/android/view/translation/ViewTranslationCallback.java +++ b/core/java/android/view/translation/ViewTranslationCallback.java @@ -19,9 +19,17 @@ package android.view.translation; import android.annotation.NonNull; import android.annotation.UiThread; import android.view.View; +import android.view.contentcapture.ContentCaptureSession; /** - * Callback for handling the translated information show or hide in the {@link View}. + * <p> Callback for handling the translated information show or hide in the {@link View}. + * + * <p> When the platform intelligence starts translation of an app's ui, the system will call + * {@link View#dispatchCreateViewTranslationRequest} to collect the {@link ViewTranslationRequest}s + * for translation purpose by traversing the hierarchy then send to translation service. After + * receiving the {@link ViewTranslationResponse}, the system will call + * {@link ViewTranslationCallback#onShowTranslation(View)} to show the translated information for + * the {@link View}. */ @UiThread public interface ViewTranslationCallback { @@ -33,13 +41,19 @@ public interface ViewTranslationCallback { * method will not be called before {@link View#onViewTranslationResponse} or * {@link View#onVirtualViewTranslationResponses}. * + * <p> NOTE: For TextView implementation, {@link ContentCaptureSession#notifyViewTextChanged} + * shouldn't be called with the translated text, simply calling setText() here will trigger the + * method. You should either override {@code View#onProvideContentCaptureStructure()} to report + * the original text instead of the translated text or use a different approach to display the + * translated text. + * * See {@link View#onViewTranslationResponse} for how to get the translated information. * * @return {@code true} if the View handles showing the translation. */ boolean onShowTranslation(@NonNull View view); /** - * Called when the user wants to show the original text instead of the translated text. This + * Called when user wants to view the original content instead of the translated content. This * method will not be called before {@link View#onViewTranslationResponse} or * {@link View#onViewTranslationResponse}. * @@ -47,7 +61,8 @@ public interface ViewTranslationCallback { */ boolean onHideTranslation(@NonNull View view); /** - * Called when the user finish the Ui translation and no longer to show the translated text. + * Called when the translation state is no longer needed. It should restore the original content + * and clear all saved states. * * @return {@code true} if the View handles clearing the translation. */ diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index ca6e735f86b4..f5c1bcf2de42 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -11857,6 +11857,15 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (text != null) { if (expandedTopChar > 0 || expandedBottomChar < text.length()) { + // Cap the offsets to avoid an OOB exception. That can happen if the + // displayed/layout text, on which these offsets are calculated, is longer + // than the original text (such as when the view is translated by the + // platform intelligence). + // TODO(b/196433694): Figure out how to better handle the offset + // calculations for this case (so we don't unnecessarily cutoff the original + // text, for example). + expandedTopChar = Math.min(expandedTopChar, text.length()); + expandedBottomChar = Math.min(expandedBottomChar, text.length()); text = text.subSequence(expandedTopChar, expandedBottomChar); } diff --git a/core/java/android/window/SizeConfigurationBuckets.java b/core/java/android/window/SizeConfigurationBuckets.java index 7422f2449a8d..f474f0a76cc6 100644 --- a/core/java/android/window/SizeConfigurationBuckets.java +++ b/core/java/android/window/SizeConfigurationBuckets.java @@ -16,6 +16,7 @@ package android.window; +import static android.content.pm.ActivityInfo.CONFIG_SCREEN_LAYOUT; import static android.content.pm.ActivityInfo.CONFIG_SCREEN_SIZE; import static android.content.pm.ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE; @@ -25,6 +26,7 @@ import android.content.res.Configuration; import android.os.Parcelable; import android.util.SparseIntArray; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.DataClass; import java.util.Arrays; @@ -54,10 +56,24 @@ public final class SizeConfigurationBuckets implements Parcelable { @Nullable private final int[] mSmallest; + /** Screen Layout Size (screenLayout & SCREENLAYOUT_SIZE_MASK) buckets */ + @Nullable + private final int[] mScreenLayoutSize; + + /** + * Screen Layout Long (screenLayout & SCREENLAYOUT_LONG_MASK) boolean. Only need to know if a + * value is set because only two possible buckets, SCREENLAYOUT_LONG_NO and + * SCREENLAYOUT_LONG_YES, so if either is set, then any change is a bucket change. + */ + private final boolean mScreenLayoutLongSet; + public SizeConfigurationBuckets(Configuration[] sizeConfigurations) { SparseIntArray horizontal = new SparseIntArray(); SparseIntArray vertical = new SparseIntArray(); SparseIntArray smallest = new SparseIntArray(); + SparseIntArray screenLayoutSize = new SparseIntArray(); + int curScreenLayoutSize; + boolean screenLayoutLongSet = false; for (int i = sizeConfigurations.length - 1; i >= 0; i--) { Configuration config = sizeConfigurations[i]; if (config.screenHeightDp != Configuration.SCREEN_HEIGHT_DP_UNDEFINED) { @@ -69,23 +85,42 @@ public final class SizeConfigurationBuckets implements Parcelable { if (config.smallestScreenWidthDp != Configuration.SMALLEST_SCREEN_WIDTH_DP_UNDEFINED) { smallest.put(config.smallestScreenWidthDp, 0); } + if ((curScreenLayoutSize = config.screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) + != Configuration.SCREENLAYOUT_SIZE_UNDEFINED) { + screenLayoutSize.put(curScreenLayoutSize, 0); + } + if (!screenLayoutLongSet && (config.screenLayout & Configuration.SCREENLAYOUT_LONG_MASK) + != Configuration.SCREENLAYOUT_LONG_UNDEFINED) { + screenLayoutLongSet = true; + } } mHorizontal = horizontal.copyKeys(); mVertical = vertical.copyKeys(); mSmallest = smallest.copyKeys(); + mScreenLayoutSize = screenLayoutSize.copyKeys(); + mScreenLayoutLongSet = screenLayoutLongSet; } /** * Get the changes between two configurations but don't count changes in sizes if they don't - * cross boundaries that are important to the app. + * cross boundaries that are important to the app. * * This is a static helper to deal with null `buckets`. When no buckets have been specified, * this actually filters out all 3 size-configs. This is legacy behavior. */ - public static int filterDiff(int diff, Configuration oldConfig, Configuration newConfig, - @Nullable SizeConfigurationBuckets buckets) { + public static int filterDiff(int diff, @NonNull Configuration oldConfig, + @NonNull Configuration newConfig, @Nullable SizeConfigurationBuckets buckets) { + final boolean nonSizeLayoutFieldsUnchanged = + areNonSizeLayoutFieldsUnchanged(oldConfig.screenLayout, newConfig.screenLayout); if (buckets == null) { - return diff & ~(CONFIG_SCREEN_SIZE | CONFIG_SMALLEST_SCREEN_SIZE); + // Only unflip CONFIG_SCREEN_LAYOUT if non-size-related attributes of screen layout do + // not change. + if (nonSizeLayoutFieldsUnchanged) { + return diff & ~(CONFIG_SCREEN_SIZE | CONFIG_SMALLEST_SCREEN_SIZE + | CONFIG_SCREEN_LAYOUT); + } else { + return diff & ~(CONFIG_SCREEN_SIZE | CONFIG_SMALLEST_SCREEN_SIZE); + } } if ((diff & CONFIG_SCREEN_SIZE) != 0) { final boolean crosses = buckets.crossesHorizontalSizeThreshold(oldConfig.screenWidthDp, @@ -103,6 +138,13 @@ public final class SizeConfigurationBuckets implements Parcelable { diff &= ~CONFIG_SMALLEST_SCREEN_SIZE; } } + if ((diff & CONFIG_SCREEN_LAYOUT) != 0 && nonSizeLayoutFieldsUnchanged) { + if (!buckets.crossesScreenLayoutSizeThreshold(oldConfig, newConfig) + && !buckets.crossesScreenLayoutLongThreshold(oldConfig.screenLayout, + newConfig.screenLayout)) { + diff &= ~CONFIG_SCREEN_LAYOUT; + } + } return diff; } @@ -119,6 +161,61 @@ public final class SizeConfigurationBuckets implements Parcelable { } /** + * Returns whether a screen layout size threshold has been crossed. + */ + @VisibleForTesting + public boolean crossesScreenLayoutSizeThreshold(@NonNull Configuration firstConfig, + @NonNull Configuration secondConfig) { + // If both the old and new screen layout are equal (both can be undefined), then no + // threshold is crossed. + if ((firstConfig.screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) + == (secondConfig.screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK)) { + return false; + } + // Any time the new layout size is smaller than the old layout size, the activity has + // crossed a size threshold because layout size represents the smallest possible size the + // activity can occupy. + if (!secondConfig.isLayoutSizeAtLeast(firstConfig.screenLayout + & Configuration.SCREENLAYOUT_SIZE_MASK)) { + return true; + } + // If the new layout size is at least as large as the old layout size, then check if the new + // layout size has crossed a threshold. + if (mScreenLayoutSize != null) { + for (int screenLayoutSize : mScreenLayoutSize) { + if (firstConfig.isLayoutSizeAtLeast(screenLayoutSize) + != secondConfig.isLayoutSizeAtLeast(screenLayoutSize)) { + return true; + } + } + } + return false; + } + + private boolean crossesScreenLayoutLongThreshold(int firstScreenLayout, + int secondScreenLayout) { + final int firstScreenLayoutLongValue = firstScreenLayout + & Configuration.SCREENLAYOUT_LONG_MASK; + final int secondScreenLayoutLongValue = secondScreenLayout + & Configuration.SCREENLAYOUT_LONG_MASK; + return mScreenLayoutLongSet && firstScreenLayoutLongValue != secondScreenLayoutLongValue; + } + + /** + * Returns whether non-size related screen layout attributes have changed. If true, then + * {@link ActivityInfo#CONFIG_SCREEN_LAYOUT} should not be filtered out in + * {@link SizeConfigurationBuckets#filterDiff()} because the non-size related attributes + * do not have a bucket range like the size-related attributes of screen layout. + */ + @VisibleForTesting + public static boolean areNonSizeLayoutFieldsUnchanged(int oldScreenLayout, + int newScreenLayout) { + final int nonSizeRelatedFields = Configuration.SCREENLAYOUT_LAYOUTDIR_MASK + | Configuration.SCREENLAYOUT_ROUND_MASK | Configuration.SCREENLAYOUT_COMPAT_NEEDED; + return (oldScreenLayout & nonSizeRelatedFields) == (newScreenLayout & nonSizeRelatedFields); + } + + /** * The purpose of this method is to decide whether the activity needs to be relaunched upon * changing its size. In most cases the activities don't need to be relaunched, if the resize * is small, all the activity content has to do is relayout itself within new bounds. There are @@ -132,7 +229,8 @@ public final class SizeConfigurationBuckets implements Parcelable { * it resizes width from 620dp to 700dp, it won't be relaunched as it stays on the same side * of the threshold. */ - private static boolean crossesSizeThreshold(int[] thresholds, int firstDp, + @VisibleForTesting + public static boolean crossesSizeThreshold(int[] thresholds, int firstDp, int secondDp) { if (thresholds == null) { return false; @@ -150,12 +248,13 @@ public final class SizeConfigurationBuckets implements Parcelable { @Override public String toString() { return Arrays.toString(mHorizontal) + " " + Arrays.toString(mVertical) + " " - + Arrays.toString(mSmallest); + + Arrays.toString(mSmallest) + " " + Arrays.toString(mScreenLayoutSize) + " " + + mScreenLayoutLongSet; } - // Code below generated by codegen v1.0.22. + // Code below generated by codegen v1.0.23. // // DO NOT MODIFY! // CHECKSTYLE:OFF Generated code @@ -177,15 +276,25 @@ public final class SizeConfigurationBuckets implements Parcelable { * Vertical (screenHeightDp) buckets * @param smallest * Smallest (smallestScreenWidthDp) buckets + * @param screenLayoutSize + * Screen Layout Size (screenLayout & SCREENLAYOUT_SIZE_MASK) buckets + * @param screenLayoutLongSet + * Screen Layout Long (screenLayout & SCREENLAYOUT_LONG_MASK) boolean. Only need to know if a + * value is set because only two possible buckets, SCREENLAYOUT_LONG_NO and + * SCREENLAYOUT_LONG_YES, so if either is set, then any change is a bucket change. */ @DataClass.Generated.Member public SizeConfigurationBuckets( @Nullable int[] horizontal, @Nullable int[] vertical, - @Nullable int[] smallest) { + @Nullable int[] smallest, + @Nullable int[] screenLayoutSize, + boolean screenLayoutLongSet) { this.mHorizontal = horizontal; this.mVertical = vertical; this.mSmallest = smallest; + this.mScreenLayoutSize = screenLayoutSize; + this.mScreenLayoutLongSet = screenLayoutLongSet; // onConstructed(); // You can define this method to get a callback } @@ -214,6 +323,24 @@ public final class SizeConfigurationBuckets implements Parcelable { return mSmallest; } + /** + * Screen Layout Size (screenLayout & SCREENLAYOUT_SIZE_MASK) buckets + */ + @DataClass.Generated.Member + public @Nullable int[] getScreenLayoutSize() { + return mScreenLayoutSize; + } + + /** + * Screen Layout Long (screenLayout & SCREENLAYOUT_LONG_MASK) boolean. Only need to know if a + * value is set because only two possible buckets, SCREENLAYOUT_LONG_NO and + * SCREENLAYOUT_LONG_YES, so if either is set, then any change is a bucket change. + */ + @DataClass.Generated.Member + public boolean isScreenLayoutLongSet() { + return mScreenLayoutLongSet; + } + @Override @DataClass.Generated.Member public void writeToParcel(@NonNull android.os.Parcel dest, int flags) { @@ -221,13 +348,16 @@ public final class SizeConfigurationBuckets implements Parcelable { // void parcelFieldName(Parcel dest, int flags) { ... } byte flg = 0; + if (mScreenLayoutLongSet) flg |= 0x10; if (mHorizontal != null) flg |= 0x1; if (mVertical != null) flg |= 0x2; if (mSmallest != null) flg |= 0x4; + if (mScreenLayoutSize != null) flg |= 0x8; dest.writeByte(flg); if (mHorizontal != null) dest.writeIntArray(mHorizontal); if (mVertical != null) dest.writeIntArray(mVertical); if (mSmallest != null) dest.writeIntArray(mSmallest); + if (mScreenLayoutSize != null) dest.writeIntArray(mScreenLayoutSize); } @Override @@ -242,13 +372,17 @@ public final class SizeConfigurationBuckets implements Parcelable { // static FieldType unparcelFieldName(Parcel in) { ... } byte flg = in.readByte(); + boolean screenLayoutLongSet = (flg & 0x10) != 0; int[] horizontal = (flg & 0x1) == 0 ? null : in.createIntArray(); int[] vertical = (flg & 0x2) == 0 ? null : in.createIntArray(); int[] smallest = (flg & 0x4) == 0 ? null : in.createIntArray(); + int[] screenLayoutSize = (flg & 0x8) == 0 ? null : in.createIntArray(); this.mHorizontal = horizontal; this.mVertical = vertical; this.mSmallest = smallest; + this.mScreenLayoutSize = screenLayoutSize; + this.mScreenLayoutLongSet = screenLayoutLongSet; // onConstructed(); // You can define this method to get a callback } @@ -268,10 +402,10 @@ public final class SizeConfigurationBuckets implements Parcelable { }; @DataClass.Generated( - time = 1615845864280L, - codegenVersion = "1.0.22", + time = 1628273704583L, + codegenVersion = "1.0.23", sourceFile = "frameworks/base/core/java/android/window/SizeConfigurationBuckets.java", - inputSignatures = "private final @android.annotation.Nullable int[] mHorizontal\nprivate final @android.annotation.Nullable int[] mVertical\nprivate final @android.annotation.Nullable int[] mSmallest\npublic static int filterDiff(int,android.content.res.Configuration,android.content.res.Configuration,android.window.SizeConfigurationBuckets)\nprivate boolean crossesHorizontalSizeThreshold(int,int)\nprivate boolean crossesVerticalSizeThreshold(int,int)\nprivate boolean crossesSmallestSizeThreshold(int,int)\nprivate static boolean crossesSizeThreshold(int[],int,int)\npublic @java.lang.Override java.lang.String toString()\nclass SizeConfigurationBuckets extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genAidl=true)") + inputSignatures = "private final @android.annotation.Nullable int[] mHorizontal\nprivate final @android.annotation.Nullable int[] mVertical\nprivate final @android.annotation.Nullable int[] mSmallest\nprivate final @android.annotation.Nullable int[] mScreenLayoutSize\nprivate final boolean mScreenLayoutLongSet\npublic static int filterDiff(int,android.content.res.Configuration,android.content.res.Configuration,android.window.SizeConfigurationBuckets)\nprivate boolean crossesHorizontalSizeThreshold(int,int)\nprivate boolean crossesVerticalSizeThreshold(int,int)\nprivate boolean crossesSmallestSizeThreshold(int,int)\npublic @com.android.internal.annotations.VisibleForTesting boolean crossesScreenLayoutSizeThreshold(android.content.res.Configuration,android.content.res.Configuration)\nprivate boolean crossesScreenLayoutLongThreshold(int,int)\npublic static @com.android.internal.annotations.VisibleForTesting boolean areNonSizeLayoutFieldsUnchanged(int,int)\npublic static @com.android.internal.annotations.VisibleForTesting boolean crossesSizeThreshold(int[],int,int)\npublic @java.lang.Override java.lang.String toString()\nclass SizeConfigurationBuckets extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genAidl=true)") @Deprecated private void __metadata() {} diff --git a/core/java/android/window/TransitionFilter.java b/core/java/android/window/TransitionFilter.java index 34facc40d398..db15145b0a1e 100644 --- a/core/java/android/window/TransitionFilter.java +++ b/core/java/android/window/TransitionFilter.java @@ -59,6 +59,9 @@ public final class TransitionFilter implements Parcelable { /** All flags must be set on a transition. */ public @WindowManager.TransitionFlags int mFlags = 0; + /** All flags must NOT be set on a transition. */ + public @WindowManager.TransitionFlags int mNotFlags = 0; + /** * A list of required changes. To pass, a transition must meet all requirements. */ @@ -70,6 +73,7 @@ public final class TransitionFilter implements Parcelable { private TransitionFilter(Parcel in) { mTypeSet = in.createIntArray(); mFlags = in.readInt(); + mNotFlags = in.readInt(); mRequirements = in.createTypedArray(Requirement.CREATOR); } @@ -89,6 +93,9 @@ public final class TransitionFilter implements Parcelable { if ((info.getFlags() & mFlags) != mFlags) { return false; } + if ((info.getFlags() & mNotFlags) != 0) { + return false; + } // Make sure info meets all of the requirements. if (mRequirements != null) { for (int i = 0; i < mRequirements.length; ++i) { @@ -106,6 +113,7 @@ public final class TransitionFilter implements Parcelable { public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeIntArray(mTypeSet); dest.writeInt(mFlags); + dest.writeInt(mNotFlags); dest.writeTypedArray(mRequirements, flags); } @@ -139,6 +147,7 @@ public final class TransitionFilter implements Parcelable { } } sb.append("] flags=0x" + Integer.toHexString(mFlags)); + sb.append("] notFlags=0x" + Integer.toHexString(mNotFlags)); sb.append(" checks=["); if (mRequirements != null) { for (int i = 0; i < mRequirements.length; ++i) { diff --git a/core/java/android/window/TransitionInfo.java b/core/java/android/window/TransitionInfo.java index 702a8baf0f15..c2ffc03b6119 100644 --- a/core/java/android/window/TransitionInfo.java +++ b/core/java/android/window/TransitionInfo.java @@ -26,6 +26,7 @@ import static android.app.WindowConfiguration.ROTATION_UNDEFINED; import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_UNSPECIFIED; import static android.view.WindowManager.TRANSIT_CHANGE; import static android.view.WindowManager.TRANSIT_CLOSE; +import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY; import static android.view.WindowManager.TRANSIT_NONE; import static android.view.WindowManager.TRANSIT_OPEN; import static android.view.WindowManager.TRANSIT_TO_BACK; @@ -249,6 +250,13 @@ public final class TransitionInfo implements Parcelable { mChanges.add(change); } + /** + * Whether this transition includes keyguard going away. + */ + public boolean isKeyguardGoingAway() { + return (mFlags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY) != 0; + } + @Override public String toString() { StringBuilder sb = new StringBuilder(); diff --git a/core/java/android/window/WindowContainerTransaction.java b/core/java/android/window/WindowContainerTransaction.java index 342df4f9eee3..387837d82acf 100644 --- a/core/java/android/window/WindowContainerTransaction.java +++ b/core/java/android/window/WindowContainerTransaction.java @@ -516,11 +516,13 @@ public final class WindowContainerTransaction implements Parcelable { */ @NonNull public WindowContainerTransaction setAdjacentTaskFragments( - @NonNull IBinder fragmentToken1, @Nullable IBinder fragmentToken2) { + @NonNull IBinder fragmentToken1, @Nullable IBinder fragmentToken2, + @Nullable TaskFragmentAdjacentOptions options) { final HierarchyOp hierarchyOp = new HierarchyOp.Builder(HierarchyOp.HIERARCHY_OP_TYPE_SET_ADJACENT_TASK_FRAGMENTS) .setContainer(fragmentToken1) .setReparentContainer(fragmentToken2) + .setLaunchOptions(options != null ? options.toBundle() : null) .build(); mHierarchyOps.add(hierarchyOp); return this; @@ -1298,4 +1300,52 @@ public final class WindowContainerTransaction implements Parcelable { } } } + + /** + * Helper class for building an options Bundle that can be used to set adjacent rules of + * TaskFragments. + * @hide + */ + public static class TaskFragmentAdjacentOptions { + private static final String DELAY_PRIMARY_LAST_ACTIVITY_REMOVAL = + "android:transaction.adjacent.option.delay_primary_removal"; + private static final String DELAY_SECONDARY_LAST_ACTIVITY_REMOVAL = + "android:transaction.adjacent.option.delay_secondary_removal"; + + private boolean mDelayPrimaryLastActivityRemoval; + private boolean mDelaySecondaryLastActivityRemoval; + + public TaskFragmentAdjacentOptions() { + } + + public TaskFragmentAdjacentOptions(@NonNull Bundle bundle) { + mDelayPrimaryLastActivityRemoval = bundle.getBoolean( + DELAY_PRIMARY_LAST_ACTIVITY_REMOVAL); + mDelaySecondaryLastActivityRemoval = bundle.getBoolean( + DELAY_SECONDARY_LAST_ACTIVITY_REMOVAL); + } + + public void setDelayPrimaryLastActivityRemoval(boolean delay) { + mDelayPrimaryLastActivityRemoval = delay; + } + + public void setDelaySecondaryLastActivityRemoval(boolean delay) { + mDelaySecondaryLastActivityRemoval = delay; + } + + public boolean isDelayPrimaryLastActivityRemoval() { + return mDelayPrimaryLastActivityRemoval; + } + + public boolean isDelaySecondaryLastActivityRemoval() { + return mDelaySecondaryLastActivityRemoval; + } + + Bundle toBundle() { + final Bundle b = new Bundle(); + b.putBoolean(DELAY_PRIMARY_LAST_ACTIVITY_REMOVAL, mDelayPrimaryLastActivityRemoval); + b.putBoolean(DELAY_SECONDARY_LAST_ACTIVITY_REMOVAL, mDelaySecondaryLastActivityRemoval); + return b; + } + } } diff --git a/core/java/android/window/WindowContext.java b/core/java/android/window/WindowContext.java index 5d400853540f..cfccb712127e 100644 --- a/core/java/android/window/WindowContext.java +++ b/core/java/android/window/WindowContext.java @@ -26,6 +26,7 @@ import android.content.Context; import android.content.ContextWrapper; import android.content.res.Configuration; import android.os.Bundle; +import android.view.Display; import android.view.WindowManager; import com.android.internal.annotations.VisibleForTesting; @@ -42,29 +43,33 @@ import java.lang.ref.Reference; * @hide */ @UiContext -public class WindowContext extends ContextWrapper { +public class WindowContext extends ContextWrapper implements WindowProvider { private final WindowManager mWindowManager; - private final @WindowManager.LayoutParams.WindowType int mType; - private final @Nullable Bundle mOptions; + @WindowManager.LayoutParams.WindowType + private final int mType; + @Nullable + private final Bundle mOptions; private final ComponentCallbacksController mCallbacksController = new ComponentCallbacksController(); private final WindowContextController mController; /** - * Default constructor. Will generate a {@link WindowTokenClient} and attach this context to - * the token. + * Default implementation of {@link WindowContext} + * <p> + * Note that the users should call {@link Context#createWindowContext(Display, int, Bundle)} + * to create a {@link WindowContext} instead of using this constructor + * </p><p> + * Example usage: + * <pre class="prettyprint"> + * Bundle options = new Bundle(); + * options.put(KEY_ROOT_DISPLAY_AREA_ID, displayAreaInfo.rootDisplayAreaId); + * Context windowContext = context.createWindowContext(display, windowType, options); + * </pre></p> * - * @param base Base {@link Context} for this new instance. - * @param type Window type to be used with this context. - * @param options A bundle used to pass window-related options. For example, on device with - * multiple DisplayAreaGroups, one may specify the RootDisplayArea for the window - * using {@link DisplayAreaOrganizer#KEY_ROOT_DISPLAY_AREA_ID} in the options. - * Example usage: - * Bundle options = new Bundle(); - * options.put(KEY_ROOT_DISPLAY_AREA_ID, displayAreaInfo.rootDisplayAreaId); - * Context windowContext = context.createWindowContext(display, type, options); + * @param base Base {@link Context} for this new instance. + * @param type Window type to be used with this context. + * @param options A bundle used to pass window-related options. * @see DisplayAreaInfo#rootDisplayAreaId - * @hide */ public WindowContext(@NonNull Context base, int type, @Nullable Bundle options) { super(base); @@ -110,10 +115,13 @@ public class WindowContext extends ContextWrapper { @Override public void destroy() { - mCallbacksController.clearCallbacks(); - // Called to the base ContextImpl to do final clean-up. - getBaseContext().destroy(); - Reference.reachabilityFence(this); + try { + mCallbacksController.clearCallbacks(); + // Called to the base ContextImpl to do final clean-up. + getBaseContext().destroy(); + } finally { + Reference.reachabilityFence(this); + } } @Override @@ -130,4 +138,15 @@ public class WindowContext extends ContextWrapper { void dispatchConfigurationChanged(@NonNull Configuration newConfig) { mCallbacksController.dispatchConfigurationChanged(newConfig); } + + @Override + public int getWindowType() { + return mType; + } + + @Nullable + @Override + public Bundle getWindowContextOptions() { + return mOptions; + } } diff --git a/core/java/android/window/WindowProvider.java b/core/java/android/window/WindowProvider.java new file mode 100644 index 000000000000..b078b9362b90 --- /dev/null +++ b/core/java/android/window/WindowProvider.java @@ -0,0 +1,39 @@ +/* + * 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.window; + +import android.annotation.Nullable; +import android.os.Bundle; +import android.view.WindowManager.LayoutParams.WindowType; + +/** + * An interface to provide a non-activity window. + * Examples are {@link WindowContext} and {@link WindowProviderService}. + * + * @hide + */ +public interface WindowProvider { + /** @hide */ + String KEY_IS_WINDOW_PROVIDER_SERVICE = "android.windowContext.isWindowProviderService"; + + /** Gets the window type of this provider */ + @WindowType + int getWindowType(); + + /** Gets the launch options of this provider */ + @Nullable + Bundle getWindowContextOptions(); +} diff --git a/core/java/android/window/WindowProviderService.java b/core/java/android/window/WindowProviderService.java index 5171adf168ce..f8484d15344e 100644 --- a/core/java/android/window/WindowProviderService.java +++ b/core/java/android/window/WindowProviderService.java @@ -42,21 +42,39 @@ import android.view.WindowManagerImpl; * {@link WindowContext}, but is represented as {@link Service}. * * @see android.inputmethodservice.InputMethodService - * @see android.accessibilityservice.AccessibilityService * * @hide */ @TestApi @UiContext -public abstract class WindowProviderService extends Service { +public abstract class WindowProviderService extends Service implements WindowProvider { + private final Bundle mOptions; private final WindowTokenClient mWindowToken = new WindowTokenClient(); private final WindowContextController mController = new WindowContextController(mWindowToken); private WindowManager mWindowManager; private boolean mInitialized; /** - * Returns the type of this {@link WindowProviderService}. + * Returns {@code true} if the {@code windowContextOptions} declares that it is a + * {@link WindowProviderService}. + * + * @hide + */ + public static boolean isWindowProviderService(@Nullable Bundle windowContextOptions) { + if (windowContextOptions == null) { + return false; + } + return (windowContextOptions.getBoolean(KEY_IS_WINDOW_PROVIDER_SERVICE, false)); + } + + public WindowProviderService() { + mOptions = new Bundle(); + mOptions.putBoolean(KEY_IS_WINDOW_PROVIDER_SERVICE, true); + } + + /** + * Returns the window type of this {@link WindowProviderService}. * Each inheriting class must implement this method to provide the type of the window. It is * used similar to {@code type} of {@link Context#createWindowContext(int, Bundle)} * @@ -68,15 +86,24 @@ public abstract class WindowProviderService extends Service { @SuppressLint("OnNameExpected") // Suppress the lint because it is not a callback and users should provide window type // so we cannot make it final. - public abstract @WindowType int getWindowType(); + @WindowType + @Override + public abstract int getWindowType(); /** * Returns the option of this {@link WindowProviderService}. - * Default is {@code null}. The inheriting class can implement this method to provide the - * customization {@code option} of the window. It is used similar to {@code options} of - * {@link Context#createWindowContext(int, Bundle)} - * - * @see Context#createWindowContext(int, Bundle) + * <p> + * The inheriting class can implement this method to provide the customization {@code option} of + * the window, but must be based on this method's returned value. + * It is used similar to {@code options} of {@link Context#createWindowContext(int, Bundle)} + * </p> + * <pre class="prettyprint"> + * public Bundle getWindowContextOptions() { + * final Bundle options = super.getWindowContextOptions(); + * options.put(KEY_ROOT_DISPLAY_AREA_ID, displayAreaInfo.rootDisplayAreaId); + * return options; + * } + * </pre> * * @hide */ @@ -85,8 +112,10 @@ public abstract class WindowProviderService extends Service { // Suppress the lint because it is not a callback and users may override this API to provide // launch option. Also, the return value of this API is null by default. @Nullable + @CallSuper + @Override public Bundle getWindowContextOptions() { - return null; + return mOptions; } /** diff --git a/core/java/android/window/WindowTokenClient.java b/core/java/android/window/WindowTokenClient.java index 284b4b98b4c9..f3e3859b4256 100644 --- a/core/java/android/window/WindowTokenClient.java +++ b/core/java/android/window/WindowTokenClient.java @@ -94,6 +94,8 @@ public class WindowTokenClient extends IWindowToken.Stub { onConfigurationChanged(newConfig, newDisplayId, true /* shouldReportConfigChange */); } + // TODO(b/192048581): rewrite this method based on WindowContext and WindowProviderService + // are inherited from WindowProvider. /** * Called when {@link Configuration} updates from the server side receive. * diff --git a/core/java/com/android/internal/util/ContrastColorUtil.java b/core/java/com/android/internal/util/ContrastColorUtil.java index 8b3c1337c0c8..7a712e50e6a8 100644 --- a/core/java/com/android/internal/util/ContrastColorUtil.java +++ b/core/java/com/android/internal/util/ContrastColorUtil.java @@ -291,10 +291,10 @@ public class ContrastColorUtil { * Finds a suitable color such that there's enough contrast. * * @param color the color to start searching from. - * @param other the color to ensure contrast against. Assumed to be lighter than {@param color} - * @param findFg if true, we assume {@param color} is a foreground, otherwise a background. + * @param other the color to ensure contrast against. Assumed to be lighter than {@code color} + * @param findFg if true, we assume {@code color} is a foreground, otherwise a background. * @param minRatio the minimum contrast ratio required. - * @return a color with the same hue as {@param color}, potentially darkened to meet the + * @return a color with the same hue as {@code color}, potentially darkened to meet the * contrast ratio. */ public static int findContrastColor(int color, int other, boolean findFg, double minRatio) { @@ -331,7 +331,7 @@ public class ContrastColorUtil { * @param color the color to start searching from. * @param backgroundColor the color to ensure contrast against. * @param minRatio the minimum contrast ratio required. - * @return the same color as {@param color} with potentially modified alpha to meet contrast + * @return the same color as {@code color} with potentially modified alpha to meet contrast */ public static int findAlphaToMeetContrast(int color, int backgroundColor, double minRatio) { int fg = color; @@ -361,10 +361,10 @@ public class ContrastColorUtil { * Finds a suitable color such that there's enough contrast. * * @param color the color to start searching from. - * @param other the color to ensure contrast against. Assumed to be darker than {@param color} - * @param findFg if true, we assume {@param color} is a foreground, otherwise a background. + * @param other the color to ensure contrast against. Assumed to be darker than {@code color} + * @param findFg if true, we assume {@code color} is a foreground, otherwise a background. * @param minRatio the minimum contrast ratio required. - * @return a color with the same hue as {@param color}, potentially darkened to meet the + * @return a color with the same hue as {@code color}, potentially lightened to meet the * contrast ratio. */ public static int findContrastColorAgainstDark(int color, int other, boolean findFg, @@ -393,7 +393,8 @@ public class ContrastColorUtil { low = l; } } - return findFg ? fg : bg; + hsl[2] = high; + return ColorUtilsFromCompat.HSLToColor(hsl); } public static int ensureTextContrastOnBlack(int color) { @@ -452,7 +453,7 @@ public class ContrastColorUtil { } /** - * Resolves {@param color} to an actual color if it is {@link Notification#COLOR_DEFAULT} + * Resolves {@code color} to an actual color if it is {@link Notification#COLOR_DEFAULT} */ public static int resolveColor(Context context, int color, boolean defaultBackgroundIsDark) { if (color == Notification.COLOR_DEFAULT) { diff --git a/core/java/com/android/internal/widget/NotificationActionListLayout.java b/core/java/com/android/internal/widget/NotificationActionListLayout.java index 0c2d2a9bcf41..3191e23a9883 100644 --- a/core/java/com/android/internal/widget/NotificationActionListLayout.java +++ b/core/java/com/android/internal/widget/NotificationActionListLayout.java @@ -53,6 +53,8 @@ public class NotificationActionListLayout extends LinearLayout { private int mEmphasizedHeight; private int mRegularHeight; @DimenRes private int mCollapsibleIndentDimen = R.dimen.notification_actions_padding_start; + int mNumNotGoneChildren; + int mNumPriorityChildren; public NotificationActionListLayout(Context context, AttributeSet attrs) { this(context, attrs, 0); @@ -76,15 +78,14 @@ public class NotificationActionListLayout extends LinearLayout { && ((EmphasizedNotificationButton) actionView).isPriority(); } - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - final int N = getChildCount(); + private void countAndRebuildMeasureOrder() { + final int numChildren = getChildCount(); int textViews = 0; int otherViews = 0; - int notGoneChildren = 0; - int priorityChildren = 0; + mNumNotGoneChildren = 0; + mNumPriorityChildren = 0; - for (int i = 0; i < N; i++) { + for (int i = 0; i < numChildren; i++) { View c = getChildAt(i); if (c instanceof TextView) { textViews++; @@ -92,9 +93,9 @@ public class NotificationActionListLayout extends LinearLayout { otherViews++; } if (c.getVisibility() != GONE) { - notGoneChildren++; + mNumNotGoneChildren++; if (isPriority(c)) { - priorityChildren++; + mNumPriorityChildren++; } } } @@ -119,17 +120,20 @@ public class NotificationActionListLayout extends LinearLayout { if (needRebuild) { rebuildMeasureOrder(textViews, otherViews); } + } + private int measureAndGetUsedWidth(int widthMeasureSpec, int heightMeasureSpec, int innerWidth, + boolean collapsePriorityActions) { + final int numChildren = getChildCount(); final boolean constrained = MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.UNSPECIFIED; - - final int innerWidth = MeasureSpec.getSize(widthMeasureSpec) - mPaddingLeft - mPaddingRight; final int otherSize = mMeasureOrderOther.size(); int usedWidth = 0; + int maxPriorityWidth = 0; int measuredChildren = 0; int measuredPriorityChildren = 0; - for (int i = 0; i < N; i++) { + for (int i = 0; i < numChildren; i++) { // Measure shortest children first. To avoid measuring twice, we approximate by looking // at the text length. final boolean isPriority; @@ -154,12 +158,20 @@ public class NotificationActionListLayout extends LinearLayout { // measure in the order of (approx.) size, a large view can still take more than its // share if the others are small. int availableWidth = innerWidth - usedWidth; - int unmeasuredChildren = notGoneChildren - measuredChildren; + int unmeasuredChildren = mNumNotGoneChildren - measuredChildren; int maxWidthForChild = availableWidth / unmeasuredChildren; - if (isPriority) { + if (isPriority && collapsePriorityActions) { + // Collapsing the actions to just the width required to show the icon. + if (maxPriorityWidth == 0) { + maxPriorityWidth = getResources().getDimensionPixelSize( + R.dimen.notification_actions_collapsed_priority_width); + } + maxWidthForChild = maxPriorityWidth + lp.leftMargin + lp.rightMargin; + } else if (isPriority) { // Priority children get a larger maximum share of the total space: // maximum priority share = (nPriority + 1) / (MAX + 1) - int unmeasuredPriorityChildren = priorityChildren - measuredPriorityChildren; + int unmeasuredPriorityChildren = mNumPriorityChildren + - measuredPriorityChildren; int unmeasuredOtherChildren = unmeasuredChildren - unmeasuredPriorityChildren; int widthReservedForOtherChildren = innerWidth * unmeasuredOtherChildren / (Notification.MAX_ACTION_BUTTONS + 1); @@ -187,6 +199,19 @@ public class NotificationActionListLayout extends LinearLayout { } else { mExtraStartPadding = 0; } + return usedWidth; + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + countAndRebuildMeasureOrder(); + final int innerWidth = MeasureSpec.getSize(widthMeasureSpec) - mPaddingLeft - mPaddingRight; + int usedWidth = measureAndGetUsedWidth(widthMeasureSpec, heightMeasureSpec, innerWidth, + false /* collapsePriorityButtons */); + if (mNumPriorityChildren != 0 && usedWidth >= innerWidth) { + usedWidth = measureAndGetUsedWidth(widthMeasureSpec, heightMeasureSpec, innerWidth, + true /* collapsePriorityButtons */); + } mTotalWidth = usedWidth + mPaddingRight + mPaddingLeft + mExtraStartPadding; setMeasuredDimension(resolveSize(getSuggestedMinimumWidth(), widthMeasureSpec), diff --git a/core/jni/android_hardware_input_InputWindowHandle.cpp b/core/jni/android_hardware_input_InputWindowHandle.cpp index 3f51dd8617c1..e4ef7d39d77c 100644 --- a/core/jni/android_hardware_input_InputWindowHandle.cpp +++ b/core/jni/android_hardware_input_InputWindowHandle.cpp @@ -26,6 +26,7 @@ #include <ui/Region.h> #include <utils/threads.h> +#include <android/graphics/matrix.h> #include <gui/WindowInfo.h> #include "SkRegion.h" #include "android_hardware_input_InputApplicationHandle.h" @@ -74,6 +75,8 @@ static struct { jfieldID displayId; jfieldID replaceTouchableRegionWithCrop; WeakRefHandleField touchableRegionSurfaceControl; + jfieldID transform; + jfieldID windowToken; } gInputWindowHandleClassInfo; static struct { @@ -213,6 +216,14 @@ bool NativeInputWindowHandle::updateInfo() { mInfo.touchableRegionCropHandle.clear(); } + jobject windowTokenObj = env->GetObjectField(obj, gInputWindowHandleClassInfo.windowToken); + if (windowTokenObj) { + mInfo.windowToken = ibinderForJavaObject(env, windowTokenObj); + env->DeleteLocalRef(windowTokenObj); + } else { + mInfo.windowToken.clear(); + } + env->DeleteLocalRef(obj); return true; } @@ -305,6 +316,16 @@ jobject android_view_InputWindowHandle_fromWindowInfo(JNIEnv* env, gui::WindowIn env->SetIntField(inputWindowHandle, gInputWindowHandleClassInfo.inputFeatures, static_cast<int32_t>(windowInfo.inputFeatures.get())); + float transformVals[9]; + for (int i = 0; i < 9; i++) { + transformVals[i] = windowInfo.transform[i % 3][i / 3]; + } + ScopedLocalRef<jobject> matrixObj(env, AMatrix_newInstance(env, transformVals)); + env->SetObjectField(inputWindowHandle, gInputWindowHandleClassInfo.transform, matrixObj.get()); + + env->SetObjectField(inputWindowHandle, gInputWindowHandleClassInfo.windowToken, + javaObjectForIBinder(env, windowInfo.windowToken)); + return inputWindowHandle; } @@ -429,6 +450,12 @@ int register_android_view_InputWindowHandle(JNIEnv* env) { GET_FIELD_ID(gInputWindowHandleClassInfo.replaceTouchableRegionWithCrop, clazz, "replaceTouchableRegionWithCrop", "Z"); + GET_FIELD_ID(gInputWindowHandleClassInfo.transform, clazz, "transform", + "Landroid/graphics/Matrix;"); + + GET_FIELD_ID(gInputWindowHandleClassInfo.windowToken, clazz, "windowToken", + "Landroid/os/IBinder;"); + jclass weakRefClazz; FIND_CLASS(weakRefClazz, "java/lang/ref/Reference"); diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp index e93b00d7b148..86d781033e5e 100644 --- a/core/jni/android_util_AssetManager.cpp +++ b/core/jni/android_util_AssetManager.cpp @@ -92,6 +92,7 @@ static struct configuration_offsets_t { jfieldID mSmallestScreenWidthDpOffset; jfieldID mScreenWidthDpOffset; jfieldID mScreenHeightDpOffset; + jfieldID mScreenLayoutOffset; } gConfigurationOffsets; static struct arraymap_offsets_t { @@ -1019,6 +1020,7 @@ static jobject ConstructConfigurationObject(JNIEnv* env, const ResTable_config& config.smallestScreenWidthDp); env->SetIntField(result, gConfigurationOffsets.mScreenWidthDpOffset, config.screenWidthDp); env->SetIntField(result, gConfigurationOffsets.mScreenHeightDpOffset, config.screenHeightDp); + env->SetIntField(result, gConfigurationOffsets.mScreenLayoutOffset, config.screenLayout); return result; } @@ -1553,6 +1555,8 @@ int register_android_content_AssetManager(JNIEnv* env) { GetFieldIDOrDie(env, configurationClass, "screenWidthDp", "I"); gConfigurationOffsets.mScreenHeightDpOffset = GetFieldIDOrDie(env, configurationClass, "screenHeightDp", "I"); + gConfigurationOffsets.mScreenLayoutOffset = + GetFieldIDOrDie(env, configurationClass, "screenLayout", "I"); jclass arrayMapClass = FindClassOrDie(env, "android/util/ArrayMap"); gArrayMapOffsets.classObject = MakeGlobalRefOrDie(env, arrayMapClass); diff --git a/core/proto/android/providers/settings/global.proto b/core/proto/android/providers/settings/global.proto index c3d159659622..6bc00e2281ef 100644 --- a/core/proto/android/providers/settings/global.proto +++ b/core/proto/android/providers/settings/global.proto @@ -771,6 +771,8 @@ message GlobalSettingsProto { optional SettingProto power_manager_constants = 93; reserved 94; // Used to be priv_app_oob_enabled + optional SettingProto power_button_long_press_duration_ms = 154 [ (android.privacy).dest = DEST_AUTOMATIC ]; + message PrepaidSetup { option (android.msg_privacy).dest = DEST_EXPLICIT; @@ -1063,5 +1065,5 @@ message GlobalSettingsProto { // Please insert fields in alphabetical order and group them into messages // if possible (to avoid reaching the method limit). - // Next tag = 154; + // Next tag = 155; } diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto index 1bba12ff7fa6..ba4a5b0ce222 100644 --- a/core/proto/android/providers/settings/secure.proto +++ b/core/proto/android/providers/settings/secure.proto @@ -79,7 +79,7 @@ message SecureSettingsProto { optional SettingProto accessibility_magnification_mode = 34 [ (android.privacy).dest = DEST_AUTOMATIC ]; optional SettingProto button_targets = 35 [ (android.privacy).dest = DEST_AUTOMATIC ]; optional SettingProto accessibility_magnification_capability = 36 [ (android.privacy).dest = DEST_AUTOMATIC ]; - // Settings for accessibility button mode (navigation bar or floating action menu). + // Settings for accessibility button related config optional SettingProto accessibility_button_mode = 37 [ (android.privacy).dest = DEST_AUTOMATIC ]; optional SettingProto accessibility_floating_menu_size = 38 [ (android.privacy).dest = DEST_AUTOMATIC ]; optional SettingProto accessibility_floating_menu_icon_type = 39 [ (android.privacy).dest = DEST_AUTOMATIC ]; diff --git a/core/res/res/drawable/btn_notification_emphasized.xml b/core/res/res/drawable/btn_notification_emphasized.xml index 29c51f2a33c9..7c09fb889c48 100644 --- a/core/res/res/drawable/btn_notification_emphasized.xml +++ b/core/res/res/drawable/btn_notification_emphasized.xml @@ -24,9 +24,9 @@ android:insetBottom="@dimen/button_inset_vertical_material"> <shape android:shape="rectangle"> <corners android:radius="@dimen/notification_action_button_radius" /> - <padding android:left="12dp" + <padding android:left="16dp" android:top="@dimen/button_padding_vertical_material" - android:right="12dp" + android:right="16dp" android:bottom="@dimen/button_padding_vertical_material" /> <solid android:color="@color/white" /> </shape> diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml index 0310b181398d..14221dc5fb6b 100644 --- a/core/res/res/values-ar/strings.xml +++ b/core/res/res/values-ar/strings.xml @@ -341,7 +341,7 @@ <string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"استرداد محتوى النافذة"</string> <string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"فحص محتوى نافذة يتم التفاعل معها"</string> <string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"تفعيل الاستكشاف باللمس"</string> - <string name="capability_desc_canRequestTouchExploration" msgid="4394677060796752976">"سيتم قول العناصر التي تم النقر عليها بصوت عال ويمكن استكشاف الشاشة باستخدام الإيماءات."</string> + <string name="capability_desc_canRequestTouchExploration" msgid="4394677060796752976">"سيتم قول العناصر التي تم النقر عليها بصوت عالٍ ويمكن استكشاف الشاشة باستخدام الإيماءات."</string> <string name="capability_title_canRequestFilterKeyEvents" msgid="2772371671541753254">"ملاحظة النص الذي تكتبه"</string> <string name="capability_desc_canRequestFilterKeyEvents" msgid="2381315802405773092">"يتضمن بيانات شخصية مثل أرقام بطاقات الائتمان وكلمات المرور."</string> <string name="capability_title_canControlMagnification" msgid="7701572187333415795">"التحكم في تكبير الشاشة"</string> diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml index 61fb4710b20c..a8ae213b6180 100644 --- a/core/res/res/values-cs/strings.xml +++ b/core/res/res/values-cs/strings.xml @@ -1369,9 +1369,9 @@ <string name="sms_control_message" msgid="6574313876316388239">"Aplikace <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>odesílá velký počet SMS zpráv. Chcete aplikaci povolit, aby zprávy odesílala i nadále?"</string> <string name="sms_control_yes" msgid="4858845109269524622">"Povolit"</string> <string name="sms_control_no" msgid="4845717880040355570">"Odmítnout"</string> - <string name="sms_short_code_confirm_message" msgid="1385416688897538724">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> chce odeslat zprávu na adresu <b><xliff:g id="DEST_ADDRESS">%2$s</xliff:g></b>."</string> + <string name="sms_short_code_confirm_message" msgid="1385416688897538724">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> chce odeslat zprávu na číslo <b><xliff:g id="DEST_ADDRESS">%2$s</xliff:g></b>."</string> <string name="sms_short_code_details" msgid="2723725738333388351">"Tato akce "<b>"může vést k naúčtování poplatků"</b>" na váš účet u mobilního operátora."</string> - <string name="sms_premium_short_code_details" msgid="1400296309866638111"><b>"Tato akce povede k naúčtování poplatku na váš účet u mobilního operátora."</b></string> + <string name="sms_premium_short_code_details" msgid="1400296309866638111"><b>"Tato akce může vést k naúčtování ceny služby třetí strany na vrub vašeho účtu u mobilního operátora."</b></string> <string name="sms_short_code_confirm_allow" msgid="920477594325526691">"Odeslat"</string> <string name="sms_short_code_confirm_deny" msgid="1356917469323768230">"Zrušit"</string> <string name="sms_short_code_remember_choice" msgid="1374526438647744862">"Zapamatovat moji volbu"</string> diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml index 17382c453f7f..8ae4bc742d40 100644 --- a/core/res/res/values-in/strings.xml +++ b/core/res/res/values-in/strings.xml @@ -326,12 +326,12 @@ <string name="permgroupdesc_phone" msgid="270048070781478204">"melakukan dan mengelola panggilan telepon"</string> <string name="permgrouplab_sensors" msgid="9134046949784064495">"Sensor tubuh"</string> <string name="permgroupdesc_sensors" msgid="2610631290633747752">"mengakses data sensor tentang tanda-tanda vital"</string> - <string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"Mengambil konten jendela"</string> - <string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"Memeriksa konten jendela tempat Anda berinteraksi."</string> + <string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"Membaca konten di jendela"</string> + <string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"Memeriksa konten di jendela yang sedang Anda buka."</string> <string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"Mengaktifkan Jelajahi dengan Sentuhan"</string> <string name="capability_desc_canRequestTouchExploration" msgid="4394677060796752976">"Item yang diketuk akan diucapkan dengan jelas dan layar dapat dijelajahi menggunakan gestur."</string> <string name="capability_title_canRequestFilterKeyEvents" msgid="2772371671541753254">"Mengamati teks yang Anda ketik"</string> - <string name="capability_desc_canRequestFilterKeyEvents" msgid="2381315802405773092">"Meliputi data pribadi seperti nomor kartu kredit dan sandi."</string> + <string name="capability_desc_canRequestFilterKeyEvents" msgid="2381315802405773092">"Termasuk data pribadi, seperti nomor kartu kredit dan sandi."</string> <string name="capability_title_canControlMagnification" msgid="7701572187333415795">"Mengontrol perbesaran layar"</string> <string name="capability_desc_canControlMagnification" msgid="2206586716709254805">"Mengontrol tingkat zoom dan pemosisian layar."</string> <string name="capability_title_canPerformGestures" msgid="9106545062106728987">"Melakukan isyarat"</string> diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml index 6919a3dc6c7c..316514e6cc29 100644 --- a/core/res/res/values-it/strings.xml +++ b/core/res/res/values-it/strings.xml @@ -327,13 +327,13 @@ <string name="permgrouplab_sensors" msgid="9134046949784064495">"Sensori del corpo"</string> <string name="permgroupdesc_sensors" msgid="2610631290633747752">"accedere ai dati dei sensori relativi ai tuoi parametri vitali"</string> <string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"Recuperare contenuti della finestra"</string> - <string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"Esaminare i contenuti di una finestra con cui interagisci."</string> + <string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"Esamina i contenuti di una finestra con cui interagisci."</string> <string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"Attivare Esplora al tocco"</string> - <string name="capability_desc_canRequestTouchExploration" msgid="4394677060796752976">"Gli elementi toccati verranno pronunciati ad alta voce e sarà possibile esplorare lo schermo utilizzando i gesti."</string> + <string name="capability_desc_canRequestTouchExploration" msgid="4394677060796752976">"Gli elementi toccati verranno pronunciati ad alta voce e sarà possibile esplorare lo schermo con i gesti."</string> <string name="capability_title_canRequestFilterKeyEvents" msgid="2772371671541753254">"Osservare il testo digitato"</string> <string name="capability_desc_canRequestFilterKeyEvents" msgid="2381315802405773092">"Sono inclusi dati personali come numeri di carte di credito e password."</string> <string name="capability_title_canControlMagnification" msgid="7701572187333415795">"Controllare l\'ingrandimento del display"</string> - <string name="capability_desc_canControlMagnification" msgid="2206586716709254805">"Controlla il livello di zoom e la posizione del display."</string> + <string name="capability_desc_canControlMagnification" msgid="2206586716709254805">"Controlla la posizione e il livello di zoom del display."</string> <string name="capability_title_canPerformGestures" msgid="9106545062106728987">"Eseguire gesti"</string> <string name="capability_desc_canPerformGestures" msgid="6619457251067929726">"Consente di toccare, far scorrere, pizzicare ed eseguire altri gesti."</string> <string name="capability_title_canCaptureFingerprintGestures" msgid="1189053104594608091">"Gesti con sensore di impronte"</string> diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml index 28522d7e564c..1747bdb68515 100644 --- a/core/res/res/values-mr/strings.xml +++ b/core/res/res/values-mr/strings.xml @@ -471,7 +471,7 @@ <string name="permdesc_readPhoneState" msgid="7229063553502788058">"डिव्हाइस च्या फोन वैशिष्ट्यांवर अॅक्सेस करण्यास ॲपला अनुमती देते. ही परवानगी कॉल ॲक्टिव्हेट असला किंवा नसला तरीही, फोन नंबर आणि डिव्हाइस आयडी आणि कॉलद्वारे कनेक्ट केलेला रिमोट नंबर निर्धारित करण्यासाठी ॲपला अनुमती देते."</string> <string name="permlab_manageOwnCalls" msgid="9033349060307561370">"प्रणालीच्या माध्यमातून कॉल रूट करा"</string> <string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"कॉल करण्याचा अनुभव सुधारण्यासाठी ॲपला त्याचे कॉल प्रणालीच्या माध्यमातून रूट करू देते."</string> - <string name="permlab_callCompanionApp" msgid="3654373653014126884">"सिस्टम वापरून कॉल पाहा आणि नियंत्रण ठेवा."</string> + <string name="permlab_callCompanionApp" msgid="3654373653014126884">"सिस्टम वापरून कॉल पहा आणि नियंत्रण ठेवा."</string> <string name="permdesc_callCompanionApp" msgid="8474168926184156261">"डिव्हाइसवर येणार कॉल पाहण्यासाठी आणि नियंत्रित करण्यासाठी ॲपला अनुमती देते. यामध्ये कॉल करण्यासाठी कॉलचा नंबर आणि कॉलची स्थिती यासारख्या माहितीचा समावेश असतो."</string> <string name="permlab_exemptFromAudioRecordRestrictions" msgid="1164725468350759486">"ऑडिओ रेकॉर्ड प्रतिबंधांपासून मुक्त"</string> <string name="permdesc_exemptFromAudioRecordRestrictions" msgid="2425117015896871976">"ऑडिओ रेकॉर्ड करण्यासाठी प्रतिबंधांपासून ॲपला मुक्त करा."</string> @@ -581,7 +581,7 @@ <string name="fingerprint_acquired_imager_dirty" msgid="5236744087471419479">"सेन्सर साफ करा"</string> <string name="fingerprint_acquired_too_fast" msgid="6038375140739678098">"जरा जास्त वेळ धरून ठेवा"</string> <string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"बोट खूप सावकाश हलविले. कृपया पुन्हा प्रयत्न करा."</string> - <string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"दुसरी फिंगरप्रिंट वापरून पाहा"</string> + <string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"दुसरी फिंगरप्रिंट वापरून पहा"</string> <string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"खूप प्रखर"</string> <string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"अॅडजस्ट करण्याचा प्रयत्न करा"</string> <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"तुमच्या बोटाची स्थिती प्रत्येक वेळी थोडीशी बदला"</string> @@ -629,7 +629,7 @@ <string name="face_acquired_too_low" msgid="1512237819632165945">"फोन आणखी खाली हलवा."</string> <string name="face_acquired_too_right" msgid="2513391513020932655">"फोन डावीकडे हलवा."</string> <string name="face_acquired_too_left" msgid="8882499346502714350">"फोन उजवीकडे हलवा."</string> - <string name="face_acquired_poor_gaze" msgid="4427153558773628020">"कृपया तुमच्या डिव्हाइसकडे थेट पाहा"</string> + <string name="face_acquired_poor_gaze" msgid="4427153558773628020">"कृपया तुमच्या डिव्हाइसकडे थेट पहा"</string> <string name="face_acquired_not_detected" msgid="2945945257956443257">"तुमचा चेहरा थेट फोन समोर आणा."</string> <string name="face_acquired_too_much_motion" msgid="8199691445085189528">"खूप हलत आहे. फोन स्थिर धरून ठेवा."</string> <string name="face_acquired_recalibrate" msgid="8724013080976469746">"कृपया तुमच्या चेहऱ्याची पुन्हा नोंदणी करा."</string> @@ -1701,9 +1701,9 @@ <string name="accessibility_enable_service_title" msgid="3931558336268541484">"<xliff:g id="SERVICE">%1$s</xliff:g> ला तुमचे डिव्हाइसच संपूर्णपणे नियंत्रित करायची अनुमती द्यायची का?"</string> <string name="accessibility_enable_service_encryption_warning" msgid="8603532708618236909">"तुम्ही <xliff:g id="SERVICE">%1$s</xliff:g> सुरू केल्यास, तुमचे डिव्हाइस डेटा एंक्रिप्शनमध्ये सुधारणा करण्यासाठी स्क्रीन लॉक वापरणार नाही."</string> <string name="accessibility_service_warning_description" msgid="291674995220940133">"जी ॲप्स तुमच्या ॲक्सेसिबिलिटी गरजा पूर्ण करतात अशा ॲप्ससाठी संपूर्ण नियंत्रण योग्य आहे. पण ते सर्व ॲप्सना लागू होईल असे नाही."</string> - <string name="accessibility_service_screen_control_title" msgid="190017412626919776">"स्क्रीन पाहा आणि नियंत्रित करा"</string> + <string name="accessibility_service_screen_control_title" msgid="190017412626919776">"स्क्रीन पहा आणि नियंत्रित करा"</string> <string name="accessibility_service_screen_control_description" msgid="6946315917771791525">"ते स्क्रीनवरील सर्व आशय वाचू शकते आणि इतर ॲप्सवर आशय प्रदर्शित करू शकते."</string> - <string name="accessibility_service_action_perform_title" msgid="779670378951658160">"पाहा आणि क्रिया करा"</string> + <string name="accessibility_service_action_perform_title" msgid="779670378951658160">"पहा आणि क्रिया करा"</string> <string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"तुम्ही ॲप किंवा हार्डवेअर सेन्सर कसे वापरता याचा हे मागोवा घेऊ शकते आणि इतर ॲप्ससोबत तुमच्या वतीने काम करू शकते."</string> <string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"अनुमती द्या"</string> <string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"नकार द्या"</string> @@ -1982,7 +1982,7 @@ <string name="work_mode_turn_on" msgid="3662561662475962285">"सुरू करा"</string> <string name="app_blocked_title" msgid="7353262160455028160">"ॲप उपलब्ध नाही"</string> <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> आता उपलब्ध नाही."</string> - <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"हे अॅप Android च्या जुन्या आवृत्ती साठी तयार करण्यात आले होते आणि योग्यरितीने कार्य करू शकणार नाही. अपडेट आहेत का ते तपासून पाहा, किंवा डेव्हलपरशी संपर्क साधण्याचा प्रयत्न करा."</string> + <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"हे अॅप Android च्या जुन्या आवृत्ती साठी तयार करण्यात आले होते आणि योग्यरितीने कार्य करू शकणार नाही. अपडेट आहेत का ते तपासून पहा, किंवा डेव्हलपरशी संपर्क साधण्याचा प्रयत्न करा."</string> <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"अपडेटसाठी तपासा"</string> <string name="new_sms_notification_title" msgid="6528758221319927107">"आपल्याकडे नवीन मेसेज आहेत"</string> <string name="new_sms_notification_content" msgid="3197949934153460639">"पाहण्यासाठी SMS अॅप उघडा"</string> diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml index c45ac41462ae..b72b0000ce00 100644 --- a/core/res/res/values-pl/strings.xml +++ b/core/res/res/values-pl/strings.xml @@ -333,7 +333,7 @@ <string name="permgrouplab_sensors" msgid="9134046949784064495">"Czujniki na ciele"</string> <string name="permgroupdesc_sensors" msgid="2610631290633747752">"dostęp do danych czujnika podstawowych funkcji życiowych"</string> <string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"Pobieranie zawartości okna"</string> - <string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"Sprawdzanie zawartości okna, z którego korzystasz."</string> + <string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"Sprawdzanie zawartości okna, z którego korzystasz."</string> <string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"Włączenie czytania dotykiem"</string> <string name="capability_desc_canRequestTouchExploration" msgid="4394677060796752976">"Klikane elementy będą wymawiane na głos, a ekran można przeglądać, używając gestów."</string> <string name="capability_title_canRequestFilterKeyEvents" msgid="2772371671541753254">"Obserwowanie wpisywanego tekstu"</string> diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml index 18b588cd395c..4a5be62120f6 100644 --- a/core/res/res/values-ta/strings.xml +++ b/core/res/res/values-ta/strings.xml @@ -169,7 +169,7 @@ <string name="httpErrorUnsupportedScheme" msgid="2664108769858966374">"நெறிமுறை ஆதரிக்கப்படவில்லை."</string> <string name="httpErrorFailedSslHandshake" msgid="546319061228876290">"பாதுகாப்பான இணைப்பை நிறுவ முடியவில்லை."</string> <string name="httpErrorBadUrl" msgid="754447723314832538">"URL தவறாக உள்ளதால் பக்கத்தைத் திறக்க முடியவில்லை."</string> - <string name="httpErrorFile" msgid="3400658466057744084">"கோப்பை அணுக முடியவில்லை."</string> + <string name="httpErrorFile" msgid="3400658466057744084">"ஃபைலை அணுக முடியவில்லை."</string> <string name="httpErrorFileNotFound" msgid="5191433324871147386">"கோரப்பட்ட கோப்பைக் கண்டறிய முடியவில்லை."</string> <string name="httpErrorTooManyRequests" msgid="2149677715552037198">"மிக அதிகமான கோரிக்கைகள் செயல்படுத்தப்படுகின்றன. பிறகு முயற்சிக்கவும்."</string> <string name="notification_title" msgid="5783748077084481121">"<xliff:g id="ACCOUNT">%1$s</xliff:g> க்கான உள்நுழைவு பிழை"</string> diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml index 1d2fd8a0885b..3e4f65c7f4d7 100644 --- a/core/res/res/values-te/strings.xml +++ b/core/res/res/values-te/strings.xml @@ -82,7 +82,7 @@ <string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"వాయిస్ సేవ లేదు"</string> <string name="RestrictedOnAllVoiceTitle" msgid="3982069078579103087">"వాయిస్ సేవ లేదా అత్యవసర కాలింగ్ లేదు"</string> <string name="RestrictedStateContent" msgid="7693575344608618926">"మీ క్యారియర్ తాత్కాలికంగా ఆఫ్ చేయబడింది"</string> - <string name="RestrictedStateContentMsimTemplate" msgid="5228235722511044687">"SIM <xliff:g id="SIMNUMBER">%d</xliff:g> కోసం మీ క్యారియర్ తాత్కాలికంగా ఆఫ్ చేసారు"</string> + <string name="RestrictedStateContentMsimTemplate" msgid="5228235722511044687">"SIM <xliff:g id="SIMNUMBER">%d</xliff:g> కోసం మీ క్యారియర్ తాత్కాలికంగా ఆఫ్ చేశారు"</string> <string name="NetworkPreferenceSwitchTitle" msgid="1008329951315753038">"మొబైల్ నెట్వర్క్ అందుబాటులో లేదు"</string> <string name="NetworkPreferenceSwitchSummary" msgid="2086506181486324860">"ప్రాధాన్య నెట్వర్క్ను మార్చుకోవడానికి ప్రయత్నించండి. మార్చడానికి నొక్కండి."</string> <string name="EmergencyCallWarningTitle" msgid="1615688002899152860">"అత్యవసర కాలింగ్ అందుబాటులో లేదు"</string> @@ -91,8 +91,8 @@ <string name="notification_channel_call_forward" msgid="8230490317314272406">"కాల్ ఫార్వార్డింగ్"</string> <string name="notification_channel_emergency_callback" msgid="54074839059123159">"అత్యవసర కాల్బ్యాక్ మోడ్"</string> <string name="notification_channel_mobile_data_status" msgid="1941911162076442474">"మొబైల్ డేటా స్థితి"</string> - <string name="notification_channel_sms" msgid="1243384981025535724">"SMS సందేశాలు"</string> - <string name="notification_channel_voice_mail" msgid="8457433203106654172">"వాయిస్ మెయిల్ సందేశాలు"</string> + <string name="notification_channel_sms" msgid="1243384981025535724">"SMS మెసేజ్లు"</string> + <string name="notification_channel_voice_mail" msgid="8457433203106654172">"వాయిస్ మెయిల్ మెసేజ్లు"</string> <string name="notification_channel_wfc" msgid="9048240466765169038">"Wi-Fi కాలింగ్"</string> <string name="notification_channel_sim" msgid="5098802350325677490">"SIM స్టేటస్"</string> <string name="notification_channel_sim_high_prio" msgid="642361929452850928">"అధిక ప్రాధాన్యత గల SIM స్థితి"</string> @@ -105,7 +105,7 @@ <string name="serviceClassFAX" msgid="2561653371698904118">"ఫ్యాక్స్"</string> <string name="serviceClassSMS" msgid="1547664561704509004">"SMS"</string> <string name="serviceClassDataAsync" msgid="2029856900898545984">"నిరర్థకం"</string> - <string name="serviceClassDataSync" msgid="7895071363569133704">"సమకాలీకరణ"</string> + <string name="serviceClassDataSync" msgid="7895071363569133704">"సింక్"</string> <string name="serviceClassPacket" msgid="1430642951399303804">"ప్యాకెట్"</string> <string name="serviceClassPAD" msgid="6850244583416306321">"PAD"</string> <string name="roamingText0" msgid="7793257871609854208">"రోమింగ్ సూచిక ఆన్లో ఉంది"</string> @@ -124,7 +124,7 @@ <string name="roamingTextSearching" msgid="5323235489657753486">"సేవ కోసం శోధిస్తోంది"</string> <string name="wfcRegErrorTitle" msgid="3193072971584858020">"Wi‑Fi కాలింగ్ని సెటప్ చేయడం సాధ్యపడలేదు"</string> <string-array name="wfcOperatorErrorAlertMessages"> - <item msgid="468830943567116703">"Wi-Fiతో కాల్స్ను చేయడానికి మరియు సందేశాలను పంపించడానికి, మొదట ఈ సేవను సెటప్ చేయాల్సిందిగా మీ క్యారియర్కి చెప్పండి. ఆ తర్వాత సెట్టింగ్ల నుండి Wi-Fi కాలింగ్ని మళ్లీ ఆన్ చేయండి. (లోపం కోడ్: <xliff:g id="CODE">%1$s</xliff:g>)"</item> + <item msgid="468830943567116703">"Wi-Fiతో కాల్స్ను చేయడానికి మరియు మెసేజ్లను పంపించడానికి, మొదట ఈ సేవను సెటప్ చేయాల్సిందిగా మీ క్యారియర్కి చెప్పండి. ఆ తర్వాత సెట్టింగ్ల నుండి Wi-Fi కాలింగ్ని మళ్లీ ఆన్ చేయండి. (లోపం కోడ్: <xliff:g id="CODE">%1$s</xliff:g>)"</item> </string-array> <string-array name="wfcOperatorErrorNotificationMessages"> <item msgid="4795145070505729156">"మీ క్యారియర్తో Wi‑Fi కాలింగ్ని నమోదు చేయడంలో సమస్య: <xliff:g id="CODE">%1$s</xliff:g>"</item> @@ -171,9 +171,9 @@ <string name="httpErrorBadUrl" msgid="754447723314832538">"URL చెల్లనిది అయినందువలన పేజీని తెరవడం సాధ్యపడలేదు."</string> <string name="httpErrorFile" msgid="3400658466057744084">"ఫైల్ను యాక్సెస్ చేయడం సాధ్యపడలేదు."</string> <string name="httpErrorFileNotFound" msgid="5191433324871147386">"అభ్యర్థించిన ఫైల్ను కనుగొనడం సాధ్యపడలేదు."</string> - <string name="httpErrorTooManyRequests" msgid="2149677715552037198">"చాలా ఎక్కువ అభ్యర్థనలు ప్రాసెస్ చేయబడుతున్నాయి. తర్వాత మళ్లీ ప్రయత్నించండి."</string> + <string name="httpErrorTooManyRequests" msgid="2149677715552037198">"చాలా ఎక్కువ రిక్వెస్ట్లు ప్రాసెస్ చేయబడుతున్నాయి. తర్వాత మళ్లీ ప్రయత్నించండి."</string> <string name="notification_title" msgid="5783748077084481121">"<xliff:g id="ACCOUNT">%1$s</xliff:g>కు సైన్ఇన్ ఎర్రర్"</string> - <string name="contentServiceSync" msgid="2341041749565687871">"సమకాలీకరణ"</string> + <string name="contentServiceSync" msgid="2341041749565687871">"సింక్"</string> <string name="contentServiceSyncNotificationTitle" msgid="5766411446676388623">"సమకాలీకరించడం సాధ్యపడదు"</string> <string name="contentServiceTooManyDeletesNotificationDesc" msgid="4562226280528716090">"చాలా ఎక్కువ <xliff:g id="CONTENT_TYPE">%s</xliff:g> తొలగించడానికి ప్రయత్నించారు."</string> <string name="low_memory" product="tablet" msgid="5557552311566179924">"టాబ్లెట్ నిల్వ నిండింది. స్థలాన్ని ఖాళీ చేయడానికి కొన్ని ఫైళ్లను తొలగించండి."</string> @@ -190,7 +190,7 @@ <string name="work_profile_deleted" msgid="5891181538182009328">"కార్యాలయ ప్రొఫైల్ తొలగించబడింది"</string> <string name="work_profile_deleted_details" msgid="3773706828364418016">"కార్యాలయ ప్రొఫైల్ నిర్వాహక యాప్ లేదు లేదా పాడైంది. తత్ఫలితంగా, మీ కార్యాలయ ప్రొఫైల్ మరియు సంబంధిత డేటా తొలగించబడ్డాయి. సహాయం కోసం మీ నిర్వాహకులను సంప్రదించండి."</string> <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"ఈ పరికరంలో మీ కార్యాలయ ప్రొఫైల్ ఇప్పుడు అందుబాటులో లేదు"</string> - <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"చాలా ఎక్కువ పాస్వర్డ్ ప్రయత్నాలు చేసారు"</string> + <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"చాలా ఎక్కువ పాస్వర్డ్ ప్రయత్నాలు చేశారు"</string> <string name="device_ownership_relinquished" msgid="4080886992183195724">"వ్యక్తిగత వినియోగం కోసం నిర్వాహకులు పరికరాన్ని తీసి వేశారు"</string> <string name="network_logging_notification_title" msgid="554983187553845004">"పరికరం నిర్వహించబడింది"</string> <string name="network_logging_notification_text" msgid="1327373071132562512">"మీ సంస్థ ఈ పరికరాన్ని నిర్వహిస్తుంది మరియు నెట్వర్క్ ట్రాఫిక్ని పర్యవేక్షించవచ్చు. వివరాల కోసం నొక్కండి."</string> @@ -251,7 +251,7 @@ <string name="global_action_logout" msgid="6093581310002476511">"సెషన్ను ముగించు"</string> <string name="global_action_screenshot" msgid="2610053466156478564">"స్క్రీన్షాట్"</string> <string name="bugreport_title" msgid="8549990811777373050">"బగ్ రిపోర్ట్"</string> - <string name="bugreport_message" msgid="5212529146119624326">"ఇది ఇ-మెయిల్ సందేశం రూపంలో పంపడానికి మీ ప్రస్తుత పరికర స్థితి గురించి సమాచారాన్ని సేకరిస్తుంది. బగ్ రిపోర్ట్ను ప్రారంభించడం మొదలుకొని పంపడానికి సిద్ధం చేసే వరకు ఇందుకు కొంత సమయం పడుతుంది; దయచేసి ఓపిక పట్టండి."</string> + <string name="bugreport_message" msgid="5212529146119624326">"ఇది ఇ-మెయిల్ మెసేజ్ రూపంలో పంపడానికి మీ ప్రస్తుత పరికర స్థితి గురించి సమాచారాన్ని సేకరిస్తుంది. బగ్ రిపోర్ట్ను ప్రారంభించడం మొదలుకొని పంపడానికి సిద్ధం చేసే వరకు ఇందుకు కొంత సమయం పడుతుంది; దయచేసి ఓపిక పట్టండి."</string> <string name="bugreport_option_interactive_title" msgid="7968287837902871289">"ప్రభావశీల రిపోర్ట్"</string> <string name="bugreport_option_interactive_summary" msgid="8493795476325339542">"చాలా సందర్భాల్లో దీన్ని ఉపయోగించండి. ఇది రిపోర్ట్ ప్రోగ్రెస్ను ట్రాక్ చేయడానికి, సమస్య గురించి మరిన్ని వివరాలను నమోదు చేయడానికి మరియు స్క్రీన్షాట్లు తీయడానికి మిమ్మల్ని అనుమతిస్తుంది. ఇది నివేదించడానికి ఎక్కువ సమయం పట్టే తక్కువ వినియోగ విభాగాలను విడిచిపెట్టవచ్చు."</string> <string name="bugreport_option_full_title" msgid="7681035745950045690">"పూర్తి రిపోర్ట్"</string> @@ -279,8 +279,8 @@ <string name="notification_channel_security" msgid="8516754650348238057">"సెక్యూరిటీ"</string> <string name="notification_channel_car_mode" msgid="2123919247040988436">"కార్ మోడ్"</string> <string name="notification_channel_account" msgid="6436294521740148173">"ఖాతా స్థితి"</string> - <string name="notification_channel_developer" msgid="1691059964407549150">"డెవలపర్ సందేశాలు"</string> - <string name="notification_channel_developer_important" msgid="7197281908918789589">"ముఖ్యమైన డెవలపర్ సందేశాలు"</string> + <string name="notification_channel_developer" msgid="1691059964407549150">"డెవలపర్ మెసేజ్లు"</string> + <string name="notification_channel_developer_important" msgid="7197281908918789589">"ముఖ్యమైన డెవలపర్ మెసేజ్లు"</string> <string name="notification_channel_updates" msgid="7907863984825495278">"అప్డేట్లు"</string> <string name="notification_channel_network_status" msgid="2127687368725272809">"నెట్వర్క్ స్థితి"</string> <string name="notification_channel_network_alerts" msgid="6312366315654526528">"నెట్వర్క్ హెచ్చరికలు"</string> @@ -309,13 +309,13 @@ <string name="permgrouplab_calendar" msgid="6426860926123033230">"Calendar"</string> <string name="permgroupdesc_calendar" msgid="6762751063361489379">"మీ క్యాలెండర్ను యాక్సెస్ చేయడానికి"</string> <string name="permgrouplab_sms" msgid="795737735126084874">"SMS"</string> - <string name="permgroupdesc_sms" msgid="5726462398070064542">"SMS సందేశాలను పంపడం మరియు వీక్షించడం"</string> + <string name="permgroupdesc_sms" msgid="5726462398070064542">"SMS మెసేజ్లను పంపడం మరియు వీక్షించడం"</string> <string name="permgrouplab_storage" msgid="1938416135375282333">"ఫైల్స్, మీడియా"</string> <string name="permgroupdesc_storage" msgid="6351503740613026600">"మీ పరికరంలోని ఫోటోలు, మీడియా మరియు ఫైళ్లను యాక్సెస్ చేయడానికి"</string> <string name="permgrouplab_microphone" msgid="2480597427667420076">"మైక్రోఫోన్"</string> <string name="permgroupdesc_microphone" msgid="1047786732792487722">"ఆడియోను రికార్డ్ చేయడానికి"</string> <string name="permgrouplab_activityRecognition" msgid="3324466667921775766">"ఫిజికల్ యాక్టివిటీ"</string> - <string name="permgroupdesc_activityRecognition" msgid="4725624819457670704">"భౌతిక కార్యకలాపాన్ని యాక్సెస్ చేయండి"</string> + <string name="permgroupdesc_activityRecognition" msgid="4725624819457670704">"భౌతిక యాక్టివిటీని యాక్సెస్ చేయండి"</string> <string name="permgrouplab_camera" msgid="9090413408963547706">"కెమెరా"</string> <string name="permgroupdesc_camera" msgid="7585150538459320326">"చిత్రాలను తీయడానికి మరియు వీడియోను రికార్డ్ చేయడానికి"</string> <string name="permgrouplab_nearby_devices" msgid="5529147543651181991">"సమీపంలోని పరికరాలు"</string> @@ -349,33 +349,33 @@ <string name="permlab_fullScreenIntent" msgid="4310888199502509104">"లాక్ చేసి ఉన్న పరికరంలో నోటిఫికేషన్లను ఫుల్ స్క్రీన్ యాక్టివిటీలుగా డిస్ప్లే చేస్తుంది"</string> <string name="permdesc_fullScreenIntent" msgid="1100721419406643997">"లాక్ చేసి ఉన్న పరికరంలో నోటిఫికేషన్లను ఫుల్ స్క్రీన్ యాక్టివిటీలుగా డిస్ప్లే చేయడానికి యాప్ను అనుమతిస్తుంది"</string> <string name="permlab_install_shortcut" msgid="7451554307502256221">"షార్ట్కట్లను ఇన్స్టాల్ చేయడం"</string> - <string name="permdesc_install_shortcut" msgid="4476328467240212503">"వినియోగదారు ప్రమేయం లేకుండానే హోమ్స్క్రీన్ సత్వరమార్గాలను జోడించడానికి యాప్ను అనుమతిస్తుంది."</string> - <string name="permlab_uninstall_shortcut" msgid="295263654781900390">"సత్వరమార్గాలను అన్ఇన్స్టాల్ చేయడం"</string> - <string name="permdesc_uninstall_shortcut" msgid="1924735350988629188">"వినియోగదారు ప్రమేయం లేకుండానే హోమ్స్క్రీన్ సత్వరమార్గాలను తీసివేయడానికి యాప్ను అనుమతిస్తుంది."</string> + <string name="permdesc_install_shortcut" msgid="4476328467240212503">"వినియోగదారు ప్రమేయం లేకుండానే హోమ్స్క్రీన్ షార్ట్కట్లను జోడించడానికి యాప్ను అనుమతిస్తుంది."</string> + <string name="permlab_uninstall_shortcut" msgid="295263654781900390">"షార్ట్కట్లను అన్ఇన్స్టాల్ చేయడం"</string> + <string name="permdesc_uninstall_shortcut" msgid="1924735350988629188">"వినియోగదారు ప్రమేయం లేకుండానే హోమ్స్క్రీన్ షార్ట్కట్లను తీసివేయడానికి యాప్ను అనుమతిస్తుంది."</string> <string name="permlab_processOutgoingCalls" msgid="4075056020714266558">"అవుట్గోయింగ్ కాల్స్ను దారి మళ్లించడం"</string> <string name="permdesc_processOutgoingCalls" msgid="7833149750590606334">"కాల్ను వేరే నంబర్కు దారి మళ్లించే లేదా మొత్తంగా కాల్ను ఆపివేసే ఎంపిక సహాయంతో అవుట్గోయింగ్ కాల్ సమయంలో డయల్ చేయబడుతున్న నంబర్ను చూడటానికి యాప్ను అనుమతిస్తుంది."</string> <string name="permlab_answerPhoneCalls" msgid="4131324833663725855">"ఫోన్ కాల్స్కు సమాధానమివ్వు"</string> <string name="permdesc_answerPhoneCalls" msgid="894386681983116838">"ఇన్కమింగ్ ఫోన్ కాల్స్కు సమాధానమివ్వడానికి యాప్ను అనుమతిస్తుంది."</string> - <string name="permlab_receiveSms" msgid="505961632050451881">"వచన సందేశాలను (SMS) స్వీకరించడం"</string> - <string name="permdesc_receiveSms" msgid="1797345626687832285">"SMS సందేశాలను స్వీకరించడానికి మరియు ప్రాసెస్ చేయడానికి యాప్ను అనుమతిస్తుంది. యాప్ మీ డివైజ్కు పంపబడిన సందేశాలను మీకు చూపకుండానే పర్యవేక్షించగలదని లేదా తొలగించగలదని దీని అర్థం."</string> - <string name="permlab_receiveMms" msgid="4000650116674380275">"వచన సందేశాలను (MMS) స్వీకరించడం"</string> - <string name="permdesc_receiveMms" msgid="958102423732219710">"MMS సందేశాలను స్వీకరించడానికి మరియు ప్రాసెస్ చేయడానికి యాప్ను అనుమతిస్తుంది. యాప్ మీ డివైజ్కు పంపబడిన సందేశాలను మీకు చూపకుండానే పర్యవేక్షించగలదని లేదా తొలగించగలదని దీని అర్థం."</string> - <string name="permlab_bindCellBroadcastService" msgid="586746677002040651">"సెల్ ప్రసార సందేశాలను ఫార్వర్డ్ చేయడం"</string> - <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"సెల్ ప్రసార సందేశాలను అందుకుంటే, వాటిని ఫార్వర్డ్ చేసే విధంగా సెల్ ప్రసార మాడ్యూల్కు కట్టుబడి ఉండటానికి యాప్ను అనుమతిస్తుంది. సెల్ ప్రసార హెచ్చరికలు అత్యవసర పరిస్థితుల గురించి మిమ్మల్ని హెచ్చరించడానికి కొన్ని స్థానాల్లో అందించబడతాయి. అత్యవసర సెల్ ప్రసారం అందుకున్నప్పుడు హానికరమైన యాప్లు మీ పరికరం యొక్క పనితీరు లేదా నిర్వహణకు అంతరాయం కలిగించవచ్చు."</string> + <string name="permlab_receiveSms" msgid="505961632050451881">"వచన మెసేజ్లను (SMS) స్వీకరించడం"</string> + <string name="permdesc_receiveSms" msgid="1797345626687832285">"SMS మెసేజ్లను స్వీకరించడానికి మరియు ప్రాసెస్ చేయడానికి యాప్ను అనుమతిస్తుంది. యాప్ మీ డివైజ్కు పంపబడిన మెసేజ్లను మీకు చూపకుండానే పర్యవేక్షించగలదని లేదా తొలగించగలదని దీని అర్థం."</string> + <string name="permlab_receiveMms" msgid="4000650116674380275">"వచన మెసేజ్లను (MMS) స్వీకరించడం"</string> + <string name="permdesc_receiveMms" msgid="958102423732219710">"MMS మెసేజ్లను స్వీకరించడానికి మరియు ప్రాసెస్ చేయడానికి యాప్ను అనుమతిస్తుంది. యాప్ మీ డివైజ్కు పంపబడిన మెసేజ్లను మీకు చూపకుండానే పర్యవేక్షించగలదని లేదా తొలగించగలదని దీని అర్థం."</string> + <string name="permlab_bindCellBroadcastService" msgid="586746677002040651">"సెల్ ప్రసార మెసేజ్లను ఫార్వర్డ్ చేయడం"</string> + <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"సెల్ ప్రసార మెసేజ్లను అందుకుంటే, వాటిని ఫార్వర్డ్ చేసే విధంగా సెల్ ప్రసార మాడ్యూల్కు కట్టుబడి ఉండటానికి యాప్ను అనుమతిస్తుంది. సెల్ ప్రసార హెచ్చరికలు అత్యవసర పరిస్థితుల గురించి మిమ్మల్ని హెచ్చరించడానికి కొన్ని స్థానాల్లో అందించబడతాయి. అత్యవసర సెల్ ప్రసారం అందుకున్నప్పుడు హానికరమైన యాప్లు మీ పరికరం యొక్క పనితీరు లేదా నిర్వహణకు అంతరాయం కలిగించవచ్చు."</string> <string name="permlab_manageOngoingCalls" msgid="281244770664231782">"కొనసాగుతున్న కాల్స్ను మేనేజ్ చేయి"</string> <string name="permdesc_manageOngoingCalls" msgid="7003138133829915265">"మీ పరికరంలో కొనసాగుతున్న కాల్స్ను చూడటానికి అలాగే వాటిని కంట్రోల్ చేయడానికి ఒక యాప్కు అనుమతిస్తోంది."</string> - <string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"సెల్ ప్రసార సందేశాలను చదవడం"</string> - <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"మీ పరికరం స్వీకరించిన సెల్ ప్రసార సందేశాలను చదవడానికి యాప్ను అనుమతిస్తుంది. సెల్ ప్రసార హెచ్చరికలు అత్యవసర పరిస్థితుల గురించి మిమ్మల్ని హెచ్చరించడానికి కొన్ని స్థానాల్లో అందించబడతాయి. అత్యవసర సెల్ ప్రసారం స్వీకరించినప్పుడు హానికరమైన యాప్లు మీ పరికరం యొక్క పనితీరు లేదా నిర్వహణకు అంతరాయం కలిగించవచ్చు."</string> + <string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"సెల్ ప్రసార మెసేజ్లను చదవడం"</string> + <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"మీ పరికరం స్వీకరించిన సెల్ ప్రసార మెసేజ్లను చదవడానికి యాప్ను అనుమతిస్తుంది. సెల్ ప్రసార హెచ్చరికలు అత్యవసర పరిస్థితుల గురించి మిమ్మల్ని హెచ్చరించడానికి కొన్ని స్థానాల్లో అందించబడతాయి. అత్యవసర సెల్ ప్రసారం స్వీకరించినప్పుడు హానికరమైన యాప్లు మీ పరికరం యొక్క పనితీరు లేదా నిర్వహణకు అంతరాయం కలిగించవచ్చు."</string> <string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"చందా చేయబడిన ఫీడ్లను చదవడం"</string> <string name="permdesc_subscribedFeedsRead" msgid="6911349196661811865">"ప్రస్తుతం సమకాలీకరించిన ఫీడ్ల గురించి వివరాలను పొందడానికి యాప్ను అనుమతిస్తుంది."</string> - <string name="permlab_sendSms" msgid="7757368721742014252">"SMS సందేశాలను పంపడం మరియు వీక్షించడం"</string> - <string name="permdesc_sendSms" msgid="6757089798435130769">"SMS సందేశాలు పంపడానికి యాప్ను అనుమతిస్తుంది. దీని వలన ఊహించని ఛార్జీలు విధించబడవచ్చు. హానికరమైన యాప్లు మీ నిర్ధారణ లేకుండానే సందేశాలను పంపడం ద్వారా మీకు డబ్బు ఖర్చయ్యేలా చేయవచ్చు."</string> - <string name="permlab_readSms" msgid="5164176626258800297">"మీ వచన సందేశాలు (SMS లేదా MMS) చదవడం"</string> - <string name="permdesc_readSms" product="tablet" msgid="7912990447198112829">"ఈ యాప్ మీ టాబ్లెట్లో నిల్వ చేసిన అన్ని SMS (వచన) సందేశాలను చదవగలదు."</string> + <string name="permlab_sendSms" msgid="7757368721742014252">"SMS మెసేజ్లను పంపడం మరియు వీక్షించడం"</string> + <string name="permdesc_sendSms" msgid="6757089798435130769">"SMS మెసేజ్లు పంపడానికి యాప్ను అనుమతిస్తుంది. దీని వలన ఊహించని ఛార్జీలు విధించబడవచ్చు. హానికరమైన యాప్లు మీ నిర్ధారణ లేకుండానే మెసేజ్లను పంపడం ద్వారా మీకు డబ్బు ఖర్చయ్యేలా చేయవచ్చు."</string> + <string name="permlab_readSms" msgid="5164176626258800297">"మీ వచన మెసేజ్లు (SMS లేదా MMS) చదవడం"</string> + <string name="permdesc_readSms" product="tablet" msgid="7912990447198112829">"ఈ యాప్ మీ టాబ్లెట్లో నిల్వ చేసిన అన్ని SMS (వచన) మెసేజ్లను చదవగలదు."</string> <string name="permdesc_readSms" product="tv" msgid="3054753345758011986">"ఈ యాప్ మీ Android TV పరికరంలో నిల్వ అయిన SMS (వచనం) సందేశాలన్నింటినీ చదవగలదు."</string> - <string name="permdesc_readSms" product="default" msgid="774753371111699782">"ఈ యాప్ మీ ఫోన్లో నిల్వ చేసిన అన్ని SMS (వచన) సందేశాలను చదవగలదు."</string> - <string name="permlab_receiveWapPush" msgid="4223747702856929056">"వచన సందేశాలను (WAP) స్వీకరించడం"</string> - <string name="permdesc_receiveWapPush" msgid="1638677888301778457">"WAP సందేశాలను స్వీకరించడానికి మరియు ప్రాసెస్ చేయడానికి యాప్ను అనుమతిస్తుంది. ఈ అనుమతి మీకు పంపబడిన సందేశాలను మీకు చూపకుండానే పర్యవేక్షించగల లేదా తొలగించగల సామర్థ్యాన్ని కలిగి ఉంటుంది."</string> + <string name="permdesc_readSms" product="default" msgid="774753371111699782">"ఈ యాప్ మీ ఫోన్లో నిల్వ చేసిన అన్ని SMS (వచన) మెసేజ్లను చదవగలదు."</string> + <string name="permlab_receiveWapPush" msgid="4223747702856929056">"వచన మెసేజ్లను (WAP) స్వీకరించడం"</string> + <string name="permdesc_receiveWapPush" msgid="1638677888301778457">"WAP మెసేజ్లను స్వీకరించడానికి మరియు ప్రాసెస్ చేయడానికి యాప్ను అనుమతిస్తుంది. ఈ అనుమతి మీకు పంపబడిన మెసేజ్లను మీకు చూపకుండానే పర్యవేక్షించగల లేదా తొలగించగల సామర్థ్యాన్ని కలిగి ఉంటుంది."</string> <string name="permlab_getTasks" msgid="7460048811831750262">"అమలవుతున్న యాప్లను పునరుద్ధరించడం"</string> <string name="permdesc_getTasks" msgid="7388138607018233726">"ప్రస్తుతం మరియు ఇటీవల అమలవుతున్న విధుల గురించి వివరణాత్మక సమాచారాన్ని తిరిగి పొందడానికి యాప్ను అనుమతిస్తుంది. ఇది పరికరంలో ఉపయోగించబడిన యాప్ల గురించి సమాచారాన్ని కనుగొనడానికి యాప్ను అనుమతించవచ్చు."</string> <string name="permlab_manageProfileAndDeviceOwners" msgid="639849495253987493">"ప్రొఫైల్ మరియు పరికర యజమానులను నిర్వహించడం"</string> @@ -431,9 +431,9 @@ <string name="permdesc_readCalendar" product="tv" msgid="5811726712981647628">"ఈ యాప్ మీ Android TV పరికరంలో నిల్వ చేసిన క్యాలెండర్ ఈవెంట్లన్నీ చదవగలదు, మీ క్యాలెండర్ డేటాను షేర్ చేయగలదు లేదా సేవ్ చేయగలదు."</string> <string name="permdesc_readCalendar" product="default" msgid="9118823807655829957">"ఈ యాప్ మీ ఫోన్లో నిల్వ చేసిన క్యాలెండర్ ఈవెంట్లన్నీ చదవగలదు మరియు మీ క్యాలెండర్ డేటాను షేర్ చేయగలదు లేదా సేవ్ చేయగలదు."</string> <string name="permlab_writeCalendar" msgid="6422137308329578076">"యజమానికి తెలియకుండానే క్యాలెండర్ ఈవెంట్లను జోడించి లేదా సవరించి, అతిథులకు ఇమెయిల్ పంపడం"</string> - <string name="permdesc_writeCalendar" product="tablet" msgid="8722230940717092850">"ఈ యాప్ మీ టాబ్లెట్లో క్యాలెండర్ ఈవెంట్లను జోడించగలదు, తీసివేయగలదు లేదా మార్చగలదు. ఈ యాప్ క్యాలెండర్ యజమానుల నుండి వచ్చినట్లుగా సందేశాలను పంపగలదు లేదా ఈవెంట్లను వాటి యజమానులకు తెలియకుండానే మార్చగలదు."</string> - <string name="permdesc_writeCalendar" product="tv" msgid="951246749004952706">"ఈ యాప్ మీ Android TV పరికరంలో క్యాలెండర్ ఈవెంట్లను జోడించగలదు, తీసివేయగలదు లేదా మార్చగలదు. ఈ యాప్ క్యాలెండర్ యజమానుల నుండి వచ్చినట్లుగా సందేశాలను పంపగలదు లేదా ఈవెంట్లను వాటి యజమానులకు తెలియకుండానే మార్చగలదు."</string> - <string name="permdesc_writeCalendar" product="default" msgid="5416380074475634233">"ఈ యాప్ మీ ఫోన్లో క్యాలెండర్ ఈవెంట్లను జోడించగలదు, తీసివేయగలదు లేదా మార్చగలదు. ఈ యాప్ క్యాలెండర్ యజమానుల నుండి వచ్చినట్లుగా సందేశాలను పంపగలదు లేదా ఈవెంట్లను వాటి యజమానులకు తెలియకుండానే మార్చగలదు."</string> + <string name="permdesc_writeCalendar" product="tablet" msgid="8722230940717092850">"ఈ యాప్ మీ టాబ్లెట్లో క్యాలెండర్ ఈవెంట్లను జోడించగలదు, తీసివేయగలదు లేదా మార్చగలదు. ఈ యాప్ క్యాలెండర్ యజమానుల నుండి వచ్చినట్లుగా మెసేజ్లను పంపగలదు లేదా ఈవెంట్లను వాటి యజమానులకు తెలియకుండానే మార్చగలదు."</string> + <string name="permdesc_writeCalendar" product="tv" msgid="951246749004952706">"ఈ యాప్ మీ Android TV పరికరంలో క్యాలెండర్ ఈవెంట్లను జోడించగలదు, తీసివేయగలదు లేదా మార్చగలదు. ఈ యాప్ క్యాలెండర్ యజమానుల నుండి వచ్చినట్లుగా మెసేజ్లను పంపగలదు లేదా ఈవెంట్లను వాటి యజమానులకు తెలియకుండానే మార్చగలదు."</string> + <string name="permdesc_writeCalendar" product="default" msgid="5416380074475634233">"ఈ యాప్ మీ ఫోన్లో క్యాలెండర్ ఈవెంట్లను జోడించగలదు, తీసివేయగలదు లేదా మార్చగలదు. ఈ యాప్ క్యాలెండర్ యజమానుల నుండి వచ్చినట్లుగా మెసేజ్లను పంపగలదు లేదా ఈవెంట్లను వాటి యజమానులకు తెలియకుండానే మార్చగలదు."</string> <string name="permlab_accessLocationExtraCommands" msgid="5162339812057983988">"అదనపు స్థాన ప్రదాత ఆదేశాలను యాక్సెస్ చేయడం"</string> <string name="permdesc_accessLocationExtraCommands" msgid="355369611979907967">"అదనపు స్థాన ప్రదాత ఆదేశాలను యాక్సెస్ చేయడానికి యాప్ను అనుమతిస్తుంది. ఇది GPS లేదా ఇతర స్థాన మూలాల నిర్వహణలో యాప్ ప్రమేయం ఉండేలా అనుమతించవచ్చు."</string> <string name="permlab_accessFineLocation" msgid="6426318438195622966">"స్క్రీన్పై ఉన్నప్పుడు మాత్రమే ఖచ్చితమైన స్థానాన్ని యాక్సెస్ చేయండి"</string> @@ -500,9 +500,9 @@ <string name="permdesc_setTimeZone" product="tv" msgid="9069045914174455938">"మీ Android TV పరికరం సమయ మండలిని మార్చడానికి యాప్ని అనుమతిస్తుంది."</string> <string name="permdesc_setTimeZone" product="default" msgid="4611828585759488256">"ఫోన్ యొక్క సమయ మండలిని మార్చడానికి యాప్ను అనుమతిస్తుంది."</string> <string name="permlab_getAccounts" msgid="5304317160463582791">"పరికరంలో ఖాతాలను కనుగొనడం"</string> - <string name="permdesc_getAccounts" product="tablet" msgid="1784452755887604512">"టాబ్లెట్కు తెలిసిన ఖాతాల జాబితాను పొందడానికి యాప్ను అనుమతిస్తుంది. దీనిలో మీరు ఇన్స్టాల్ చేసిన యాప్ల ద్వారా సృష్టించబడిన ఖాతాలు ఏవైనా ఉండవచ్చు."</string> - <string name="permdesc_getAccounts" product="tv" msgid="437604680436540822">"మీ Android TV పరికరానికి తెలిసిన ఖాతాల జాబితాను పొందడానికి యాప్ను అనుమతిస్తుంది. దీనిలో మీరు ఇన్స్టాల్ చేసిన యాప్ల ద్వారా సృష్టించబడిన ఖాతాలు ఏవైనా ఉండవచ్చు."</string> - <string name="permdesc_getAccounts" product="default" msgid="2491273043569751867">"ఫోన్కు తెలిసిన ఖాతాల జాబితాను పొందడానికి యాప్ను అనుమతిస్తుంది. దీనిలో మీరు ఇన్స్టాల్ చేసిన యాప్ల ద్వారా సృష్టించబడిన ఖాతాలు ఏవైనా ఉండవచ్చు."</string> + <string name="permdesc_getAccounts" product="tablet" msgid="1784452755887604512">"టాబ్లెట్కు తెలిసిన ఖాతాల లిస్ట్ను పొందడానికి యాప్ను అనుమతిస్తుంది. దీనిలో మీరు ఇన్స్టాల్ చేసిన యాప్ల ద్వారా సృష్టించబడిన ఖాతాలు ఏవైనా ఉండవచ్చు."</string> + <string name="permdesc_getAccounts" product="tv" msgid="437604680436540822">"మీ Android TV పరికరానికి తెలిసిన ఖాతాల లిస్ట్ను పొందడానికి యాప్ను అనుమతిస్తుంది. దీనిలో మీరు ఇన్స్టాల్ చేసిన యాప్ల ద్వారా సృష్టించబడిన ఖాతాలు ఏవైనా ఉండవచ్చు."</string> + <string name="permdesc_getAccounts" product="default" msgid="2491273043569751867">"ఫోన్కు తెలిసిన ఖాతాల లిస్ట్ను పొందడానికి యాప్ను అనుమతిస్తుంది. దీనిలో మీరు ఇన్స్టాల్ చేసిన యాప్ల ద్వారా సృష్టించబడిన ఖాతాలు ఏవైనా ఉండవచ్చు."</string> <string name="permlab_accessNetworkState" msgid="2349126720783633918">"నెట్వర్క్ కనెక్షన్లను వీక్షించడం"</string> <string name="permdesc_accessNetworkState" msgid="4394564702881662849">"ఏ నెట్వర్క్లు ఉన్నాయి మరియు కనెక్ట్ చేయబడ్డాయి వంటి నెట్వర్క్ కనెక్షన్ల గురించి సమాచారాన్ని వీక్షించడానికి యాప్ను అనుమతిస్తుంది."</string> <string name="permlab_createNetworkSockets" msgid="3224420491603590541">"నెట్వర్క్ను పూర్తిగా యాక్సెస్ చేయగలగడం"</string> @@ -516,9 +516,9 @@ <string name="permlab_changeWifiState" msgid="7947824109713181554">"Wi-Fiకి కనెక్ట్ చేయడం మరియు దాని నుండి డిస్కనెక్ట్ చేయడం"</string> <string name="permdesc_changeWifiState" msgid="7170350070554505384">"Wi-Fi యాక్సెస్ స్థానాలకు కనెక్ట్ చేయడానికి మరియు వాటి నుండి డిస్కనెక్ట్ చేయడానికి మరియు Wi-Fi నెట్వర్క్ల కోసం పరికర కాన్ఫిగరేషన్కు మార్పులు చేయడానికి యాప్ను అనుమతిస్తుంది."</string> <string name="permlab_changeWifiMulticastState" msgid="285626875870754696">"Wi-Fi Multicast స్వీకరణను అనుమతించడం"</string> - <string name="permdesc_changeWifiMulticastState" product="tablet" msgid="191079868596433554">"మల్టీక్యాస్ట్ చిరునామాలను ఉపయోగించి మీ టాబ్లెట్కు మాత్రమే కాకుండా Wi-Fi నెట్వర్క్లోని అన్ని పరికరాలకు పంపబడిన ప్యాకెట్లను స్వీకరించడానికి యాప్ను అనుమతిస్తుంది. మల్టీక్యాస్ట్ యేతర మోడ్ కంటే ఇది ఎక్కువ పవర్ ఉపయోగిస్తుంది."</string> - <string name="permdesc_changeWifiMulticastState" product="tv" msgid="1336952358450652595">"మల్టీక్యాస్ట్ చిరునామాలను ఉపయోగించి మీ Android TV పరికరానికి మాత్రమే కాకుండా Wi-Fi నెట్వర్క్లోని అన్ని పరికరాలకు పంపిన ప్యాకెట్లను స్వీకరించడానికి యాప్ని అనుమతిస్తుంది. ఇది మల్టీక్యాస్ట్ యేతర మోడ్ కంటే ఎక్కువ పవర్ను ఉపయోగిస్తుంది."</string> - <string name="permdesc_changeWifiMulticastState" product="default" msgid="8296627590220222740">"మల్టీక్యాస్ట్ చిరునామాలను ఉపయోగించి మీ ఫోన్కు మాత్రమే కాకుండా Wi-Fi నెట్వర్క్లోని అన్ని పరికరాలకు పంపబడిన ప్యాకెట్లను స్వీకరించడానికి యాప్ను అనుమతిస్తుంది. మల్టీక్యాస్ట్ యేతర మోడ్ కంటే ఇది ఎక్కువ పవర్ ఉపయోగిస్తుంది."</string> + <string name="permdesc_changeWifiMulticastState" product="tablet" msgid="191079868596433554">"మల్టీక్యాస్ట్ అడ్రస్లను ఉపయోగించి మీ టాబ్లెట్కు మాత్రమే కాకుండా Wi-Fi నెట్వర్క్లోని అన్ని పరికరాలకు పంపబడిన ప్యాకెట్లను స్వీకరించడానికి యాప్ను అనుమతిస్తుంది. మల్టీక్యాస్ట్ యేతర మోడ్ కంటే ఇది ఎక్కువ పవర్ ఉపయోగిస్తుంది."</string> + <string name="permdesc_changeWifiMulticastState" product="tv" msgid="1336952358450652595">"మల్టీక్యాస్ట్ అడ్రస్లను ఉపయోగించి మీ Android TV పరికరానికి మాత్రమే కాకుండా Wi-Fi నెట్వర్క్లోని అన్ని పరికరాలకు పంపిన ప్యాకెట్లను స్వీకరించడానికి యాప్ని అనుమతిస్తుంది. ఇది మల్టీక్యాస్ట్ యేతర మోడ్ కంటే ఎక్కువ పవర్ను ఉపయోగిస్తుంది."</string> + <string name="permdesc_changeWifiMulticastState" product="default" msgid="8296627590220222740">"మల్టీక్యాస్ట్ అడ్రస్లను ఉపయోగించి మీ ఫోన్కు మాత్రమే కాకుండా Wi-Fi నెట్వర్క్లోని అన్ని పరికరాలకు పంపబడిన ప్యాకెట్లను స్వీకరించడానికి యాప్ను అనుమతిస్తుంది. మల్టీక్యాస్ట్ యేతర మోడ్ కంటే ఇది ఎక్కువ పవర్ ఉపయోగిస్తుంది."</string> <string name="permlab_bluetoothAdmin" msgid="6490373569441946064">"బ్లూటూత్ సెట్టింగ్లను యాక్సెస్ చేయడం"</string> <string name="permdesc_bluetoothAdmin" product="tablet" msgid="5370837055438574863">"స్థానిక బ్లూటూత్ టాబ్లెట్ను కాన్ఫిగర్ చేయడానికి మరియు రిమోట్ పరికరాలతో దాన్ని కనుగొనడానికి మరియు జత చేయడానికి యాప్ను అనుమతిస్తుంది."</string> <string name="permdesc_bluetoothAdmin" product="tv" msgid="1623992984547014588">"మీ Android TV పరికరంలో బ్లూటూత్ను కాన్ఫిగర్ చేయడానికి మరియు రిమోట్ పరికరాలతో దాన్ని కనుగొని, జత చేయడానికి యాప్ను అనుమతిస్తుంది."</string> @@ -547,7 +547,7 @@ <string name="permdesc_nfc" msgid="8352737680695296741">"సమీప ఫీల్డ్ కమ్యూనికేషన్ (NFC) ట్యాగ్లు, కార్డులు మరియు రీడర్లతో కమ్యూనికేట్ చేయడానికి యాప్ను అనుమతిస్తుంది."</string> <string name="permlab_disableKeyguard" msgid="3605253559020928505">"మీ స్క్రీన్ లాక్ను నిలిపివేయడం"</string> <string name="permdesc_disableKeyguard" msgid="3223710003098573038">"కీలాక్ మరియు ఏదైనా అనుబంధించబడిన పాస్వర్డ్ భద్రతను నిలిపివేయడానికి యాప్ను అనుమతిస్తుంది. ఉదాహరణకు, ఇన్కమింగ్ ఫోన్ కాల్ వస్తున్నప్పుడు ఫోన్ కీలాక్ను నిలిపివేస్తుంది, ఆపై కాల్ ముగిసిన తర్వాత కీలాక్ను మళ్లీ ప్రారంభిస్తుంది."</string> - <string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"స్క్రీన్ లాక్ సంక్లిష్టత అభ్యర్థన"</string> + <string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"స్క్రీన్ లాక్ సంక్లిష్టత రిక్వెస్ట్"</string> <string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"ఇది మీ స్క్రీన్ లాక్ పాస్వర్డ్ సంక్లిష్టత స్థాయి (తీవ్రంగా ఉండాలా, ఓ మోస్తరుగా ఉండాలా, తక్కువ తీవ్రంగా ఉండాలా లేదా అస్సలు తీవ్రత ఉండకూడదా) తెలుసుకోవడానికి యాప్ను అనుమతిస్తుంది, అంటే పొడుగు ఎంత ఉండాలి, ఏ రకమైన స్క్రీన్ లాక్ పధ్ధతి అనుసరించాలో సూచిస్తుంది. అలాగే, స్క్రీన్ లాక్ పాస్వర్డ్ సంక్లిష్టతను ఏ స్థాయికి సెట్ చేసుకుంటే బాగుంటుందో కూడా వినియోగదారులకు యాప్ సూచించగలదు, కానీ వినియోగదారులు నిరభ్యంతరంగా ఆ సూచనలను పట్టించుకోకుండా వారి ఇష్టం మేరకు చక్కగా సెట్ చేసుకోవచ్చు. ఇంకో ముఖ్య విషయం, స్క్రీన్ లాక్ అన్నది సాదా వచన రూపంలో నిల్వ చేయబడదు, కనుక ఖచ్చితమైన పాస్వర్డ్ ఏమిటనేది యాప్కు తెలియదు."</string> <string name="permlab_useBiometric" msgid="6314741124749633786">"బయోమెట్రిక్ హార్డ్వేర్ని ఉపయోగించు"</string> <string name="permdesc_useBiometric" msgid="7502858732677143410">"ప్రమాణీకరణ కోసం బయోమెట్రిక్ హార్డ్వేర్ను ఉపయోగించడానికి యాప్ని అనుమతిస్తుంది"</string> @@ -555,11 +555,11 @@ <string name="permdesc_manageFingerprint" msgid="2025616816437339865">"వినియోగం కోసం వేలిముద్ర టెంప్లేట్లను జోడించే, తొలగించే పద్ధతులను అమలు చేయడానికి యాప్ను అనుమతిస్తుంది."</string> <string name="permlab_useFingerprint" msgid="1001421069766751922">"వేలిముద్ర హార్డ్వేర్ని ఉపయోగించడానికి అనుమతి"</string> <string name="permdesc_useFingerprint" msgid="412463055059323742">"ప్రామాణీకరణ కోసం వేలిముద్ర హార్డ్వేర్ను ఉపయోగించడానికి యాప్ను అనుమతిస్తుంది"</string> - <string name="permlab_audioWrite" msgid="8501705294265669405">"మీ సంగీత సేకరణను సవరించండి"</string> + <string name="permlab_audioWrite" msgid="8501705294265669405">"మీ సంగీత సేకరణను ఎడిట్ చేయండి"</string> <string name="permdesc_audioWrite" msgid="8057399517013412431">"మీ సంగీత సేకరణని సవరించడానికి యాప్ను అనుమతిస్తుంది."</string> - <string name="permlab_videoWrite" msgid="5940738769586451318">"మీ వీడియో సేకరణను సవరించండి"</string> + <string name="permlab_videoWrite" msgid="5940738769586451318">"మీ వీడియో సేకరణను ఎడిట్ చేయండి"</string> <string name="permdesc_videoWrite" msgid="6124731210613317051">"మీ వీడియో సేకరణను సవరించడానికి యాప్ని అనుమతిస్తుంది."</string> - <string name="permlab_imagesWrite" msgid="1774555086984985578">"మీ ఫోటో సేకరణను సవరించండి"</string> + <string name="permlab_imagesWrite" msgid="1774555086984985578">"మీ ఫోటో సేకరణను ఎడిట్ చేయండి"</string> <string name="permdesc_imagesWrite" msgid="5195054463269193317">"మీ ఫోటో సేకరణను సవరించడానికి యాప్ను అనుమతిస్తుంది."</string> <string name="permlab_mediaLocation" msgid="7368098373378598066">"మీ మీడియా సేకరణ నుండి స్థానాలను చదవండి"</string> <string name="permdesc_mediaLocation" msgid="597912899423578138">"మీ మీడియా సేకరణ నుండి స్థానాలను చదవడానికి యాప్ను అనుమతిస్తుంది."</string> @@ -593,9 +593,9 @@ <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"వేలిముద్ర హార్డ్వేర్ అందుబాటులో లేదు."</string> <string name="fingerprint_error_no_space" msgid="7285481581905967580">"వేలిముద్రను సెటప్ చేయడం సాధ్యం కాదు"</string> <string name="fingerprint_error_timeout" msgid="2946635815726054226">"వేలిముద్ర గడువు సమయం చేరుకుంది. మళ్లీ ప్రయత్నించండి."</string> - <string name="fingerprint_error_canceled" msgid="540026881380070750">"వేలిముద్ర కార్యకలాపం రద్దయింది."</string> - <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"వేలిముద్ర చర్యని వినియోగదారు రద్దు చేసారు."</string> - <string name="fingerprint_error_lockout" msgid="7853461265604738671">"చాలా ఎక్కువ ప్రయత్నాలు చేసారు. తర్వాత మళ్లీ ప్రయత్నించండి."</string> + <string name="fingerprint_error_canceled" msgid="540026881380070750">"వేలిముద్ర యాక్టివిటీ రద్దయింది."</string> + <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"వేలిముద్ర చర్యని వినియోగదారు రద్దు చేశారు."</string> + <string name="fingerprint_error_lockout" msgid="7853461265604738671">"చాలా ఎక్కువ ప్రయత్నాలు చేశారు. తర్వాత మళ్లీ ప్రయత్నించండి."</string> <string name="fingerprint_error_lockout_permanent" msgid="3895478283943513746">"అనేకసార్లు ప్రయత్నించారు. వేలిముద్ర సెన్సార్ నిలిపివేయబడింది."</string> <string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"మళ్లీ ప్రయత్నించండి."</string> <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"వేలిముద్రలు నమోదు చేయబడలేదు."</string> @@ -645,9 +645,9 @@ <string name="face_error_hw_not_available" msgid="5085202213036026288">"ముఖం ధృవీకరించలేరు. హార్డ్వేర్ అందుబాటులో లేదు."</string> <string name="face_error_timeout" msgid="2598544068593889762">"ఫేస్ అన్లాక్ను మళ్లీ ట్రై చేయండి"</string> <string name="face_error_no_space" msgid="5649264057026021723">"కొత్త ముఖం డేటాను నిల్వ చేయడం కాదు. మొదట పాతది తొలిగించండి."</string> - <string name="face_error_canceled" msgid="2164434737103802131">"ముఖ కార్యకలాపం రద్దయింది."</string> + <string name="face_error_canceled" msgid="2164434737103802131">"ముఖ యాక్టివిటీ రద్దయింది."</string> <string name="face_error_user_canceled" msgid="5766472033202928373">"ఫేస్ అన్లాక్ను యూజర్ రద్దు చేశారు"</string> - <string name="face_error_lockout" msgid="7864408714994529437">"చాలా ఎక్కువ ప్రయత్నాలు చేసారు. తర్వాత మళ్లీ ప్రయత్నించండి."</string> + <string name="face_error_lockout" msgid="7864408714994529437">"చాలా ఎక్కువ ప్రయత్నాలు చేశారు. తర్వాత మళ్లీ ప్రయత్నించండి."</string> <string name="face_error_lockout_permanent" msgid="3277134834042995260">"చాలా ఎక్కువ సార్లు ప్రయత్నించారు. ఫేస్ అన్లాక్ డిజేబుల్ చేయబడింది."</string> <string name="face_error_lockout_screen_lock" msgid="5062609811636860928">"చాలా ఎక్కువ సార్లు ప్రయత్నించారు. బదులుగా స్క్రీన్ లాక్ను ఎంటర్ చేయండి."</string> <string name="face_error_unable_to_process" msgid="5723292697366130070">"ముఖం ధృవీకరించలేకపోయింది. మళ్లీ ప్రయత్నించండి."</string> @@ -663,14 +663,14 @@ </string-array> <string name="face_icon_content_description" msgid="465030547475916280">"ముఖ చిహ్నం"</string> <string name="permlab_readSyncSettings" msgid="6250532864893156277">"సింక్ సెట్టింగ్లను చదవగలగడం"</string> - <string name="permdesc_readSyncSettings" msgid="1325658466358779298">"ఖాతా యొక్క సమకాలీకరణ సెట్టింగ్లను చదవడానికి యాప్ను అనుమతిస్తుంది. ఉదాహరణకు, వ్యక్తుల యాప్ ఖాతాతో సమకాలీకరించబడాలా లేదా అనే విషయాన్ని ఇది నిశ్చయించవచ్చు."</string> + <string name="permdesc_readSyncSettings" msgid="1325658466358779298">"ఖాతా యొక్క సింక్ సెట్టింగ్లను చదవడానికి యాప్ను అనుమతిస్తుంది. ఉదాహరణకు, వ్యక్తుల యాప్ ఖాతాతో సమకాలీకరించబడాలా లేదా అనే విషయాన్ని ఇది నిశ్చయించవచ్చు."</string> <string name="permlab_writeSyncSettings" msgid="6583154300780427399">"\'సింక్\'ను ఆన్, ఆఫ్ల మధ్య టోగుల్ చేయడం"</string> - <string name="permdesc_writeSyncSettings" msgid="6029151549667182687">"ఖాతా యొక్క సమకాలీకరణ సెట్టింగ్లను సవరించడానికి యాప్ను అనుమతిస్తుంది. ఉదాహరణకు, ఇది ఒక ఖాతాతో వ్యక్తుల యాప్ యొక్క సమకాలీకరణను ప్రారంభించడానికి ఉపయోగించబడవచ్చు."</string> + <string name="permdesc_writeSyncSettings" msgid="6029151549667182687">"ఖాతా యొక్క సింక్ సెట్టింగ్లను సవరించడానికి యాప్ను అనుమతిస్తుంది. ఉదాహరణకు, ఇది ఒక ఖాతాతో వ్యక్తుల యాప్ యొక్క సింక్ను ప్రారంభించడానికి ఉపయోగించబడవచ్చు."</string> <string name="permlab_readSyncStats" msgid="3747407238320105332">"సింక్ గణాంకాలను చదవగలగడం"</string> - <string name="permdesc_readSyncStats" msgid="3867809926567379434">"ఖాతా యొక్క సమకాలీకరణ గణాంకాలను అలాగే సమకాలీకరణ ఈవెంట్ల చరిత్రను మరియు ఎంత డేటా సమకాలీకరించబడింది అనేవాటిని చదవడానికి యాప్ను అనుమతిస్తుంది."</string> + <string name="permdesc_readSyncStats" msgid="3867809926567379434">"ఖాతా యొక్క సింక్ గణాంకాలను అలాగే సింక్ ఈవెంట్ల చరిత్రను మరియు ఎంత డేటా సమకాలీకరించబడింది అనేవాటిని చదవడానికి యాప్ను అనుమతిస్తుంది."</string> <string name="permlab_sdcardRead" msgid="5791467020950064920">"మీ షేర్ చేసిన నిల్వ యొక్క కంటెంట్లను చదువుతుంది"</string> <string name="permdesc_sdcardRead" msgid="6872973242228240382">"మీ షేర్ చేసిన నిల్వ యొక్క కంటెంట్లను చదవడానికి యాప్ను అనుమతిస్తుంది."</string> - <string name="permlab_sdcardWrite" msgid="4863021819671416668">"మీ షేర్ చేసిన నిల్వ యొక్క కంటెంట్లను సవరించండి లేదా తొలగించండి"</string> + <string name="permlab_sdcardWrite" msgid="4863021819671416668">"మీ షేర్ చేసిన నిల్వ యొక్క కంటెంట్లను ఎడిట్ చేయండి లేదా తొలగించండి"</string> <string name="permdesc_sdcardWrite" msgid="8376047679331387102">"మీ షేర్ చేసిన నిల్వ యొక్క కంటెంట్లను రాయడానికి యాప్ను అనుమతిస్తుంది."</string> <string name="permlab_use_sip" msgid="8250774565189337477">"SIP కాల్స్ను చేయడానికి/స్వీకరించడానికి"</string> <string name="permdesc_use_sip" msgid="3590270893253204451">"SIP కాల్స్ను చేయడానికి మరియు స్వీకరించడానికి యాప్ను అనుమతిస్తుంది."</string> @@ -713,7 +713,7 @@ <string name="permlab_removeDrmCertificates" msgid="710576248717404416">"DRM ప్రమాణపత్రాలను తీసివేయడం"</string> <string name="permdesc_removeDrmCertificates" msgid="4068445390318355716">"DRM ప్రమాణపత్రాలను తీసివేయడానికి యాప్ను అనుమతిస్తుంది. సాధారణ యాప్లకు ఎప్పటికీ అవసరం ఉండదు."</string> <string name="permlab_bindCarrierMessagingService" msgid="3363450860593096967">"క్యారియర్ సందేశ సేవకు అనుబంధించడం"</string> - <string name="permdesc_bindCarrierMessagingService" msgid="6316457028173478345">"క్యారియర్ సందేశ సేవ యొక్క అగ్ర-స్థాయి ఇంటర్ఫేస్కు అనుబంధించడానికి హోల్డర్ను అనుమతిస్తుంది. సాధారణ యాప్లకు ఎప్పటికీ అవసరం ఉండదు."</string> + <string name="permdesc_bindCarrierMessagingService" msgid="6316457028173478345">"క్యారియర్ మెసేజింగ్ సర్వీస్ యొక్క అగ్ర-స్థాయి ఇంటర్ఫేస్కు అనుబంధించడానికి హోల్డర్ను అనుమతిస్తుంది. సాధారణ యాప్లకు ఎప్పటికీ అవసరం ఉండదు."</string> <string name="permlab_bindCarrierServices" msgid="2395596978626237474">"క్యారియర్ సేవలకు అనుబంధించడం"</string> <string name="permdesc_bindCarrierServices" msgid="9185614481967262900">"క్యారియర్ సేవలకు అనుబంధించడానికి హోల్డర్ను అనుమతిస్తుంది. సాధారణ యాప్లకు ఎప్పటికీ అవసరం ఉండదు."</string> <string name="permlab_access_notification_policy" msgid="5524112842876975537">"అంతరాయం కలిగించవద్దును యాక్సెస్ చేయడం"</string> @@ -911,21 +911,21 @@ <string name="lockscreen_sim_locked_message" msgid="3160196135801185938">"సిమ్ కార్డు లాక్ చేయబడింది."</string> <string name="lockscreen_sim_unlock_progress_dialog_message" msgid="2286497117428409709">"సిమ్ కార్డును అన్లాక్ చేస్తోంది…"</string> <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6458790975898594240">"మీరు మీ అన్లాక్ నమూనాను <xliff:g id="NUMBER_0">%1$d</xliff:g> సార్లు తప్పుగా గీసారు. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> సెకన్లలో మళ్లీ ప్రయత్నించండి."</string> - <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="3118353451602377380">"మీరు మీ పాస్వర్డ్ను <xliff:g id="NUMBER_0">%1$d</xliff:g> సార్లు తప్పుగా టైప్ చేసారు. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> సెకన్లలో మళ్లీ ప్రయత్నించండి."</string> - <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="2874278239714821984">"మీరు మీ పిన్ను <xliff:g id="NUMBER_0">%1$d</xliff:g> సార్లు తప్పుగా టైప్ చేసారు. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> సెకన్లలో మళ్లీ ప్రయత్నించండి."</string> + <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="3118353451602377380">"మీరు మీ పాస్వర్డ్ను <xliff:g id="NUMBER_0">%1$d</xliff:g> సార్లు తప్పుగా టైప్ చేశారు. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> సెకన్లలో మళ్లీ ప్రయత్నించండి."</string> + <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="2874278239714821984">"మీరు మీ పిన్ను <xliff:g id="NUMBER_0">%1$d</xliff:g> సార్లు తప్పుగా టైప్ చేశారు. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> సెకన్లలో మళ్లీ ప్రయత్నించండి."</string> <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="3069635524964070596">"మీరు మీ అన్లాక్ నమూనాని <xliff:g id="NUMBER_0">%1$d</xliff:g> సార్లు తప్పుగా గీసారు. మరో <xliff:g id="NUMBER_1">%2$d</xliff:g> విజయవంతం కాని ప్రయత్నాల తర్వాత, మీరు మీ Google సైన్ఇన్ను ఉపయోగించి మీ టాబ్లెట్ను అన్లాక్ చేయడానికి అడగబడతారు.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> సెకన్లలో మళ్లీ ప్రయత్నించండి."</string> <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="6399092175942158529">"మీరు మీ అన్లాక్ నమూనాని <xliff:g id="NUMBER_0">%1$d</xliff:g> సార్లు తప్పుగా గీసారు. మరో <xliff:g id="NUMBER_1">%2$d</xliff:g> విఫల ప్రయత్నాల తర్వాత, మీరు మీ Google సైన్ఇన్ను ఉపయోగించి మీ Android TV పరికరాన్ని అన్లాక్ చేయాల్సిందిగా మీకు తెలపబడుతుంది.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> సెకన్లలో మళ్లీ ప్రయత్నించండి."</string> <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="5691623136957148335">"మీరు మీ అన్లాక్ నమూనాని <xliff:g id="NUMBER_0">%1$d</xliff:g> సార్లు తప్పుగా గీసారు. మరో <xliff:g id="NUMBER_1">%2$d</xliff:g> విజయవంతం కాని ప్రయత్నాల తర్వాత, మీరు మీ Google సైన్ఇన్ను ఉపయోగించి మీ ఫోన్ను అన్లాక్ చేయడానికి అడగబడతారు.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> సెకన్లలో మళ్లీ ప్రయత్నించండి."</string> <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="7914445759242151426">"మీరు టాబ్లెట్ను అన్లాక్ చేయడానికి <xliff:g id="NUMBER_0">%1$d</xliff:g> సార్లు తప్పుగా ప్రయత్నించారు. మరో <xliff:g id="NUMBER_1">%2$d</xliff:g> వైఫల్య ప్రయత్నాల తర్వాత, టాబ్లెట్ ఫ్యాక్టరీ డిఫాల్ట్కు రీసెట్ చేయబడుతుంది మరియు మొత్తం వినియోగదారు డేటాను కోల్పోవడం సంభవిస్తుంది."</string> - <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="4275591249631864248">"మీరు మీ Android TV పరికరాన్ని అన్లాక్ చేయడానికి <xliff:g id="NUMBER_0">%1$d</xliff:g> సార్లు విఫల ప్రయత్నాలు చేసారు. మరో <xliff:g id="NUMBER_1">%2$d</xliff:g> విఫల ప్రయత్నాల తర్వాత, మీ Android TV పరికరం ఫ్యాక్టరీ డిఫాల్ట్కి రీసెట్ చేయబడుతుంది, అలాగే వినియోగదారు డేటా మొత్తాన్ని కోల్పోతారు."</string> + <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="4275591249631864248">"మీరు మీ Android TV పరికరాన్ని అన్లాక్ చేయడానికి <xliff:g id="NUMBER_0">%1$d</xliff:g> సార్లు విఫల ప్రయత్నాలు చేశారు. మరో <xliff:g id="NUMBER_1">%2$d</xliff:g> విఫల ప్రయత్నాల తర్వాత, మీ Android TV పరికరం ఫ్యాక్టరీ డిఫాల్ట్కి రీసెట్ చేయబడుతుంది, అలాగే వినియోగదారు డేటా మొత్తాన్ని కోల్పోతారు."</string> <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="1166532464798446579">"మీరు ఫోన్ను అన్లాక్ చేయడానికి <xliff:g id="NUMBER_0">%1$d</xliff:g> సార్లు తప్పుగా ప్రయత్నించారు. మరో <xliff:g id="NUMBER_1">%2$d</xliff:g> వైఫల్య ప్రయత్నాల తర్వాత, ఫోన్ ఫ్యాక్టరీ డిఫాల్ట్కు రీసెట్ చేయబడుతుంది మరియు మొత్తం వినియోగదారు డేటాను కోల్పోవడం సంభవిస్తుంది."</string> <string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="8682445539263683414">"మీరు టాబ్లెట్ను అన్లాక్ చేయడానికి <xliff:g id="NUMBER">%d</xliff:g> సార్లు తప్పుగా ప్రయత్నించారు. టాబ్లెట్ ఇప్పుడు ఫ్యాక్టరీ డిఫాల్ట్కు రీసెట్ చేయబడుతుంది."</string> - <string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="2205435033340091883">"మీరు మీ Android TV పరికరాన్ని అన్లాక్ చేయడానికి <xliff:g id="NUMBER">%d</xliff:g> సార్లు విఫల ప్రయత్నాలు చేసారు. మీ Android TV పరికరం ఇప్పుడు ఫ్యాక్టరీ రీసెట్ చేయబడుతుంది."</string> + <string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="2205435033340091883">"మీరు మీ Android TV పరికరాన్ని అన్లాక్ చేయడానికి <xliff:g id="NUMBER">%d</xliff:g> సార్లు విఫల ప్రయత్నాలు చేశారు. మీ Android TV పరికరం ఇప్పుడు ఫ్యాక్టరీ రీసెట్ చేయబడుతుంది."</string> <string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="2203704707679895487">"మీరు ఫోన్ను అన్లాక్ చేయడానికి <xliff:g id="NUMBER">%d</xliff:g> సార్లు తప్పుగా ప్రయత్నించారు. ఫోన్ ఇప్పుడు ఫ్యాక్టరీ డిఫాల్ట్కు రీసెట్ చేయబడుతుంది."</string> <string name="lockscreen_too_many_failed_attempts_countdown" msgid="6807200118164539589">"<xliff:g id="NUMBER">%d</xliff:g> సెకన్లలో మళ్లీ ప్రయత్నించండి."</string> <string name="lockscreen_forgot_pattern_button_text" msgid="8362442730606839031">"నమూనాను మర్చిపోయారా?"</string> <string name="lockscreen_glogin_forgot_pattern" msgid="9218940117797602518">"ఖాతా అన్లాక్"</string> - <string name="lockscreen_glogin_too_many_attempts" msgid="3775904917743034195">"చాలా ఎక్కువ ఆకృతి ప్రయత్నాలు చేసారు"</string> + <string name="lockscreen_glogin_too_many_attempts" msgid="3775904917743034195">"చాలా ఎక్కువ ఆకృతి ప్రయత్నాలు చేశారు"</string> <string name="lockscreen_glogin_instructions" msgid="4695162942525531700">"అన్లాక్ చేయడానికి, మీ Google ఖాతాతో సైన్ ఇన్ చేయండి."</string> <string name="lockscreen_glogin_username_hint" msgid="6916101478673157045">"వినియోగదారు పేరు (ఇమెయిల్)"</string> <string name="lockscreen_glogin_password_hint" msgid="3031027901286812848">"పాస్వర్డ్"</string> @@ -1012,7 +1012,7 @@ <string name="permlab_setAlarm" msgid="1158001610254173567">"అలారం సెట్ చేయడం"</string> <string name="permdesc_setAlarm" msgid="2185033720060109640">"ఇన్స్టాల్ చేయబడిన అలారం గడియారం యాప్లో అలారంను సెట్ చేయడానికి యాప్ను అనుమతిస్తుంది. కొన్ని అలారం గల గడియారం యాప్లు ఈ ఫీచర్ను అమలు చేయకపోవచ్చు."</string> <string name="permlab_addVoicemail" msgid="4770245808840814471">"వాయిస్ మెయిల్ను జోడించడం"</string> - <string name="permdesc_addVoicemail" msgid="5470312139820074324">"మీ వాయిస్ మెయిల్ ఇన్బాక్స్కి సందేశాలను జోడించడానికి యాప్ను అనుమతిస్తుంది."</string> + <string name="permdesc_addVoicemail" msgid="5470312139820074324">"మీ వాయిస్ మెయిల్ ఇన్బాక్స్కి మెసేజ్లను జోడించడానికి యాప్ను అనుమతిస్తుంది."</string> <string name="permlab_writeGeolocationPermissions" msgid="8605631647492879449">"బ్రౌజర్ భౌగోళిక స్థానం అనుమతులను సవరించడం"</string> <string name="permdesc_writeGeolocationPermissions" msgid="5817346421222227772">"బ్రౌజర్ యొక్క భౌగోళిక స్థానం అనుమతులను సవరించడానికి యాప్ను అనుమతిస్తుంది. హానికరమైన యాప్లు ఏకపక్ష వెబ్ సైట్లకు స్థాన సమాచారాన్ని అనుమతించడానికి దీన్ని ఉపయోగించవచ్చు."</string> <string name="save_password_message" msgid="2146409467245462965">"మీరు బ్రౌజర్ ఈ పాస్వర్డ్ను గుర్తుపెట్టుకోవాలని కోరుకుంటున్నారా?"</string> @@ -1041,13 +1041,13 @@ <string name="search_go" msgid="2141477624421347086">"సెర్చ్"</string> <string name="search_hint" msgid="455364685740251925">"వెతుకు..."</string> <string name="searchview_description_search" msgid="1045552007537359343">"సెర్చ్"</string> - <string name="searchview_description_query" msgid="7430242366971716338">"ప్రశ్నను శోధించండి"</string> + <string name="searchview_description_query" msgid="7430242366971716338">"ప్రశ్నను వెతకండి"</string> <string name="searchview_description_clear" msgid="1989371719192982900">"ప్రశ్నను క్లియర్ చేయి"</string> <string name="searchview_description_submit" msgid="6771060386117334686">"ప్రశ్నని సమర్పించండి"</string> <string name="searchview_description_voice" msgid="42360159504884679">"వాయిస్ శోధన"</string> <string name="enable_explore_by_touch_warning_title" msgid="5095399706284943314">"తాకడం ద్వారా విశ్లేషణను ప్రారంభించాలా?"</string> - <string name="enable_explore_by_touch_warning_message" product="tablet" msgid="1037295476738940824">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> తాకడం ద్వారా విశ్లేషణను ప్రారంభించాలనుకుంటోంది. తాకడం ద్వారా విశ్లేషణను ఆన్ చేసినప్పుడు, మీరు మీ వేలి క్రింద ఉన్నవాటి యొక్క వివరణలను వినవచ్చు లేదా చూడవచ్చు లేదా టాబ్లెట్తో పరస్పర చర్య చేయడానికి సంజ్ఞలు చేయవచ్చు."</string> - <string name="enable_explore_by_touch_warning_message" product="default" msgid="4312979647356179250">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> తాకడం ద్వారా విశ్లేషణను ప్రారంభించాలనుకుంటోంది. తాకడం ద్వారా విశ్లేషణ ఆన్ చేయబడినప్పుడు, మీరు మీ వేలి క్రింద ఉన్నవాటి యొక్క వివరణలను వినవచ్చు లేదా చూడవచ్చు లేదా ఫోన్తో పరస్పర చర్య చేయడానికి సంజ్ఞలు చేయవచ్చు."</string> + <string name="enable_explore_by_touch_warning_message" product="tablet" msgid="1037295476738940824">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> తాకడం ద్వారా విశ్లేషణను ప్రారంభించాలనుకుంటోంది. తాకడం ద్వారా విశ్లేషణను ఆన్ చేసినప్పుడు, మీరు మీ వేలి కింద ఉన్నవాటి యొక్క వివరణలను వినవచ్చు లేదా చూడవచ్చు లేదా టాబ్లెట్తో పరస్పర చర్య చేయడానికి సంజ్ఞలు చేయవచ్చు."</string> + <string name="enable_explore_by_touch_warning_message" product="default" msgid="4312979647356179250">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> తాకడం ద్వారా విశ్లేషణను ప్రారంభించాలనుకుంటోంది. తాకడం ద్వారా విశ్లేషణ ఆన్ చేయబడినప్పుడు, మీరు మీ వేలి కింద ఉన్నవాటి యొక్క వివరణలను వినవచ్చు లేదా చూడవచ్చు లేదా ఫోన్తో పరస్పర చర్య చేయడానికి సంజ్ఞలు చేయవచ్చు."</string> <string name="oneMonthDurationPast" msgid="4538030857114635777">"1 నెల క్రితం"</string> <string name="beforeOneMonthDurationPast" msgid="8315149541372065392">"1 నెలకు ముందు"</string> <plurals name="last_num_days" formatted="false" msgid="687443109145393632"> @@ -1325,11 +1325,11 @@ <string name="accept" msgid="5447154347815825107">"ఆమోదిస్తున్నాను"</string> <string name="decline" msgid="6490507610282145874">"తిరస్కరిస్తున్నాను"</string> <string name="select_character" msgid="3352797107930786979">"అక్షరాన్ని చొప్పించండి"</string> - <string name="sms_control_title" msgid="4748684259903148341">"SMS సందేశాలు పంపుతోంది"</string> - <string name="sms_control_message" msgid="6574313876316388239">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> పెద్ద సంఖ్యలో SMS సందేశాలను పంపుతోంది. సందేశాలను పంపడం కొనసాగించడానికి మీరు ఈ యాప్ను అనుమతించాలనుకుంటున్నారా?"</string> + <string name="sms_control_title" msgid="4748684259903148341">"SMS మెసేజ్లు పంపుతోంది"</string> + <string name="sms_control_message" msgid="6574313876316388239">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> పెద్ద సంఖ్యలో SMS మెసేజ్లను పంపుతోంది. మెసేజ్లను పంపడం కొనసాగించడానికి మీరు ఈ యాప్ను అనుమతించాలనుకుంటున్నారా?"</string> <string name="sms_control_yes" msgid="4858845109269524622">"అనుమతిస్తున్నాను"</string> <string name="sms_control_no" msgid="4845717880040355570">"తిరస్కరిస్తున్నాను"</string> - <string name="sms_short_code_confirm_message" msgid="1385416688897538724">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ఒక సందేశాన్ని <b><xliff:g id="DEST_ADDRESS">%2$s</xliff:g></b>కి పంపాలనుకుంటోంది."</string> + <string name="sms_short_code_confirm_message" msgid="1385416688897538724">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ఒక మెసేజ్ను <b><xliff:g id="DEST_ADDRESS">%2$s</xliff:g></b>కి పంపాలనుకుంటోంది."</string> <string name="sms_short_code_details" msgid="2723725738333388351">"దీని వలన మీ మొబైల్ ఖాతాకు "<b>"ఛార్జీలు విధించబడవచ్చు"</b>"."</string> <string name="sms_premium_short_code_details" msgid="1400296309866638111"><b>"దీని వలన మీ మొబైల్ ఖాతాకు ఛార్జీలు విధించబడవచ్చు."</b></string> <string name="sms_short_code_confirm_allow" msgid="920477594325526691">"పంపు"</string> @@ -1474,10 +1474,10 @@ <string name="ime_action_previous" msgid="6548799326860401611">"మునుపటి"</string> <string name="ime_action_default" msgid="8265027027659800121">"అమలు చేయి"</string> <string name="dial_number_using" msgid="6060769078933953531">"<xliff:g id="NUMBER">%s</xliff:g>ని ఉపయోగించి\nనంబర్ డయల్ చేయండి"</string> - <string name="create_contact_using" msgid="6200708808003692594">"<xliff:g id="NUMBER">%s</xliff:g>ని ఉపయోగించి\nపరిచయాన్ని సృష్టించండి"</string> - <string name="grant_credentials_permission_message_header" msgid="5365733888842570481">"క్రింది ఒకటి లేదా అంతకంటే ఎక్కువ యాప్లు మీ ఖాతాను యాక్సెస్ చేయడానికి ఇప్పుడు మరియు భవిష్యత్తులో అనుమతిని అభ్యర్థించవచ్చు."</string> - <string name="grant_credentials_permission_message_footer" msgid="1886710210516246461">"మీరు ఈ అభ్యర్థనను అనుమతించాలనుకుంటున్నారా?"</string> - <string name="grant_permissions_header_text" msgid="3420736827804657201">"యాక్సెస్ అభ్యర్థన"</string> + <string name="create_contact_using" msgid="6200708808003692594">"<xliff:g id="NUMBER">%s</xliff:g>ని ఉపయోగించి\nకాంటాక్ట్ను క్రియేట్ చేయండి"</string> + <string name="grant_credentials_permission_message_header" msgid="5365733888842570481">"కింది ఒకటి లేదా అంతకంటే ఎక్కువ యాప్లు మీ ఖాతాను యాక్సెస్ చేయడానికి ఇప్పుడు మరియు భవిష్యత్తులో అనుమతిని అభ్యర్థించవచ్చు."</string> + <string name="grant_credentials_permission_message_footer" msgid="1886710210516246461">"మీరు ఈ రిక్వెస్ట్ను అనుమతించాలనుకుంటున్నారా?"</string> + <string name="grant_permissions_header_text" msgid="3420736827804657201">"యాక్సెస్ రిక్వెస్ట్"</string> <string name="allow" msgid="6195617008611933762">"అనుమతించండి"</string> <string name="deny" msgid="6632259981847676572">"తిరస్కరించండి"</string> <string name="permission_request_notification_title" msgid="1810025922441048273">"అనుమతి అభ్యర్థించబడింది"</string> @@ -1486,7 +1486,7 @@ <string name="forward_intent_to_owner" msgid="4620359037192871015">"మీరు మీ కార్యాలయ ప్రొఫైల్కు వెలుపల ఈ యాప్ను ఉపయోగిస్తున్నారు"</string> <string name="forward_intent_to_work" msgid="3620262405636021151">"మీరు మీ కార్యాలయ ప్రొఫైల్లో ఈ యాప్ను ఉపయోగిస్తున్నారు"</string> <string name="input_method_binding_label" msgid="1166731601721983656">"ఇన్పుట్ పద్ధతి"</string> - <string name="sync_binding_label" msgid="469249309424662147">"సమకాలీకరణ"</string> + <string name="sync_binding_label" msgid="469249309424662147">"సింక్"</string> <string name="accessibility_binding_label" msgid="1974602776545801715">"యాక్సెసిబిలిటీ"</string> <string name="wallpaper_binding_label" msgid="1197440498000786738">"వాల్పేపర్"</string> <string name="chooser_wallpaper" msgid="3082405680079923708">"వాల్పేపర్ను మార్చండి"</string> @@ -1525,8 +1525,8 @@ <string name="websearch" msgid="5624340204512793290">"వెబ్ శోధన"</string> <string name="find_next" msgid="5341217051549648153">"తదుపరిదాన్ని కనుగొను"</string> <string name="find_previous" msgid="4405898398141275532">"మునుపటిదాన్ని కనుగొను"</string> - <string name="gpsNotifTicker" msgid="3207361857637620780">"<xliff:g id="NAME">%s</xliff:g> నుండి స్థాన అభ్యర్థన"</string> - <string name="gpsNotifTitle" msgid="1590033371665669570">"స్థాన అభ్యర్థన"</string> + <string name="gpsNotifTicker" msgid="3207361857637620780">"<xliff:g id="NAME">%s</xliff:g> నుండి స్థాన రిక్వెస్ట్"</string> + <string name="gpsNotifTitle" msgid="1590033371665669570">"స్థాన రిక్వెస్ట్"</string> <string name="gpsNotifMessage" msgid="7346649122793758032">"<xliff:g id="NAME">%1$s</xliff:g> (<xliff:g id="SERVICE">%2$s</xliff:g>) ద్వారా అభ్యర్థించబడింది"</string> <string name="gpsVerifYes" msgid="3719843080744112940">"అవును"</string> <string name="gpsVerifNo" msgid="1671201856091564741">"కాదు"</string> @@ -1664,7 +1664,7 @@ <string name="kg_invalid_sim_puk_hint" msgid="2539364558870734339">"PUK కోడ్ 8 సంఖ్యలు ఉండాలి."</string> <string name="kg_invalid_puk" msgid="4809502818518963344">"సరైన PUK కోడ్ను మళ్లీ నమోదు చేయండి. పునరావృత ప్రయత్నాల వలన సిమ్ శాశ్వతంగా నిలిపివేయబడుతుంది."</string> <string name="kg_invalid_confirm_pin_hint" product="default" msgid="4705368340409816254">"పిన్ కోడ్లు సరిపోలలేదు"</string> - <string name="kg_login_too_many_attempts" msgid="699292728290654121">"చాలా ఎక్కువ ఆకృతి ప్రయత్నాలు చేసారు"</string> + <string name="kg_login_too_many_attempts" msgid="699292728290654121">"చాలా ఎక్కువ ఆకృతి ప్రయత్నాలు చేశారు"</string> <string name="kg_login_instructions" msgid="3619844310339066827">"అన్లాక్ చేయడానికి, మీ Google ఖాతాతో సైన్ ఇన్ చేయండి."</string> <string name="kg_login_username_hint" msgid="1765453775467133251">"వినియోగదారు పేరు (ఇమెయిల్)"</string> <string name="kg_login_password_hint" msgid="3330530727273164402">"పాస్వర్డ్"</string> @@ -1672,15 +1672,15 @@ <string name="kg_login_invalid_input" msgid="8292367491901220210">"చెల్లని వినియోగదారు పేరు లేదా పాస్వర్డ్."</string> <string name="kg_login_account_recovery_hint" msgid="4892466171043541248">"మీ వినియోగదారు పేరు లేదా పాస్వర్డ్ను మర్చిపోయారా?\n"<b>"google.com/accounts/recovery"</b>"ని సందర్శించండి."</string> <string name="kg_login_checking_password" msgid="4676010303243317253">"ఖాతాను తనిఖీ చేస్తోంది…"</string> - <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="23741434207544038">"మీరు మీ పిన్ను <xliff:g id="NUMBER_0">%1$d</xliff:g> సార్లు తప్పుగా టైప్ చేసారు. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> సెకన్లలో మళ్లీ ప్రయత్నించండి."</string> - <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="3328686432962224215">"మీరు మీ పాస్వర్డ్ను <xliff:g id="NUMBER_0">%1$d</xliff:g> సార్లు తప్పుగా టైప్ చేసారు. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> సెకన్లలో మళ్లీ ప్రయత్నించండి."</string> + <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="23741434207544038">"మీరు మీ పిన్ను <xliff:g id="NUMBER_0">%1$d</xliff:g> సార్లు తప్పుగా టైప్ చేశారు. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> సెకన్లలో మళ్లీ ప్రయత్నించండి."</string> + <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="3328686432962224215">"మీరు మీ పాస్వర్డ్ను <xliff:g id="NUMBER_0">%1$d</xliff:g> సార్లు తప్పుగా టైప్ చేశారు. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> సెకన్లలో మళ్లీ ప్రయత్నించండి."</string> <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="7357404233979139075">"మీరు మీ అన్లాక్ నమూనాను <xliff:g id="NUMBER_0">%1$d</xliff:g> సార్లు తప్పుగా గీసారు. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> సెకన్లలో మళ్లీ ప్రయత్నించండి."</string> - <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="3479940221343361587">"మీరు టాబ్లెట్ను అన్లాక్ చేయడానికి <xliff:g id="NUMBER_0">%1$d</xliff:g> చెల్లని ప్రయత్నాలు చేసారు. మరో <xliff:g id="NUMBER_1">%2$d</xliff:g> విఫల ప్రయత్నాల తర్వాత, టాబ్లెట్ ఫ్యాక్టరీ డిఫాల్ట్కు రీసెట్ చేయబడుతుంది మరియు మొత్తం వినియోగదారు డేటాను కోల్పోవడం సంభవిస్తుంది."</string> - <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="9064457748587850217">"మీరు మీ Android TV పరికరాన్ని అన్లాక్ చేయడానికి <xliff:g id="NUMBER_0">%1$d</xliff:g> సార్లు విఫల ప్రయత్నాలు చేసారు. మరో <xliff:g id="NUMBER_1">%2$d</xliff:g> విఫల ప్రయత్నాల తర్వాత, మీ Android TV పరికరం ఫ్యాక్టరీ డిఫాల్ట్కి రీసెట్ చేయబడుతుంది, అలాగే వినియోగదారు డేటా మొత్తాన్ని కోల్పోతారు."</string> - <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="5955398963754432548">"మీరు ఫోన్ను అన్లాక్ చేయడానికి <xliff:g id="NUMBER_0">%1$d</xliff:g> చెల్లని ప్రయత్నాలు చేసారు. మరో <xliff:g id="NUMBER_1">%2$d</xliff:g> విఫల ప్రయత్నాల తర్వాత, ఫోన్ ఫ్యాక్టరీ డిఫాల్ట్కు రీసెట్ చేయబడుతుంది మరియు మొత్తం వినియోగదారు డేటాను కోల్పోవడం సంభవిస్తుంది."</string> - <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2299099385175083308">"మీరు టాబ్లెట్ను అన్లాక్ చేయడానికి <xliff:g id="NUMBER">%d</xliff:g> చెల్లని ప్రయత్నాలు చేసారు. టాబ్లెట్ ఇప్పుడు ఫ్యాక్టరీ డిఫాల్ట్కు రీసెట్ చేయబడుతుంది."</string> - <string name="kg_failed_attempts_now_wiping" product="tv" msgid="5045460916106267585">"మీరు మీ Android TV పరికరాన్ని అన్లాక్ చేయడానికి <xliff:g id="NUMBER">%d</xliff:g> సార్లు విఫల ప్రయత్నాలు చేసారు. మీ Android TV పరికరం ఇప్పుడు ఫ్యాక్టరీ రీసెట్ చేయబడుతుంది."</string> - <string name="kg_failed_attempts_now_wiping" product="default" msgid="5043730590446071189">"మీరు ఫోన్ను అన్లాక్ చేయడానికి <xliff:g id="NUMBER">%d</xliff:g> చెల్లని ప్రయత్నాలు చేసారు. ఫోన్ ఇప్పుడు ఫ్యాక్టరీ డిఫాల్ట్కు రీసెట్ చేయబడుతుంది."</string> + <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="3479940221343361587">"మీరు టాబ్లెట్ను అన్లాక్ చేయడానికి <xliff:g id="NUMBER_0">%1$d</xliff:g> చెల్లని ప్రయత్నాలు చేశారు. మరో <xliff:g id="NUMBER_1">%2$d</xliff:g> విఫల ప్రయత్నాల తర్వాత, టాబ్లెట్ ఫ్యాక్టరీ డిఫాల్ట్కు రీసెట్ చేయబడుతుంది మరియు మొత్తం వినియోగదారు డేటాను కోల్పోవడం సంభవిస్తుంది."</string> + <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="9064457748587850217">"మీరు మీ Android TV పరికరాన్ని అన్లాక్ చేయడానికి <xliff:g id="NUMBER_0">%1$d</xliff:g> సార్లు విఫల ప్రయత్నాలు చేశారు. మరో <xliff:g id="NUMBER_1">%2$d</xliff:g> విఫల ప్రయత్నాల తర్వాత, మీ Android TV పరికరం ఫ్యాక్టరీ డిఫాల్ట్కి రీసెట్ చేయబడుతుంది, అలాగే వినియోగదారు డేటా మొత్తాన్ని కోల్పోతారు."</string> + <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="5955398963754432548">"మీరు ఫోన్ను అన్లాక్ చేయడానికి <xliff:g id="NUMBER_0">%1$d</xliff:g> చెల్లని ప్రయత్నాలు చేశారు. మరో <xliff:g id="NUMBER_1">%2$d</xliff:g> విఫల ప్రయత్నాల తర్వాత, ఫోన్ ఫ్యాక్టరీ డిఫాల్ట్కు రీసెట్ చేయబడుతుంది మరియు మొత్తం వినియోగదారు డేటాను కోల్పోవడం సంభవిస్తుంది."</string> + <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2299099385175083308">"మీరు టాబ్లెట్ను అన్లాక్ చేయడానికి <xliff:g id="NUMBER">%d</xliff:g> చెల్లని ప్రయత్నాలు చేశారు. టాబ్లెట్ ఇప్పుడు ఫ్యాక్టరీ డిఫాల్ట్కు రీసెట్ చేయబడుతుంది."</string> + <string name="kg_failed_attempts_now_wiping" product="tv" msgid="5045460916106267585">"మీరు మీ Android TV పరికరాన్ని అన్లాక్ చేయడానికి <xliff:g id="NUMBER">%d</xliff:g> సార్లు విఫల ప్రయత్నాలు చేశారు. మీ Android TV పరికరం ఇప్పుడు ఫ్యాక్టరీ రీసెట్ చేయబడుతుంది."</string> + <string name="kg_failed_attempts_now_wiping" product="default" msgid="5043730590446071189">"మీరు ఫోన్ను అన్లాక్ చేయడానికి <xliff:g id="NUMBER">%d</xliff:g> చెల్లని ప్రయత్నాలు చేశారు. ఫోన్ ఇప్పుడు ఫ్యాక్టరీ డిఫాల్ట్కు రీసెట్ చేయబడుతుంది."</string> <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="7086799295109717623">"మీరు మీ అన్లాక్ నమూనాను <xliff:g id="NUMBER_0">%1$d</xliff:g> సార్లు తప్పుగా గీసారు. మరో <xliff:g id="NUMBER_1">%2$d</xliff:g> విఫల ప్రయత్నాల తర్వాత, ఇమెయిల్ ఖాతాను ఉపయోగించి మీ టాబ్లెట్ను అన్లాక్ చేయాల్సిందిగా మిమ్మల్ని అడుగుతారు.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> సెకన్లలో మళ్లీ ప్రయత్నించండి."</string> <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4670840383567106114">"మీరు మీ అన్లాక్ నమూనాను <xliff:g id="NUMBER_0">%1$d</xliff:g> సార్లు తప్పుగా గీశారు. మరో <xliff:g id="NUMBER_1">%2$d</xliff:g> విఫల ప్రయత్నాల తర్వాత మీ Android TV పరికరాన్ని ఇమెయిల్ ఖాతా ద్వారా అన్లాక్ చేయాల్సిందిగా మిమ్మల్ని కోరడం జరుగుతుంది.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> సెకన్లలో మళ్లీ ప్రయత్నించండి."</string> <string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"మీరు మీ అన్లాక్ నమూనాను <xliff:g id="NUMBER_0">%1$d</xliff:g> సార్లు తప్పుగా గీసారు. మరో <xliff:g id="NUMBER_1">%2$d</xliff:g> విఫల ప్రయత్నాల తర్వాత, ఇమెయిల్ ఖాతాను ఉపయోగించి మీ ఫోన్ను అన్లాక్ చేయాల్సిందిగా మిమ్మల్ని అడుగుతారు.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> సెకన్లలో మళ్లీ ప్రయత్నించండి."</string> @@ -1713,8 +1713,8 @@ <string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> ఆఫ్ చేయబడింది"</string> <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"షార్ట్కట్లను ఎడిట్ చేయి"</string> <string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"పూర్తయింది"</string> - <string name="disable_accessibility_shortcut" msgid="5806091378745232383">"సత్వరమార్గాన్ని ఆఫ్ చేయి"</string> - <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"సత్వరమార్గాన్ని ఉపయోగించు"</string> + <string name="disable_accessibility_shortcut" msgid="5806091378745232383">"షార్ట్కట్ను ఆఫ్ చేయి"</string> + <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"షార్ట్కట్ను ఉపయోగించు"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"కలర్ మార్పిడి"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"కలర్ సరిచేయడం"</string> <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"వన్-హ్యాండెడ్ మోడ్"</string> @@ -1869,7 +1869,7 @@ <string name="lock_to_app_unlock_pin" msgid="3890940811866290782">"అన్పిన్ చేయడానికి ముందు పిన్ కోసం అడుగు"</string> <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"అన్పిన్ చేయడానికి ముందు అన్లాక్ ఆకృతి కోసం అడుగు"</string> <string name="lock_to_app_unlock_password" msgid="9126722403506560473">"అన్పిన్ చేయడానికి ముందు పాస్వర్డ్ కోసం అడుగు"</string> - <string name="package_installed_device_owner" msgid="7035926868974878525">"మీ నిర్వాహకులు ఇన్స్టాల్ చేసారు"</string> + <string name="package_installed_device_owner" msgid="7035926868974878525">"మీ నిర్వాహకులు ఇన్స్టాల్ చేశారు"</string> <string name="package_updated_device_owner" msgid="7560272363805506941">"మీ నిర్వాహకులు నవీకరించారు"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"మీ నిర్వాహకులు తొలగించారు"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"సరే"</string> @@ -1926,14 +1926,14 @@ <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> కొన్ని ధ్వనులను మ్యూట్ చేస్తోంది"</string> <string name="system_error_wipe_data" msgid="5910572292172208493">"మీ పరికరంతో అంతర్గత సమస్య ఏర్పడింది మరియు మీరు ఫ్యాక్టరీ డేటా రీసెట్ చేసే వరకు అస్థిరంగా ఉంటుంది."</string> <string name="system_error_manufacturer" msgid="703545241070116315">"మీ పరికరంతో అంతర్గత సమస్య ఏర్పడింది. వివరాల కోసం మీ తయారీదారుని సంప్రదించండి."</string> - <string name="stk_cc_ussd_to_dial" msgid="3139884150741157610">"USSD అభ్యర్థన సాధారణ కాల్కు మార్చబడింది"</string> - <string name="stk_cc_ussd_to_ss" msgid="4826846653052609738">"USSD అభ్యర్థన SS అభ్యర్థనకు మార్చబడింది"</string> - <string name="stk_cc_ussd_to_ussd" msgid="8343001461299302472">"కొత్త USSD అభ్యర్థనకు మార్చబడింది"</string> - <string name="stk_cc_ussd_to_dial_video" msgid="429118590323618623">"USSD అభ్యర్థన వీడియో కాల్కు మార్చబడింది"</string> - <string name="stk_cc_ss_to_dial" msgid="4087396658768717077">"SS అభ్యర్థన సాధారణ కాల్కి మార్చబడింది"</string> - <string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"SS అభ్యర్థన వీడియో కాల్కి మార్చబడింది"</string> - <string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"SS అభ్యర్థన USSD అభ్యర్థనకు మార్చబడింది"</string> - <string name="stk_cc_ss_to_ss" msgid="132040645206514450">"కొత్త SS అభ్యర్థనకు మార్చబడింది"</string> + <string name="stk_cc_ussd_to_dial" msgid="3139884150741157610">"USSD రిక్వెస్ట్ సాధారణ కాల్కు మార్చబడింది"</string> + <string name="stk_cc_ussd_to_ss" msgid="4826846653052609738">"USSD రిక్వెస్ట్ SS రిక్వెస్ట్కు మార్చబడింది"</string> + <string name="stk_cc_ussd_to_ussd" msgid="8343001461299302472">"కొత్త USSD రిక్వెస్ట్కు మార్చబడింది"</string> + <string name="stk_cc_ussd_to_dial_video" msgid="429118590323618623">"USSD రిక్వెస్ట్ వీడియో కాల్కు మార్చబడింది"</string> + <string name="stk_cc_ss_to_dial" msgid="4087396658768717077">"SS రిక్వెస్ట్ సాధారణ కాల్కి మార్చబడింది"</string> + <string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"SS రిక్వెస్ట్ వీడియో కాల్కి మార్చబడింది"</string> + <string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"SS రిక్వెస్ట్ USSD రిక్వెస్ట్కు మార్చబడింది"</string> + <string name="stk_cc_ss_to_ss" msgid="132040645206514450">"కొత్త SS రిక్వెస్ట్కు మార్చబడింది"</string> <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"ఫిషింగ్ అలర్ట్"</string> <string name="notification_work_profile_content_description" msgid="5296477955677725799">"ఆఫీస్ ప్రొఫైల్"</string> <string name="notification_alerted_content_description" msgid="6139691253611265992">"హెచ్చరించబడింది"</string> @@ -1961,7 +1961,7 @@ <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> ఎంచుకోబడింది</item> </plurals> <string name="default_notification_channel_label" msgid="3697928973567217330">"వర్గీకరించబడలేదు"</string> - <string name="importance_from_user" msgid="2782756722448800447">"మీరు ఈ నోటిఫికేషన్ల ప్రాముఖ్యతను సెట్ చేసారు."</string> + <string name="importance_from_user" msgid="2782756722448800447">"మీరు ఈ నోటిఫికేషన్ల ప్రాముఖ్యతను సెట్ చేశారు."</string> <string name="importance_from_person" msgid="4235804979664465383">"ఇందులో పేర్కొనబడిన వ్యక్తులను బట్టి ఇది చాలా ముఖ్యమైనది."</string> <string name="notification_history_title_placeholder" msgid="7748630986182249599">"అనుకూల యాప్ నోటిఫికేషన్"</string> <string name="user_creation_account_exists" msgid="2239146360099708035">"<xliff:g id="ACCOUNT">%2$s</xliff:g>తో కొత్త వినియోగదారుని సృష్టించడానికి <xliff:g id="APP">%1$s</xliff:g>ను అనుమతించాలా (ఈ ఖాతాతో ఇప్పటికే ఒక వినియోగదారు ఉన్నారు) ?"</string> @@ -1984,7 +1984,7 @@ <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> ప్రస్తుతం అందుబాటులో లేదు."</string> <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"ఈ యాప్ పాత వెర్షన్ Android కోసం రూపొందించబడింది మరియు అది సరిగ్గా పని చేయకపోవచ్చు. అప్డేట్ల కోసం తనిఖీ చేయడానికి ప్రయత్నించండి లేదా డెవలపర్ని సంప్రదించండి."</string> <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"అప్డేట్ కోసం తనిఖీ చేయండి"</string> - <string name="new_sms_notification_title" msgid="6528758221319927107">"మీకు కొత్త సందేశాలు ఉన్నాయి"</string> + <string name="new_sms_notification_title" msgid="6528758221319927107">"మీకు కొత్త మెసేజ్లు ఉన్నాయి"</string> <string name="new_sms_notification_content" msgid="3197949934153460639">"వీక్షించడానికి SMS యాప్ను తెరవండి"</string> <string name="profile_encrypted_title" msgid="9001208667521266472">"కొంత ఫంక్షనాలిటీ పరిమితం కావచ్చు"</string> <string name="profile_encrypted_detail" msgid="5279730442756849055">"కార్యాలయ ప్రొఫైల్ లాక్ అయింది"</string> @@ -2041,20 +2041,20 @@ <string name="autofill_save_notnow" msgid="2853932672029024195">"ఇప్పుడు కాదు"</string> <string name="autofill_save_never" msgid="6821841919831402526">"ఎప్పుడూ వద్దు"</string> <string name="autofill_update_yes" msgid="4608662968996874445">"అప్డేట్ చేయి"</string> - <string name="autofill_continue_yes" msgid="7914985605534510385">"కొనసాగించు"</string> + <string name="autofill_continue_yes" msgid="7914985605534510385">"కొనసాగించండి"</string> <string name="autofill_save_type_password" msgid="5624528786144539944">"పాస్వర్డ్"</string> - <string name="autofill_save_type_address" msgid="3111006395818252885">"చిరునామా"</string> + <string name="autofill_save_type_address" msgid="3111006395818252885">"అడ్రస్"</string> <string name="autofill_save_type_credit_card" msgid="3583795235862046693">"క్రెడిట్ కార్డ్"</string> <string name="autofill_save_type_debit_card" msgid="3169397504133097468">"డెబిట్ కార్డ్"</string> <string name="autofill_save_type_payment_card" msgid="6555012156728690856">"చెల్లింపు కార్డ్"</string> <string name="autofill_save_type_generic_card" msgid="1019367283921448608">"కార్డ్"</string> <string name="autofill_save_type_username" msgid="1018816929884640882">"వినియోగదారు పేరు"</string> - <string name="autofill_save_type_email_address" msgid="1303262336895591924">"ఇమెయిల్ చిరునామా"</string> + <string name="autofill_save_type_email_address" msgid="1303262336895591924">"ఇమెయిల్ అడ్రస్"</string> <string name="etws_primary_default_message_earthquake" msgid="8401079517718280669">"ప్రశాంతంగా ఉండండి మరియు దగ్గర్లో తలదాచుకోండి."</string> <string name="etws_primary_default_message_tsunami" msgid="5828171463387976279">"వెంటనే తీర ప్రాంతాలు మరియు నదీ పరీవాహక ప్రాంతాలను ఖాళీ చేసి మెట్ట ప్రాంతాలకు తరలి వెళ్లండి."</string> <string name="etws_primary_default_message_earthquake_and_tsunami" msgid="4888224011071875068">"ప్రశాంతంగా ఉండండి మరియు దగ్గర్లో తలదాచుకోండి."</string> - <string name="etws_primary_default_message_test" msgid="4583367373909549421">"అత్యవసర సందేశాల పరీక్ష"</string> - <string name="notification_reply_button_accessibility" msgid="5235776156579456126">"ప్రత్యుత్తరం పంపండి"</string> + <string name="etws_primary_default_message_test" msgid="4583367373909549421">"అత్యవసర మెసేజ్ల పరీక్ష"</string> + <string name="notification_reply_button_accessibility" msgid="5235776156579456126">"రిప్లయి పంపండి"</string> <string name="etws_primary_default_message_others" msgid="7958161706019130739"></string> <string name="mmcc_authentication_reject" msgid="4891965994643876369">"వాయిస్ కోసం SIM అనుమతించబడదు"</string> <string name="mmcc_imsi_unknown_in_hlr" msgid="227760698553988751">"వాయిస్ కోసం SIM సదుపాయం లేదు"</string> @@ -2066,10 +2066,10 @@ <string name="mmcc_illegal_me_msim_template" msgid="4802735138861422802">"SIM <xliff:g id="SIMNUMBER">%d</xliff:g> అనుమతించబడదు"</string> <string name="popup_window_default_title" msgid="6907717596694826919">"పాప్అప్ విండో"</string> <string name="slice_more_content" msgid="3377367737876888459">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string> - <string name="shortcut_restored_on_lower_version" msgid="9206301954024286063">"యాప్ వెర్షన్ డౌన్గ్రేడ్ చేయబడింది లేదా ఈ సత్వరమార్గంతో అనుకూలంగా లేదు"</string> - <string name="shortcut_restore_not_supported" msgid="4763198938588468400">"బ్యాకప్ మరియు పునరుద్ధరణకు యాప్ మద్దతు ఇవ్వని కారణంగా సత్వరమార్గాన్ని పునరుద్ధరించడం సాధ్యపడలేదు"</string> - <string name="shortcut_restore_signature_mismatch" msgid="579345304221605479">"యాప్ సంతకం సరిపోలని కారణంగా సత్వరమార్గాన్ని పునరుద్ధరించడం సాధ్యపడలేదు"</string> - <string name="shortcut_restore_unknown_issue" msgid="2478146134395982154">"సత్వరమార్గాన్ని పునరుద్ధరించడం సాధ్యపడలేదు"</string> + <string name="shortcut_restored_on_lower_version" msgid="9206301954024286063">"యాప్ వెర్షన్ డౌన్గ్రేడ్ చేయబడింది లేదా ఈ షార్ట్కట్తో అనుకూలంగా లేదు"</string> + <string name="shortcut_restore_not_supported" msgid="4763198938588468400">"బ్యాకప్ మరియు పునరుద్ధరణకు యాప్ మద్దతు ఇవ్వని కారణంగా షార్ట్కట్ను పునరుద్ధరించడం సాధ్యపడలేదు"</string> + <string name="shortcut_restore_signature_mismatch" msgid="579345304221605479">"యాప్ సంతకం సరిపోలని కారణంగా షార్ట్కట్ను పునరుద్ధరించడం సాధ్యపడలేదు"</string> + <string name="shortcut_restore_unknown_issue" msgid="2478146134395982154">"షార్ట్కట్ను పునరుద్ధరించడం సాధ్యపడలేదు"</string> <string name="shortcut_disabled_reason_unknown" msgid="753074793553599166">"షార్ట్కట్ నిలిపివేయబడింది"</string> <string name="harmful_app_warning_uninstall" msgid="6472912975664191772">"అన్ఇన్స్టాల్ చేయండి"</string> <string name="harmful_app_warning_open_anyway" msgid="5963657791740211807">"ఏదేమైనా తెరువు"</string> @@ -2133,7 +2133,7 @@ <item quantity="one"><xliff:g id="FILE_NAME_0">%s</xliff:g> + <xliff:g id="COUNT_1">%d</xliff:g> ఫైల్</item> </plurals> <string name="chooser_no_direct_share_targets" msgid="1511722103987329028">"ఎవరికి షేర్ చేయాలనే దానికి సంబంధించి సిఫార్సులేవీ లేవు"</string> - <string name="chooser_all_apps_button_label" msgid="3230427756238666328">"యాప్ల జాబితా"</string> + <string name="chooser_all_apps_button_label" msgid="3230427756238666328">"యాప్ల లిస్ట్"</string> <string name="usb_device_resolve_prompt_warn" msgid="325871329788064199">"ఈ యాప్కు రికార్డ్ చేసే అనుమతి మంజూరు కాలేదు, అయినా ఈ USB పరికరం ద్వారా ఆడియోను క్యాప్చర్ చేయగలదు."</string> <string name="accessibility_system_action_home_label" msgid="3234748160850301870">"హోమ్"</string> <string name="accessibility_system_action_back_label" msgid="4205361367345537608">"వెనుకకు"</string> @@ -2225,17 +2225,17 @@ <string name="PERSOSUBSTATE_RUIM_CORPORATE_PUK_IN_PROGRESS" msgid="2695664012344346788">"PUK అన్లాక్ను అభ్యర్థిస్తోంది…"</string> <string name="PERSOSUBSTATE_RUIM_SERVICE_PROVIDER_PUK_IN_PROGRESS" msgid="2695678959963807782">"PUK అన్లాక్ను అభ్యర్థిస్తోంది…"</string> <string name="PERSOSUBSTATE_RUIM_RUIM_PUK_IN_PROGRESS" msgid="1230605365926493599">"PUK అన్లాక్ను అభ్యర్థిస్తోంది…"</string> - <string name="PERSOSUBSTATE_SIM_NETWORK_ERROR" msgid="1924844017037151535">"SIM నెట్వర్క్ అన్లాక్ అభ్యర్థన విఫలమైంది."</string> - <string name="PERSOSUBSTATE_SIM_NETWORK_SUBSET_ERROR" msgid="3372797822292089708">"SIM నెట్వర్క్ సబ్సెట్ అన్లాక్ అభ్యర్థన విఫలమైంది."</string> - <string name="PERSOSUBSTATE_SIM_SERVICE_PROVIDER_ERROR" msgid="1878443146720411381">"SIM సర్వీస్ ప్రొవైడర్ అన్లాక్ అభ్యర్థన విఫలమైంది."</string> - <string name="PERSOSUBSTATE_SIM_CORPORATE_ERROR" msgid="7664778312218023192">"SIM కార్పొరేట్ అన్లాక్ అభ్యర్థన విఫలమైంది."</string> - <string name="PERSOSUBSTATE_SIM_SIM_ERROR" msgid="2472944311643350302">"SIM అన్లాక్ అభ్యర్థన విఫలమైంది."</string> - <string name="PERSOSUBSTATE_RUIM_NETWORK1_ERROR" msgid="828089694480999120">"RUIM నెట్వర్క్ అన్లాక్ అభ్యర్థన విఫలమైంది."</string> - <string name="PERSOSUBSTATE_RUIM_NETWORK2_ERROR" msgid="17619001007092511">"RUIM నెట్వర్క్2 అన్లాక్ అభ్యర్థన విఫలమైంది."</string> - <string name="PERSOSUBSTATE_RUIM_HRPD_ERROR" msgid="807214229604353614">"RUIM Hrpd అన్లాక్ అభ్యర్థన విఫలమైంది."</string> - <string name="PERSOSUBSTATE_RUIM_CORPORATE_ERROR" msgid="8644184447744175747">"RUIM కార్పొరేట్ అన్లాక్ అభ్యర్థన విఫలమైంది."</string> - <string name="PERSOSUBSTATE_RUIM_SERVICE_PROVIDER_ERROR" msgid="3801002648649640407">"RUIM సర్వీస్ ప్రొవైడర్ అన్లాక్ అభ్యర్థన విఫలమైంది."</string> - <string name="PERSOSUBSTATE_RUIM_RUIM_ERROR" msgid="707397021218680753">"RUIM అన్లాక్ అభ్యర్థన విఫలమైంది."</string> + <string name="PERSOSUBSTATE_SIM_NETWORK_ERROR" msgid="1924844017037151535">"SIM నెట్వర్క్ అన్లాక్ రిక్వెస్ట్ విఫలమైంది."</string> + <string name="PERSOSUBSTATE_SIM_NETWORK_SUBSET_ERROR" msgid="3372797822292089708">"SIM నెట్వర్క్ సబ్సెట్ అన్లాక్ రిక్వెస్ట్ విఫలమైంది."</string> + <string name="PERSOSUBSTATE_SIM_SERVICE_PROVIDER_ERROR" msgid="1878443146720411381">"SIM సర్వీస్ ప్రొవైడర్ అన్లాక్ రిక్వెస్ట్ విఫలమైంది."</string> + <string name="PERSOSUBSTATE_SIM_CORPORATE_ERROR" msgid="7664778312218023192">"SIM కార్పొరేట్ అన్లాక్ రిక్వెస్ట్ విఫలమైంది."</string> + <string name="PERSOSUBSTATE_SIM_SIM_ERROR" msgid="2472944311643350302">"SIM అన్లాక్ రిక్వెస్ట్ విఫలమైంది."</string> + <string name="PERSOSUBSTATE_RUIM_NETWORK1_ERROR" msgid="828089694480999120">"RUIM నెట్వర్క్ అన్లాక్ రిక్వెస్ట్ విఫలమైంది."</string> + <string name="PERSOSUBSTATE_RUIM_NETWORK2_ERROR" msgid="17619001007092511">"RUIM నెట్వర్క్2 అన్లాక్ రిక్వెస్ట్ విఫలమైంది."</string> + <string name="PERSOSUBSTATE_RUIM_HRPD_ERROR" msgid="807214229604353614">"RUIM Hrpd అన్లాక్ రిక్వెస్ట్ విఫలమైంది."</string> + <string name="PERSOSUBSTATE_RUIM_CORPORATE_ERROR" msgid="8644184447744175747">"RUIM కార్పొరేట్ అన్లాక్ రిక్వెస్ట్ విఫలమైంది."</string> + <string name="PERSOSUBSTATE_RUIM_SERVICE_PROVIDER_ERROR" msgid="3801002648649640407">"RUIM సర్వీస్ ప్రొవైడర్ అన్లాక్ రిక్వెస్ట్ విఫలమైంది."</string> + <string name="PERSOSUBSTATE_RUIM_RUIM_ERROR" msgid="707397021218680753">"RUIM అన్లాక్ రిక్వెస్ట్ విఫలమైంది."</string> <string name="PERSOSUBSTATE_SIM_NETWORK_PUK_ERROR" msgid="894358680773257820">"PUK అన్లాక్ విజయవంతం కాలేదు."</string> <string name="PERSOSUBSTATE_SIM_NETWORK_SUBSET_PUK_ERROR" msgid="352466878146726991">"PUK అన్లాక్ విజయవంతం కాలేదు."</string> <string name="PERSOSUBSTATE_SIM_CORPORATE_PUK_ERROR" msgid="7353389721907138671">"PUK అన్లాక్ విజయవంతం కాలేదు."</string> @@ -2247,11 +2247,11 @@ <string name="PERSOSUBSTATE_RUIM_SERVICE_PROVIDER_PUK_ERROR" msgid="5178635064113393143">"PUK అన్లాక్ విజయవంతం కాలేదు."</string> <string name="PERSOSUBSTATE_RUIM_RUIM_PUK_ERROR" msgid="5391587926974531008">"PUK అన్లాక్ విజయవంతం కాలేదు."</string> <string name="PERSOSUBSTATE_RUIM_CORPORATE_PUK_ERROR" msgid="4895494864493315868">"PUK అన్లాక్ విజయవంతం కాలేదు."</string> - <string name="PERSOSUBSTATE_SIM_SPN_ERROR" msgid="9017576601595353649">"SPN అన్లాక్ అభ్యర్థన విఫలమైంది."</string> - <string name="PERSOSUBSTATE_SIM_SP_EHPLMN_ERROR" msgid="1116993930995545742">"SP Equivalent Home PLMN అన్లాక్ అభ్యర్థన విఫలమైంది."</string> - <string name="PERSOSUBSTATE_SIM_ICCID_ERROR" msgid="7559167306794441462">"ICCID అన్లాక్ అభ్యర్థన విఫలమైంది."</string> - <string name="PERSOSUBSTATE_SIM_IMPI_ERROR" msgid="2782926139511136588">"IMPI నెట్వర్క్ అన్లాక్ అభ్యర్థన విఫలమైంది."</string> - <string name="PERSOSUBSTATE_SIM_NS_SP_ERROR" msgid="1890493954453456758">"నెట్వర్క్ సబ్సెట్ సర్వీస్ ప్రొవైడర్ అన్లాక్ అభ్యర్థన విఫలమైంది."</string> + <string name="PERSOSUBSTATE_SIM_SPN_ERROR" msgid="9017576601595353649">"SPN అన్లాక్ రిక్వెస్ట్ విఫలమైంది."</string> + <string name="PERSOSUBSTATE_SIM_SP_EHPLMN_ERROR" msgid="1116993930995545742">"SP Equivalent Home PLMN అన్లాక్ రిక్వెస్ట్ విఫలమైంది."</string> + <string name="PERSOSUBSTATE_SIM_ICCID_ERROR" msgid="7559167306794441462">"ICCID అన్లాక్ రిక్వెస్ట్ విఫలమైంది."</string> + <string name="PERSOSUBSTATE_SIM_IMPI_ERROR" msgid="2782926139511136588">"IMPI నెట్వర్క్ అన్లాక్ రిక్వెస్ట్ విఫలమైంది."</string> + <string name="PERSOSUBSTATE_SIM_NS_SP_ERROR" msgid="1890493954453456758">"నెట్వర్క్ సబ్సెట్ సర్వీస్ ప్రొవైడర్ అన్లాక్ రిక్వెస్ట్ విఫలమైంది."</string> <string name="PERSOSUBSTATE_SIM_NETWORK_SUCCESS" msgid="4886243367747126325">"SIM నెట్వర్క్ అన్లాక్ విజయవంతమైంది."</string> <string name="PERSOSUBSTATE_SIM_NETWORK_SUBSET_SUCCESS" msgid="4053809277733513987">"SIM నెట్వర్క్ సబ్సెట్ అన్లాక్ విజయవంతమైంది."</string> <string name="PERSOSUBSTATE_SIM_SERVICE_PROVIDER_SUCCESS" msgid="8249342930499801740">"SIM సర్వీస్ ప్రొవైడర్ అన్లాక్ విజయవంతమైంది."</string> @@ -2259,9 +2259,9 @@ <string name="PERSOSUBSTATE_SIM_SIM_SUCCESS" msgid="6975608174152828954">"SIM అన్లాక్ విజయవంతమైంది."</string> <string name="PERSOSUBSTATE_RUIM_NETWORK1_SUCCESS" msgid="2846699261330463192">"RUIM నెట్వర్క్1 అన్లాక్ విజయవంతమైంది."</string> <string name="PERSOSUBSTATE_RUIM_NETWORK2_SUCCESS" msgid="5335414726057102801">"RUIM నెట్వర్క్2 అన్లాక్ విజయవంతమైంది."</string> - <string name="PERSOSUBSTATE_RUIM_HRPD_SUCCESS" msgid="8868100318474971969">"RUIM Hrpd అన్లాక్ అభ్యర్థన విజయవంతమైంది."</string> + <string name="PERSOSUBSTATE_RUIM_HRPD_SUCCESS" msgid="8868100318474971969">"RUIM Hrpd అన్లాక్ రిక్వెస్ట్ విజయవంతమైంది."</string> <string name="PERSOSUBSTATE_RUIM_SERVICE_PROVIDER_SUCCESS" msgid="6020936629725666932">"RUIM సర్వీస్ ప్రొవైడర్ అన్లాక్ విజయవంతమైంది."</string> - <string name="PERSOSUBSTATE_RUIM_CORPORATE_SUCCESS" msgid="6944873647584595489">"RUIM కార్పొరేట్ అన్లాక్ అభ్యర్థన విజయవంతమైంది."</string> + <string name="PERSOSUBSTATE_RUIM_CORPORATE_SUCCESS" msgid="6944873647584595489">"RUIM కార్పొరేట్ అన్లాక్ రిక్వెస్ట్ విజయవంతమైంది."</string> <string name="PERSOSUBSTATE_RUIM_RUIM_SUCCESS" msgid="2526483514124121988">"RUIM అన్లాక్ విజయవంతమైంది."</string> <string name="PERSOSUBSTATE_SIM_NETWORK_PUK_SUCCESS" msgid="7662200333621664621">"PUK అన్లాక్ విజయవంతమైంది."</string> <string name="PERSOSUBSTATE_SIM_NETWORK_SUBSET_PUK_SUCCESS" msgid="2861223407953766632">"PUK అన్లాక్ విజయవంతమైంది."</string> diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml index 68a5ce7ee349..cc38de217a7b 100644 --- a/core/res/res/values-zh-rCN/strings.xml +++ b/core/res/res/values-zh-rCN/strings.xml @@ -327,17 +327,17 @@ <string name="permgrouplab_sensors" msgid="9134046949784064495">"身体传感器"</string> <string name="permgroupdesc_sensors" msgid="2610631290633747752">"访问与您的生命体征相关的传感器数据"</string> <string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"检索窗口内容"</string> - <string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"检测您正访问的窗口的内容。"</string> + <string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"检测您与之互动的窗口的内容。"</string> <string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"启用触摸浏览"</string> <string name="capability_desc_canRequestTouchExploration" msgid="4394677060796752976">"设备将大声读出您点按的内容,同时您可以通过手势来浏览屏幕。"</string> <string name="capability_title_canRequestFilterKeyEvents" msgid="2772371671541753254">"监测您输入的文字"</string> - <string name="capability_desc_canRequestFilterKeyEvents" msgid="2381315802405773092">"包含个人数据,例如信用卡号和密码。"</string> - <string name="capability_title_canControlMagnification" msgid="7701572187333415795">"控制显示内容放大功能"</string> - <string name="capability_desc_canControlMagnification" msgid="2206586716709254805">"控制显示内容的缩放级别和位置。"</string> + <string name="capability_desc_canRequestFilterKeyEvents" msgid="2381315802405773092">"包括信用卡号和密码等个人数据。"</string> + <string name="capability_title_canControlMagnification" msgid="7701572187333415795">"控制屏幕内容放大功能"</string> + <string name="capability_desc_canControlMagnification" msgid="2206586716709254805">"控制屏幕内容的缩放级别和位置。"</string> <string name="capability_title_canPerformGestures" msgid="9106545062106728987">"执行手势"</string> <string name="capability_desc_canPerformGestures" msgid="6619457251067929726">"可执行点按、滑动、双指张合等手势。"</string> <string name="capability_title_canCaptureFingerprintGestures" msgid="1189053104594608091">"指纹手势"</string> - <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"可以捕获在设备指纹传感器上执行的手势。"</string> + <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"可以捕捉在设备指纹传感器上执行的手势。"</string> <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"截取屏幕截图"</string> <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"可截取显示画面的屏幕截图。"</string> <string name="permlab_statusBar" msgid="8798267849526214017">"停用或修改状态栏"</string> diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml index 798e06c3f53c..1c02c6bbb285 100644 --- a/core/res/res/values-zh-rTW/strings.xml +++ b/core/res/res/values-zh-rTW/strings.xml @@ -246,7 +246,7 @@ <string name="global_action_power_off" msgid="4404936470711393203">"關機"</string> <string name="global_action_power_options" msgid="1185286119330160073">"電源"</string> <string name="global_action_restart" msgid="4678451019561687074">"重新啟動"</string> - <string name="global_action_emergency" msgid="1387617624177105088">"緊急電話"</string> + <string name="global_action_emergency" msgid="1387617624177105088">"緊急撥號"</string> <string name="global_action_bug_report" msgid="5127867163044170003">"錯誤報告"</string> <string name="global_action_logout" msgid="6093581310002476511">"結束"</string> <string name="global_action_screenshot" msgid="2610053466156478564">"螢幕截圖"</string> diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml index 55ed83b32262..b191584345ef 100644 --- a/core/res/res/values/colors.xml +++ b/core/res/res/values/colors.xml @@ -248,37 +248,37 @@ <color name="system_accent1_0">#ffffff</color> <!-- Shade of the accent system color at 99% lightness. This value can be overlaid at runtime by OverlayManager RROs. --> - <color name="system_accent1_10">#F1FFFC</color> + <color name="system_accent1_10">#F9FCFF</color> <!-- Shade of the accent system color at 95% lightness. This value can be overlaid at runtime by OverlayManager RROs. --> - <color name="system_accent1_50">#9CFFF2</color> + <color name="system_accent1_50">#E0F3FF</color> <!-- Shade of the accent system color at 90% lightness. This value can be overlaid at runtime by OverlayManager RROs. --> - <color name="system_accent1_100">#8DF5E3</color> + <color name="system_accent1_100">#C1E8FF</color> <!-- Shade of the accent system color at 80% lightness. This value can be overlaid at runtime by OverlayManager RROs. --> - <color name="system_accent1_200">#71D8C7</color> + <color name="system_accent1_200">#76D1FF</color> <!-- Shade of the accent system color at 70% lightness. This value can be overlaid at runtime by OverlayManager RROs. --> - <color name="system_accent1_300">#53BCAC</color> + <color name="system_accent1_300">#4BB6E8</color> <!-- Shade of the accent system color at 60% lightness. This value can be overlaid at runtime by OverlayManager RROs. --> - <color name="system_accent1_400">#34A192</color> + <color name="system_accent1_400">#219BCC</color> <!-- Shade of the accent system color at 49% lightness. This value can be overlaid at runtime by OverlayManager RROs. --> - <color name="system_accent1_500">#008375</color> + <color name="system_accent1_500">#007FAC</color> <!-- Shade of the accent system color at 40% lightness. This value can be overlaid at runtime by OverlayManager RROs. --> - <color name="system_accent1_600">#006C5F</color> + <color name="system_accent1_600">#00668B</color> <!-- Shade of the accent system color at 30% lightness. This value can be overlaid at runtime by OverlayManager RROs. --> - <color name="system_accent1_700">#005747</color> + <color name="system_accent1_700">#004C69</color> <!-- Shade of the accent system color at 20% lightness. This value can be overlaid at runtime by OverlayManager RROs. --> - <color name="system_accent1_800">#003E31</color> + <color name="system_accent1_800">#003549</color> <!-- Shade of the accent system color at 10% lightness. This value can be overlaid at runtime by OverlayManager RROs. --> - <color name="system_accent1_900">#002214</color> + <color name="system_accent1_900">#001E2C</color> <!-- Darkest shade of the accent color used by the system. Black. This value can be overlaid at runtime by OverlayManager RROs. --> <color name="system_accent1_1000">#000000</color> @@ -288,37 +288,37 @@ <color name="system_accent2_0">#ffffff</color> <!-- Shade of the secondary accent system color at 99% lightness. This value can be overlaid at runtime by OverlayManager RROs. --> - <color name="system_accent2_10">#F0FFFC</color> + <color name="system_accent2_10">#F9FCFF</color> <!-- Shade of the secondary accent system color at 95% lightness. This value can be overlaid at runtime by OverlayManager RROs. --> - <color name="system_accent2_50">#CDFAF1</color> + <color name="system_accent2_50">#E0F3FF</color> <!-- Shade of the secondary accent system color at 90% lightness. This value can be overlaid at runtime by OverlayManager RROs. --> - <color name="system_accent2_100">#BFEBE3</color> + <color name="system_accent2_100">#D1E5F4</color> <!-- Shade of the secondary accent system color at 80% lightness. This value can be overlaid at runtime by OverlayManager RROs. --> - <color name="system_accent2_200">#A4CFC7</color> + <color name="system_accent2_200">#B5CAD7</color> <!-- Shade of the secondary accent system color at 70% lightness. This value can be overlaid at runtime by OverlayManager RROs. --> - <color name="system_accent2_300">#89B4AC</color> + <color name="system_accent2_300">#9AAEBB</color> <!-- Shade of the secondary accent system color at 60% lightness. This value can be overlaid at runtime by OverlayManager RROs. --> - <color name="system_accent2_400">#6F9991</color> + <color name="system_accent2_400">#8094A0</color> <!-- Shade of the secondary accent system color at 49% lightness. This value can be overlaid at runtime by OverlayManager RROs. --> - <color name="system_accent2_500">#537C75</color> + <color name="system_accent2_500">#657985</color> <!-- Shade of the secondary accent system color at 40% lightness. This value can be overlaid at runtime by OverlayManager RROs. --> - <color name="system_accent2_600">#3D665F</color> + <color name="system_accent2_600">#4E616C</color> <!-- Shade of the secondary accent system color at 30% lightness. This value can be overlaid at runtime by OverlayManager RROs. --> - <color name="system_accent2_700">#254E47</color> + <color name="system_accent2_700">#374955</color> <!-- Shade of the secondary accent system color at 20% lightness. This value can be overlaid at runtime by OverlayManager RROs. --> - <color name="system_accent2_800">#0C3731</color> + <color name="system_accent2_800">#20333D</color> <!-- Shade of the secondary accent system color at 10% lightness. This value can be overlaid at runtime by OverlayManager RROs. --> - <color name="system_accent2_900">#00211C</color> + <color name="system_accent2_900">#091E28</color> <!-- Darkest shade of the secondary accent color used by the system. Black. This value can be overlaid at runtime by OverlayManager RROs. --> <color name="system_accent2_1000">#000000</color> @@ -331,34 +331,34 @@ <color name="system_accent3_10">#FFFBFF</color> <!-- Shade of the tertiary accent system color at 95% lightness. This value can be overlaid at runtime by OverlayManager RROs. --> - <color name="system_accent3_50">#F9EAFF</color> + <color name="system_accent3_50">#F5EEFF</color> <!-- Shade of the tertiary accent system color at 90% lightness. This value can be overlaid at runtime by OverlayManager RROs. --> - <color name="system_accent3_100">#ECDBFF</color> + <color name="system_accent3_100">#E6DEFF</color> <!-- Shade of the tertiary accent system color at 80% lightness. This value can be overlaid at runtime by OverlayManager RROs. --> - <color name="system_accent3_200">#CFBFEB</color> + <color name="system_accent3_200">#CAC1EA</color> <!-- Shade of the tertiary accent system color at 70% lightness. This value can be overlaid at runtime by OverlayManager RROs. --> - <color name="system_accent3_300">#B3A4CF</color> + <color name="system_accent3_300">#AEA6CE</color> <!-- Shade of the tertiary accent system color at 60% lightness. This value can be overlaid at runtime by OverlayManager RROs. --> - <color name="system_accent3_400">#988AB3</color> + <color name="system_accent3_400">#938CB1</color> <!-- Shade of the tertiary accent system color at 49% lightness. This value can be overlaid at runtime by OverlayManager RROs. --> - <color name="system_accent3_500">#7B6E96</color> + <color name="system_accent3_500">#787296</color> <!-- Shade of the tertiary accent system color at 40% lightness. This value can be overlaid at runtime by OverlayManager RROs. --> - <color name="system_accent3_600">#64587F</color> + <color name="system_accent3_600">#605A7C</color> <!-- Shade of the tertiary accent system color at 30% lightness. This value can be overlaid at runtime by OverlayManager RROs. --> - <color name="system_accent3_700">#4C4165</color> + <color name="system_accent3_700">#484264</color> <!-- Shade of the tertiary accent system color at 20% lightness. This value can be overlaid at runtime by OverlayManager RROs. --> - <color name="system_accent3_800">#352B4D</color> + <color name="system_accent3_800">#322C4C</color> <!-- Shade of the tertiary accent system color at 10% lightness. This value can be overlaid at runtime by OverlayManager RROs. --> - <color name="system_accent3_900">#1E1636</color> + <color name="system_accent3_900">#1D1736</color> <!-- Darkest shade of the tertiary accent color used by the system. Black. This value can be overlaid at runtime by OverlayManager RROs. --> <color name="system_accent3_1000">#000000</color> @@ -368,37 +368,37 @@ <color name="system_neutral1_0">#ffffff</color> <!-- Shade of the neutral system color at 99% lightness. This value can be overlaid at runtime by OverlayManager RROs. --> - <color name="system_neutral1_10">#fbfbfb</color> + <color name="system_neutral1_10">#FCFCFF</color> <!-- Shade of the neutral system color at 95% lightness. This value can be overlaid at runtime by OverlayManager RROs. --> - <color name="system_neutral1_50">#f0f0f0</color> + <color name="system_neutral1_50">#F0F0F3</color> <!-- Shade of the neutral system color at 90% lightness. This value can be overlaid at runtime by OverlayManager RROs. --> - <color name="system_neutral1_100">#e2e2e2</color> + <color name="system_neutral1_100">#E1E3E5</color> <!-- Shade of the neutral system color at 80% lightness. This value can be overlaid at runtime by OverlayManager RROs. --> - <color name="system_neutral1_200">#c6c6c6</color> + <color name="system_neutral1_200">#C5C7C9</color> <!-- Shade of the neutral system color at 70% lightness. This value can be overlaid at runtime by OverlayManager RROs. --> - <color name="system_neutral1_300">#ababab</color> + <color name="system_neutral1_300">#AAABAE</color> <!-- Shade of the neutral system color at 60% lightness. This value can be overlaid at runtime by OverlayManager RROs. --> - <color name="system_neutral1_400">#909090</color> + <color name="system_neutral1_400">#8F9193</color> <!-- Shade of the neutral system color at 49% lightness. This value can be overlaid at runtime by OverlayManager RROs. --> - <color name="system_neutral1_500">#757575</color> + <color name="system_neutral1_500">#747679</color> <!-- Shade of the neutral system color at 40% lightness. This value can be overlaid at runtime by OverlayManager RROs. --> - <color name="system_neutral1_600">#5e5e5e</color> + <color name="system_neutral1_600">#5C5F61</color> <!-- Shade of the neutral system color at 30% lightness. This value can be overlaid at runtime by OverlayManager RROs. --> - <color name="system_neutral1_700">#464646</color> + <color name="system_neutral1_700">#454749</color> <!-- Shade of the neutral system color at 20% lightness. This value can be overlaid at runtime by OverlayManager RROs. --> - <color name="system_neutral1_800">#303030</color> + <color name="system_neutral1_800">#2E3133</color> <!-- Shade of the neutral system color at 10% lightness. This value can be overlaid at runtime by OverlayManager RROs. --> - <color name="system_neutral1_900">#1b1b1b</color> + <color name="system_neutral1_900">#191C1E</color> <!-- Darkest shade of the neutral color used by the system. Black. This value can be overlaid at runtime by OverlayManager RROs. --> <color name="system_neutral1_1000">#000000</color> @@ -408,37 +408,37 @@ <color name="system_neutral2_0">#ffffff</color> <!-- Shade of the secondary neutral system color at 99% lightness. This value can be overlaid at runtime by OverlayManager RROs. --> - <color name="system_neutral2_10">#fbfbfb</color> + <color name="system_neutral2_10">#F9FCFF</color> <!-- Shade of the secondary neutral system color at 95% lightness. This value can be overlaid at runtime by OverlayManager RROs. --> - <color name="system_neutral2_50">#f0f0f0</color> + <color name="system_neutral2_50">#EBF1F8</color> <!-- Shade of the secondary neutral system color at 90% lightness. This value can be overlaid at runtime by OverlayManager RROs. --> - <color name="system_neutral2_100">#e2e2e2</color> + <color name="system_neutral2_100">#DCE3E9</color> <!-- Shade of the secondary neutral system color at 80% lightness. This value can be overlaid at runtime by OverlayManager RROs. --> - <color name="system_neutral2_200">#c6c6c6</color> + <color name="system_neutral2_200">#C0C7CD</color> <!-- Shade of the secondary neutral system color at 70% lightness. This value can be overlaid at runtime by OverlayManager RROs. --> - <color name="system_neutral2_300">#ababab</color> + <color name="system_neutral2_300">#A5ACB2</color> <!-- Shade of the secondary neutral system color at 60% lightness. This value can be overlaid at runtime by OverlayManager RROs. --> - <color name="system_neutral2_400">#909090</color> + <color name="system_neutral2_400">#8A9297</color> <!-- Shade of the secondary neutral system color at 49% lightness. This value can be overlaid at runtime by OverlayManager RROs. --> - <color name="system_neutral2_500">#757575</color> + <color name="system_neutral2_500">#70777C</color> <!-- Shade of the secondary neutral system color at 40% lightness. This value can be overlaid at runtime by OverlayManager RROs. --> - <color name="system_neutral2_600">#5e5e5e</color> + <color name="system_neutral2_600">#585F65</color> <!-- Shade of the secondary neutral system color at 30% lightness. This value can be overlaid at runtime by OverlayManager RROs. --> - <color name="system_neutral2_700">#464646</color> + <color name="system_neutral2_700">#40484D</color> <!-- Shade of the secondary neutral system color at 20% lightness. This value can be overlaid at runtime by OverlayManager RROs. --> - <color name="system_neutral2_800">#303030</color> + <color name="system_neutral2_800">#2A3136</color> <!-- Shade of the secondary neutral system color at 10% lightness. This value can be overlaid at runtime by OverlayManager RROs. --> - <color name="system_neutral2_900">#1b1b1b</color> + <color name="system_neutral2_900">#161C20</color> <!-- Darkest shade of the secondary neutral color used by the system. Black. This value can be overlaid at runtime by OverlayManager RROs. --> <color name="system_neutral2_1000">#000000</color> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 5d87f84332ae..89c5a8d867cb 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -971,6 +971,20 @@ --> <integer name="config_longPressOnPowerBehavior">5</integer> + <!-- The time in milliseconds after which a press on power button is considered "long". --> + <integer name="config_longPressOnPowerDurationMs">500</integer> + + <!-- The possible UI options to be surfaced for configuring long press power on duration + action. Value set in config_longPressOnPowerDurationMs should be one of the available + options to allow users to restore default. --> + <integer-array name="config_longPressOnPowerDurationSettings"> + <item>250</item> + <item>350</item> + <item>500</item> + <item>650</item> + <item>750</item> + </integer-array> + <!-- Whether the setting to change long press on power behaviour from default to assistant (5) is available in Settings. --> diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml index 7be9c7b42e5a..e8bb6067932e 100644 --- a/core/res/res/values/dimens.xml +++ b/core/res/res/values/dimens.xml @@ -237,6 +237,9 @@ value is calculated in ConversationLayout#updateActionListPadding() --> <dimen name="notification_actions_padding_start">36dp</dimen> + <!-- The max width of a priority action button when it is collapsed to just the icon. --> + <dimen name="notification_actions_collapsed_priority_width">60dp</dimen> + <!-- The start padding to optionally use (e.g. if there's extra space) for CallStyle notification actions. this = conversation_content_start (80dp) - button inset (4dp) - action padding (12dp) --> diff --git a/core/res/res/values/ids.xml b/core/res/res/values/ids.xml index c4838b83347c..84f82fd5d99d 100644 --- a/core/res/res/values/ids.xml +++ b/core/res/res/values/ids.xml @@ -254,6 +254,15 @@ <!-- Accessibility action identifier for {@link android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction#ACTION_IME_ENTER}. --> <item type="id" name="accessibilityActionImeEnter" /> + <!-- Accessibility action identifier for {@link android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction#ACTION_DRAG_START}. --> + <item type="id" name="accessibilityActionDragStart" /> + + <!-- Accessibility action identifier for {@link android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction#ACTION_DRAG_DROP}. --> + <item type="id" name="accessibilityActionDragDrop" /> + + <!-- Accessibility action identifier for {@link android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction#ACTION_DRAG_CANCEL}. --> + <item type="id" name="accessibilityActionDragCancel" /> + <!-- View tag for remote views to store the index of the next child when adding nested remote views dynamically. --> <item type="id" name="remote_views_next_child" /> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index eb4919b61dc2..b57055ca4338 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -3224,6 +3224,9 @@ </staging-public-group> <staging-public-group type="id" first-id="0x01fe0000"> + <public name="accessibilityActionDragStart" /> + <public name="accessibilityActionDragDrop" /> + <public name="accessibilityActionDragCancel" /> </staging-public-group> <staging-public-group type="style" first-id="0x01fd0000"> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 4ca1e3f4c06c..77a45f9caf7f 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -439,6 +439,8 @@ <java-symbol type="integer" name="config_extraFreeKbytesAbsolute" /> <java-symbol type="integer" name="config_immersive_mode_confirmation_panic" /> <java-symbol type="integer" name="config_longPressOnPowerBehavior" /> + <java-symbol type="integer" name="config_longPressOnPowerDurationMs" /> + <java-symbol type="array" name="config_longPressOnPowerDurationSettings" /> <java-symbol type="bool" name="config_longPressOnPowerForAssistantSettingAvailable" /> <java-symbol type="integer" name="config_veryLongPressOnPowerBehavior" /> <java-symbol type="integer" name="config_veryLongPressTimeout" /> @@ -3184,6 +3186,7 @@ <java-symbol type="id" name="notification_action_list_margin_target" /> <java-symbol type="dimen" name="notification_actions_padding_start"/> + <java-symbol type="dimen" name="notification_actions_collapsed_priority_width"/> <java-symbol type="dimen" name="notification_action_disabled_alpha" /> <java-symbol type="id" name="tag_margin_end_when_icon_visible" /> <java-symbol type="id" name="tag_margin_end_when_icon_gone" /> diff --git a/core/tests/BroadcastRadioTests/OWNERS b/core/tests/BroadcastRadioTests/OWNERS index ea4421eae96a..3e360e7e992c 100644 --- a/core/tests/BroadcastRadioTests/OWNERS +++ b/core/tests/BroadcastRadioTests/OWNERS @@ -1,2 +1,3 @@ +keunyoung@google.com +oscarazu@google.com twasilczyk@google.com -randolphs@google.com diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/StartProgramListUpdatesFanoutTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/StartProgramListUpdatesFanoutTest.java index 7c6271cbdf61..c194989b2752 100644 --- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/StartProgramListUpdatesFanoutTest.java +++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/StartProgramListUpdatesFanoutTest.java @@ -65,6 +65,7 @@ public class StartProgramListUpdatesFanoutTest { @Mock ITunerSession mHalTunerSessionMock; private android.hardware.radio.ITunerCallback[] mAidlTunerCallbackMocks; + private final Object mLock = new Object(); // RadioModule under test private RadioModule mRadioModule; @@ -96,7 +97,7 @@ public class StartProgramListUpdatesFanoutTest { mRadioModule = new RadioModule(mBroadcastRadioMock, new RadioManager.ModuleProperties(0, "", 0, "", "", "", "", 0, 0, false, false, null, false, new int[] {}, new int[] {}, - null, null)); + null, null), mLock); doAnswer((Answer) invocation -> { mHalTunerCallback = (ITunerCallback) invocation.getArguments()[0]; diff --git a/core/tests/coretests/src/android/app/NotificationTest.java b/core/tests/coretests/src/android/app/NotificationTest.java index cd07d464ee65..685671b083c4 100644 --- a/core/tests/coretests/src/android/app/NotificationTest.java +++ b/core/tests/coretests/src/android/app/NotificationTest.java @@ -399,6 +399,8 @@ public class NotificationTest { assertEquals(cDay.getSecondaryTextColor(), cNight.getSecondaryTextColor()); assertEquals(cDay.getPrimaryAccentColor(), cNight.getPrimaryAccentColor()); assertEquals(cDay.getSecondaryAccentColor(), cNight.getSecondaryAccentColor()); + assertEquals(cDay.getTertiaryAccentColor(), cNight.getTertiaryAccentColor()); + assertEquals(cDay.getOnAccentTextColor(), cNight.getOnAccentTextColor()); assertEquals(cDay.getProtectionColor(), cNight.getProtectionColor()); assertEquals(cDay.getContrastColor(), cNight.getContrastColor()); assertEquals(cDay.getRippleAlpha(), cNight.getRippleAlpha()); @@ -413,20 +415,26 @@ public class NotificationTest { assertThat(c.getSecondaryTextColor()).isNotEqualTo(Notification.COLOR_INVALID); assertThat(c.getPrimaryAccentColor()).isNotEqualTo(Notification.COLOR_INVALID); assertThat(c.getSecondaryAccentColor()).isNotEqualTo(Notification.COLOR_INVALID); + assertThat(c.getTertiaryAccentColor()).isNotEqualTo(Notification.COLOR_INVALID); + assertThat(c.getOnAccentTextColor()).isNotEqualTo(Notification.COLOR_INVALID); assertThat(c.getErrorColor()).isNotEqualTo(Notification.COLOR_INVALID); assertThat(c.getContrastColor()).isNotEqualTo(Notification.COLOR_INVALID); assertThat(c.getRippleAlpha()).isAtLeast(0x00); assertThat(c.getRippleAlpha()).isAtMost(0xff); - // Assert that various colors have sufficient contrast + // Assert that various colors have sufficient contrast with the background assertContrastIsAtLeast(c.getPrimaryTextColor(), c.getBackgroundColor(), 4.5); assertContrastIsAtLeast(c.getSecondaryTextColor(), c.getBackgroundColor(), 4.5); assertContrastIsAtLeast(c.getPrimaryAccentColor(), c.getBackgroundColor(), 4.5); assertContrastIsAtLeast(c.getErrorColor(), c.getBackgroundColor(), 4.5); assertContrastIsAtLeast(c.getContrastColor(), c.getBackgroundColor(), 4.5); - // This accent color is only used for emphasized buttons + // These colors are only used for emphasized buttons; they do not need contrast assertContrastIsAtLeast(c.getSecondaryAccentColor(), c.getBackgroundColor(), 1); + assertContrastIsAtLeast(c.getTertiaryAccentColor(), c.getBackgroundColor(), 1); + + // The text that is used within the accent color DOES need to have contrast + assertContrastIsAtLeast(c.getOnAccentTextColor(), c.getTertiaryAccentColor(), 4.5); } private void assertContrastIsAtLeast(int foreground, int background, double minContrast) { diff --git a/core/tests/coretests/src/com/android/internal/util/ContrastColorUtilTest.java b/core/tests/coretests/src/com/android/internal/util/ContrastColorUtilTest.java new file mode 100644 index 000000000000..9da720cbfa87 --- /dev/null +++ b/core/tests/coretests/src/com/android/internal/util/ContrastColorUtilTest.java @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2016 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.util; + +import static androidx.core.graphics.ColorUtils.calculateContrast; + +import static com.google.common.truth.Truth.assertThat; + +import android.graphics.Color; + +import androidx.test.filters.SmallTest; + +import junit.framework.TestCase; + +public class ContrastColorUtilTest extends TestCase { + + @SmallTest + public void testEnsureTextContrastAgainstDark() { + int darkBg = 0xFF35302A; + + int blueContrastColor = ContrastColorUtil.ensureTextContrast(Color.BLUE, darkBg, true); + assertContrastIsWithinRange(blueContrastColor, darkBg, 4.5, 4.75); + + int redContrastColor = ContrastColorUtil.ensureTextContrast(Color.RED, darkBg, true); + assertContrastIsWithinRange(redContrastColor, darkBg, 4.5, 4.75); + + final int darkGreen = 0xff008800; + int greenContrastColor = ContrastColorUtil.ensureTextContrast(darkGreen, darkBg, true); + assertContrastIsWithinRange(greenContrastColor, darkBg, 4.5, 4.75); + + int grayContrastColor = ContrastColorUtil.ensureTextContrast(Color.DKGRAY, darkBg, true); + assertContrastIsWithinRange(grayContrastColor, darkBg, 4.5, 4.75); + + int selfContrastColor = ContrastColorUtil.ensureTextContrast(darkBg, darkBg, true); + assertContrastIsWithinRange(selfContrastColor, darkBg, 4.5, 4.75); + } + + @SmallTest + public void testEnsureTextContrastAgainstLight() { + int lightBg = 0xFFFFF8F2; + + final int lightBlue = 0xff8888ff; + int blueContrastColor = ContrastColorUtil.ensureTextContrast(lightBlue, lightBg, false); + assertContrastIsWithinRange(blueContrastColor, lightBg, 4.5, 4.75); + + int redContrastColor = ContrastColorUtil.ensureTextContrast(Color.RED, lightBg, false); + assertContrastIsWithinRange(redContrastColor, lightBg, 4.5, 4.75); + + int greenContrastColor = ContrastColorUtil.ensureTextContrast(Color.GREEN, lightBg, false); + assertContrastIsWithinRange(greenContrastColor, lightBg, 4.5, 4.75); + + int grayContrastColor = ContrastColorUtil.ensureTextContrast(Color.LTGRAY, lightBg, false); + assertContrastIsWithinRange(grayContrastColor, lightBg, 4.5, 4.75); + + int selfContrastColor = ContrastColorUtil.ensureTextContrast(lightBg, lightBg, false); + assertContrastIsWithinRange(selfContrastColor, lightBg, 4.5, 4.75); + } + + private void assertContrastIsWithinRange(int foreground, int background, + double minContrast, double maxContrast) { + assertContrastIsAtLeast(foreground, background, minContrast); + assertContrastIsAtMost(foreground, background, maxContrast); + } + + private void assertContrastIsAtLeast(int foreground, int background, double minContrast) { + try { + assertThat(calculateContrast(foreground, background)).isAtLeast(minContrast); + } catch (AssertionError e) { + throw new AssertionError( + String.format("Insufficient contrast: foreground=#%08x background=#%08x", + foreground, background), e); + } + } + + private void assertContrastIsAtMost(int foreground, int background, double maxContrast) { + try { + assertThat(calculateContrast(foreground, background)).isAtMost(maxContrast); + } catch (AssertionError e) { + throw new AssertionError( + String.format("Excessive contrast: foreground=#%08x background=#%08x", + foreground, background), e); + } + } + +} diff --git a/core/tests/mockingcoretests/src/android/window/SizeConfigurationBucketsTest.java b/core/tests/mockingcoretests/src/android/window/SizeConfigurationBucketsTest.java new file mode 100644 index 000000000000..fa4aa803c75e --- /dev/null +++ b/core/tests/mockingcoretests/src/android/window/SizeConfigurationBucketsTest.java @@ -0,0 +1,379 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.window; + +import static android.content.pm.ActivityInfo.CONFIG_LOCALE; +import static android.content.pm.ActivityInfo.CONFIG_SCREEN_LAYOUT; +import static android.content.pm.ActivityInfo.CONFIG_SCREEN_SIZE; +import static android.content.pm.ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE; +import static android.content.res.Configuration.SCREENLAYOUT_COMPAT_NEEDED; +import static android.content.res.Configuration.SCREENLAYOUT_LAYOUTDIR_LTR; +import static android.content.res.Configuration.SCREENLAYOUT_LAYOUTDIR_RTL; +import static android.content.res.Configuration.SCREENLAYOUT_LAYOUTDIR_UNDEFINED; +import static android.content.res.Configuration.SCREENLAYOUT_LONG_NO; +import static android.content.res.Configuration.SCREENLAYOUT_LONG_YES; +import static android.content.res.Configuration.SCREENLAYOUT_ROUND_NO; +import static android.content.res.Configuration.SCREENLAYOUT_ROUND_UNDEFINED; +import static android.content.res.Configuration.SCREENLAYOUT_ROUND_YES; +import static android.content.res.Configuration.SCREENLAYOUT_SIZE_LARGE; +import static android.content.res.Configuration.SCREENLAYOUT_SIZE_NORMAL; +import static android.content.res.Configuration.SCREENLAYOUT_SIZE_SMALL; +import static android.content.res.Configuration.SCREENLAYOUT_SIZE_XLARGE; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import android.content.res.Configuration; +import android.platform.test.annotations.Presubmit; + +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.filters.SmallTest; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.Arrays; + +/** + * Tests for {@link SizeConfigurationBuckets} + * + * Build/Install/Run: + * atest FrameworksMockingCoreTests:SizeConfigurationBucketsTest + */ + +@SmallTest +@Presubmit +@RunWith(AndroidJUnit4.class) +public class SizeConfigurationBucketsTest { + + /** + * Tests that a change in any of the non-size-related screen layout fields results in + * {@link SizeConfigurationBuckets#areNonSizeLayoutFieldsUnchanged} returning false. + */ + @Test + public void testNonSizeRelatedScreenLayoutFields() { + // Test layout direction + assertEquals(true, SizeConfigurationBuckets + .areNonSizeLayoutFieldsUnchanged(0, SCREENLAYOUT_LAYOUTDIR_UNDEFINED)); + assertEquals(false, SizeConfigurationBuckets + .areNonSizeLayoutFieldsUnchanged(0, SCREENLAYOUT_LAYOUTDIR_LTR)); + assertEquals(false, SizeConfigurationBuckets + .areNonSizeLayoutFieldsUnchanged(0, SCREENLAYOUT_LAYOUTDIR_RTL)); + + // Test layout roundness + assertEquals(true, SizeConfigurationBuckets + .areNonSizeLayoutFieldsUnchanged(0, SCREENLAYOUT_ROUND_UNDEFINED)); + assertEquals(false, SizeConfigurationBuckets + .areNonSizeLayoutFieldsUnchanged(0, SCREENLAYOUT_ROUND_NO)); + assertEquals(false, SizeConfigurationBuckets + .areNonSizeLayoutFieldsUnchanged(0, SCREENLAYOUT_ROUND_YES)); + + // Test layout compat needed + assertEquals(false, SizeConfigurationBuckets + .areNonSizeLayoutFieldsUnchanged(0, SCREENLAYOUT_COMPAT_NEEDED)); + } + + /** + * Tests that null size configuration buckets unflips the correct configuration flags. + */ + @Test + public void testNullSizeConfigurationBuckets() { + // Check that all 3 size configurations are filtered out of the diff if the buckets are null + // and non-size attributes of screen layout are unchanged. Add a non-size related config + // change (i.e. CONFIG_LOCALE) to test that the diff is not set to zero. + final int diff = CONFIG_SCREEN_SIZE | CONFIG_SMALLEST_SCREEN_SIZE | CONFIG_SCREEN_LAYOUT + | CONFIG_LOCALE; + final int filteredDiffNonSizeLayoutUnchanged = SizeConfigurationBuckets.filterDiff(diff, + Configuration.EMPTY, Configuration.EMPTY, null); + assertEquals(CONFIG_LOCALE, filteredDiffNonSizeLayoutUnchanged); + + // Check that only screen size and smallest screen size are filtered out of the diff if the + // buckets are null and non-size attributes of screen layout are changed. + final Configuration newConfig = new Configuration(); + newConfig.screenLayout |= SCREENLAYOUT_ROUND_YES; + final int filteredDiffNonSizeLayoutChanged = SizeConfigurationBuckets.filterDiff(diff, + Configuration.EMPTY, newConfig, null); + assertEquals(CONFIG_SCREEN_LAYOUT | CONFIG_LOCALE, filteredDiffNonSizeLayoutChanged); + } + + /** + * Tests that {@link SizeConfigurationBuckets.crossesSizeThreshold()} correctly checks whether + * the bucket thresholds have or have not been crossed. This test includes boundary checks + * to ensure that arithmetic is inclusive and exclusive in the right places. + */ + @Test + public void testCrossesSizeThreshold() { + final int[] thresholds = new int[] { 360, 600 }; + final int nThresholds = thresholds.length; + for (int i = -1; i < nThresholds; i++) { + final int minValueInBucket = i < 0 ? 0 : thresholds[i]; + final int maxValueInBucket = i < nThresholds - 1 + ? thresholds[i + 1] - 1 : Integer.MAX_VALUE; + final int bucketRange = maxValueInBucket - minValueInBucket; + // Set old value to 1/4 in between the two thresholds. + final int oldValue = (int) (minValueInBucket + bucketRange * 0.25); + // Test 3 values of new value spread across bucket range: minValueInBucket, bucket + // midpoint, and max value in bucket. In all 3 cases, the bucket has not changed so + // {@link SizeConfigurationBuckets#crossedSizeThreshold()} should return false. + checkCrossesSizeThreshold(thresholds, oldValue, minValueInBucket, false); + checkCrossesSizeThreshold(thresholds, oldValue, + (int) (minValueInBucket + bucketRange * 0.5), false); + checkCrossesSizeThreshold(thresholds, oldValue, maxValueInBucket, false); + // Test 4 values of size spread outside of bucket range: more than 1 less than min + // value, 1 less than min value, 1 more than max value, and more than 1 more than max + // value. In all 4 cases, the bucket has changed so + // {@link SizeConfigurationBuckets#crossedSizeThreshold()} should return true. + // Only test less than min value if min value > 0. + if (minValueInBucket > 0) { + checkCrossesSizeThreshold(thresholds, oldValue, minValueInBucket - 20, true); + checkCrossesSizeThreshold(thresholds, oldValue, minValueInBucket - 1, true); + } + // Only test greater than max value if not in highest bucket. + if (i < nThresholds - 1) { + checkCrossesSizeThreshold(thresholds, oldValue, maxValueInBucket + 1, true); + checkCrossesSizeThreshold(thresholds, oldValue, maxValueInBucket + 20, true); + } + } + } + + /** + * Tests that if screen layout size changed but did not cross a threshold, the filtered diff + * does not include screen layout. + */ + @Test + public void testScreenLayoutFilteredIfSizeDidNotCrossThreshold() { + // Set only small and large sizes + final Configuration[] sizeConfigs = new Configuration[2]; + sizeConfigs[0] = new Configuration(); + sizeConfigs[0].screenLayout |= SCREENLAYOUT_SIZE_SMALL; + sizeConfigs[1] = new Configuration(); + sizeConfigs[1].screenLayout |= SCREENLAYOUT_SIZE_LARGE; + final SizeConfigurationBuckets sizeBuckets = new SizeConfigurationBuckets(sizeConfigs); + + // Change screen layout size from small to normal and check that screen layout flag is + // not part of the diff because a threshold was not crossed. + final int diff = CONFIG_SCREEN_LAYOUT; + final Configuration oldConfig = new Configuration(); + oldConfig.screenLayout |= SCREENLAYOUT_SIZE_SMALL; + final Configuration newConfig = new Configuration(); + newConfig.screenLayout |= SCREENLAYOUT_SIZE_NORMAL; + final int filteredDiff = SizeConfigurationBuckets.filterDiff(diff, oldConfig, newConfig, + sizeBuckets); + assertEquals(0, filteredDiff); + + // If a non-size attribute of screen layout changed, then screen layout should not be + // filtered from the diff. + newConfig.screenLayout |= SCREENLAYOUT_ROUND_YES; + final int filteredDiffNonSizeLayoutChanged = SizeConfigurationBuckets.filterDiff(diff, + oldConfig, newConfig, sizeBuckets); + assertEquals(CONFIG_SCREEN_LAYOUT, filteredDiffNonSizeLayoutChanged); + } + + /** + * Tests that if screen layout size changed and did cross a threshold, the filtered diff + * includes screen layout. + */ + @Test + public void testScreenLayoutNotFilteredIfSizeCrossedThreshold() { + // Set only small and normal sizes + final Configuration[] sizeConfigs = new Configuration[2]; + sizeConfigs[0] = new Configuration(); + sizeConfigs[0].screenLayout |= SCREENLAYOUT_SIZE_SMALL; + sizeConfigs[1] = new Configuration(); + sizeConfigs[1].screenLayout |= SCREENLAYOUT_SIZE_NORMAL; + final SizeConfigurationBuckets sizeBuckets = new SizeConfigurationBuckets(sizeConfigs); + + // Change screen layout size from small to normal and check that screen layout flag is + // still part of the diff because a threshold was crossed. + final int diff = CONFIG_SCREEN_LAYOUT; + final Configuration oldConfig = new Configuration(); + oldConfig.screenLayout |= SCREENLAYOUT_SIZE_SMALL; + final Configuration newConfig = new Configuration(); + newConfig.screenLayout |= SCREENLAYOUT_SIZE_NORMAL; + final int filteredDiff = SizeConfigurationBuckets.filterDiff(diff, oldConfig, newConfig, + sizeBuckets); + assertEquals(CONFIG_SCREEN_LAYOUT, filteredDiff); + } + + /** + * Tests that anytime screen layout size is decreased, the filtered diff still includes screen + * layout. + */ + @Test + public void testScreenLayoutNotFilteredIfSizeDecreased() { + // The size thresholds can be anything, but can't be null + final int[] horizontalThresholds = new int[] { 360, 600 }; + final SizeConfigurationBuckets sizeBuckets = new SizeConfigurationBuckets( + horizontalThresholds, null /* vertical */, null /* smallest */, + null /* screenLayoutSize */, false /* screenLayoutLongSet */); + final int[] sizeValuesInOrder = new int[] { + SCREENLAYOUT_SIZE_SMALL, SCREENLAYOUT_SIZE_NORMAL, SCREENLAYOUT_SIZE_LARGE, + SCREENLAYOUT_SIZE_XLARGE + }; + final int nSizes = sizeValuesInOrder.length; + for (int larger = nSizes - 1; larger > 0; larger--) { + for (int smaller = larger - 1; smaller >= 0; smaller--) { + final Configuration oldConfig = new Configuration(); + oldConfig.screenLayout |= sizeValuesInOrder[larger]; + final Configuration newConfig = new Configuration(); + newConfig.screenLayout |= sizeValuesInOrder[smaller]; + assertTrue(String.format("oldSize=%d, newSize=%d", oldConfig.screenLayout, + newConfig.screenLayout), + sizeBuckets.crossesScreenLayoutSizeThreshold(oldConfig, newConfig)); + } + } + } + + /** + * Tests that if screen layout long changed but did not cross a threshold, the filtered diff + * does not include screen layout. + */ + @Test + public void testScreenLayoutFilteredIfLongDidNotCrossThreshold() { + // Do not set any long threshold + final Configuration[] sizeConfigs = new Configuration[1]; + sizeConfigs[0] = Configuration.EMPTY; + final SizeConfigurationBuckets sizeBuckets = new SizeConfigurationBuckets(sizeConfigs); + + // Change screen layout long from not long to long and check that screen layout flag is + // not part of the diff because a threshold was not crossed. + final int diff = CONFIG_SCREEN_LAYOUT; + final Configuration oldConfig = new Configuration(); + oldConfig.screenLayout |= SCREENLAYOUT_LONG_NO; + final Configuration newConfig = new Configuration(); + newConfig.screenLayout |= SCREENLAYOUT_LONG_YES; + final int filteredDiff = SizeConfigurationBuckets.filterDiff(diff, oldConfig, newConfig, + sizeBuckets); + assertEquals(0, filteredDiff); + + // If a non-size attribute of screen layout changed, then screen layout should not be + // filtered from the diff. + newConfig.screenLayout |= SCREENLAYOUT_ROUND_YES; + final int filteredDiffNonSizeLayoutChanged = SizeConfigurationBuckets.filterDiff(diff, + oldConfig, newConfig, sizeBuckets); + assertEquals(CONFIG_SCREEN_LAYOUT, filteredDiffNonSizeLayoutChanged); + } + + /** + * Tests that if screen layout long changed and did cross a threshold, the filtered diff + * includes screen layout. + */ + @Test + public void testScreenLayoutNotFilteredIfLongCrossedThreshold() { + // Set only small and normal sizes + final Configuration[] sizeConfigs = new Configuration[1]; + sizeConfigs[0] = new Configuration(); + sizeConfigs[0].screenLayout |= SCREENLAYOUT_LONG_NO; + final SizeConfigurationBuckets sizeBuckets = new SizeConfigurationBuckets(sizeConfigs); + + // Change screen layout long from not long to long and check that screen layout flag is + // still part of the diff because a threshold was crossed. + final int diff = CONFIG_SCREEN_LAYOUT; + final Configuration oldConfig = new Configuration(); + oldConfig.screenLayout |= SCREENLAYOUT_LONG_NO; + final Configuration newConfig = new Configuration(); + newConfig.screenLayout |= SCREENLAYOUT_LONG_YES; + final int filteredDiff = SizeConfigurationBuckets.filterDiff(diff, oldConfig, newConfig, + sizeBuckets); + assertEquals(CONFIG_SCREEN_LAYOUT, filteredDiff); + } + + /** + * Tests that horizontal buckets are correctly checked in + * {@link SizeConfigurationBuckets#filterDiff()}. + */ + @Test + public void testHorizontalSizeThresholds() { + final int[] horizontalThresholds = new int[] { 360, 600 }; + final SizeConfigurationBuckets sizeBuckets = new SizeConfigurationBuckets( + horizontalThresholds, null /* vertical */, null /* smallest */, + null /* screenLayoutSize */, false /* screenLayoutLongSet */); + + final Configuration oldConfig = new Configuration(); + final Configuration newConfig = new Configuration(); + + oldConfig.screenWidthDp = 480; + // Test that value within bucket filters out screen size config + newConfig.screenWidthDp = 520; + assertEquals(0, SizeConfigurationBuckets.filterDiff(CONFIG_SCREEN_SIZE, oldConfig, + newConfig, sizeBuckets)); + // Test that value outside bucket does not filter out screen size config + newConfig.screenWidthDp = 640; + assertEquals(CONFIG_SCREEN_SIZE, SizeConfigurationBuckets.filterDiff(CONFIG_SCREEN_SIZE, + oldConfig, newConfig, sizeBuckets)); + } + + /** + * Tests that vertical buckets are correctly checked in + * {@link SizeConfigurationBuckets#filterDiff()}. + */ + @Test + public void testVerticalSizeThresholds() { + final int[] verticalThresholds = new int[] { 360, 600 }; + final SizeConfigurationBuckets sizeBuckets = new SizeConfigurationBuckets( + null, verticalThresholds /* vertical */, null /* smallest */, + null /* screenLayoutSize */, false /* screenLayoutLongSet */); + + final Configuration oldConfig = new Configuration(); + final Configuration newConfig = new Configuration(); + + oldConfig.screenHeightDp = 480; + // Test that value within bucket filters out screen size config + newConfig.screenHeightDp = 520; + assertEquals(0, SizeConfigurationBuckets.filterDiff(CONFIG_SCREEN_SIZE, oldConfig, + newConfig, sizeBuckets)); + // Test that value outside bucket does not filter out screen size config + newConfig.screenHeightDp = 640; + assertEquals(CONFIG_SCREEN_SIZE, SizeConfigurationBuckets.filterDiff(CONFIG_SCREEN_SIZE, + oldConfig, newConfig, sizeBuckets)); + } + + /** + * Tests that smallest width buckets are correctly checked in + * {@link SizeConfigurationBuckets#filterDiff()}. + */ + @Test + public void testSmallestWidthSizeThresholds() { + final int[] smallestWidthThresholds = new int[] { 360, 600 }; + final SizeConfigurationBuckets sizeBuckets = new SizeConfigurationBuckets( + null, null /* vertical */, smallestWidthThresholds /* smallest */, + null /* screenLayoutSize */, false /* screenLayoutLongSet */); + + final Configuration oldConfig = new Configuration(); + final Configuration newConfig = new Configuration(); + + oldConfig.smallestScreenWidthDp = 480; + // Test that value within bucket filters out smallest screen size config + newConfig.smallestScreenWidthDp = 520; + assertEquals(0, SizeConfigurationBuckets.filterDiff(CONFIG_SMALLEST_SCREEN_SIZE, oldConfig, + newConfig, sizeBuckets)); + // Test that value outside bucket does not filter out smallest screen size config + newConfig.smallestScreenWidthDp = 640; + assertEquals(CONFIG_SMALLEST_SCREEN_SIZE, SizeConfigurationBuckets.filterDiff( + CONFIG_SMALLEST_SCREEN_SIZE, oldConfig, newConfig, sizeBuckets)); + } + + private void checkCrossesSizeThreshold(int[] thresholds, int oldValue, int newValue, + boolean expected) { + final String errorString = String.format( + "thresholds=%s, oldValue=%d, newValue=%d, expected=%b", Arrays.toString(thresholds), + oldValue, newValue, expected); + final boolean actual = SizeConfigurationBuckets.crossesSizeThreshold(thresholds, oldValue, + newValue); + assertEquals(errorString, expected, actual); + } +} diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json index 8caff23539c3..b49b289d44c0 100644 --- a/data/etc/services.core.protolog.json +++ b/data/etc/services.core.protolog.json @@ -2599,6 +2599,12 @@ "group": "WM_DEBUG_CONFIGURATION", "at": "com\/android\/server\/wm\/ActivityRecord.java" }, + "743418423": { + "message": "Sending TaskFragment error exception=%s", + "level": "VERBOSE", + "group": "WM_DEBUG_WINDOW_ORGANIZER", + "at": "com\/android\/server\/wm\/TaskFragmentOrganizerController.java" + }, "744171317": { "message": " SKIP: %s", "level": "VERBOSE", diff --git a/graphics/java/android/graphics/ImageFormat.java b/graphics/java/android/graphics/ImageFormat.java index 5b79d76dd6b9..884d27f8d887 100644 --- a/graphics/java/android/graphics/ImageFormat.java +++ b/graphics/java/android/graphics/ImageFormat.java @@ -178,22 +178,8 @@ public class ImageFormat { * <p>Android YUV P010 format.</p> * * P010 is a 4:2:0 YCbCr semiplanar format comprised of a WxH Y plane - * followed immediately by a Wx(H/2) CbCr plane. Each sample is - * represented by a 16-bit little-endian value, with the lower 6 bits set - * to zero. - * - * <p>This format assumes - * <ul> - * <li>an even height</li> - * <li>a vertical stride equal to the height</li> - * </ul> - * </p> - * - * <pre> stride_in_bytes = stride * 2 </pre> - * <pre> y_size = stride_in_bytes * height </pre> - * <pre> cbcr_size = stride_in_bytes * (height / 2) </pre> - * <pre> cb_offset = y_size </pre> - * <pre> cr_offset = cb_offset + 2 </pre> + * followed by a Wx(H/2) CbCr plane. Each sample is represented by a 16-bit + * little-endian value, with the lower 6 bits set to zero. * * <p>For example, the {@link android.media.Image} object can provide data * in this format from a {@link android.hardware.camera2.CameraDevice} diff --git a/keystore/java/android/security/GenerateRkpKey.java b/keystore/java/android/security/GenerateRkpKey.java index 053bec74405e..2e54e63a5b7a 100644 --- a/keystore/java/android/security/GenerateRkpKey.java +++ b/keystore/java/android/security/GenerateRkpKey.java @@ -25,6 +25,8 @@ import android.os.RemoteException; import android.util.Log; import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; /** @@ -88,7 +90,8 @@ public class GenerateRkpKey { } intent.setComponent(comp); mCountDownLatch = new CountDownLatch(1); - if (!mContext.bindService(intent, mConnection, Context.BIND_AUTO_CREATE)) { + Executor executor = Executors.newCachedThreadPool(); + if (!mContext.bindService(intent, Context.BIND_AUTO_CREATE, executor, mConnection)) { throw new RemoteException("Failed to bind to GenerateRkpKeyService"); } try { diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/ExtensionProvider.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/ExtensionProvider.java index b7a60392c512..ce4e10364ba2 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/ExtensionProvider.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/ExtensionProvider.java @@ -18,6 +18,10 @@ package androidx.window.extensions; import android.content.Context; +import androidx.annotation.NonNull; +import androidx.window.extensions.embedding.ActivityEmbeddingComponent; +import androidx.window.extensions.organizer.EmbeddingExtensionImpl; + /** * Provider class that will instantiate the library implementation. It must be included in the * vendor library, and the vendor implementation must match the signature of this class. @@ -31,6 +35,12 @@ public class ExtensionProvider { return new SampleExtensionImpl(context); } + /** Provides a reference implementation of {@link ActivityEmbeddingComponent}. */ + public static ActivityEmbeddingComponent getActivityEmbeddingExtensionImpl( + @NonNull Context context) { + return new EmbeddingExtensionImpl(); + } + /** * The support library will use this method to check API version compatibility. * @return API version string in MAJOR.MINOR.PATCH-description format. diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/SampleExtensionImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/SampleExtensionImpl.java index cafc2337a022..a0d5b004ff1c 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/SampleExtensionImpl.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/SampleExtensionImpl.java @@ -23,19 +23,15 @@ import static androidx.window.util.ExtensionHelper.transformToWindowSpaceRect; import android.app.Activity; import android.content.Context; -import android.content.Intent; import android.graphics.Rect; -import android.os.Bundle; import android.util.Log; import androidx.annotation.NonNull; -import androidx.annotation.Nullable; import androidx.window.common.DeviceStateManagerPostureProducer; import androidx.window.common.DisplayFeature; import androidx.window.common.ResourceConfigDisplayFeatureProducer; import androidx.window.common.SettingsDevicePostureProducer; import androidx.window.common.SettingsDisplayFeatureProducer; -import androidx.window.extensions.organizer.SplitController; import androidx.window.util.DataProducer; import androidx.window.util.PriorityDataProducer; @@ -60,8 +56,6 @@ class SampleExtensionImpl extends StubExtension { private final SettingsDisplayFeatureProducer mSettingsDisplayFeatureProducer; private final DataProducer<List<DisplayFeature>> mDisplayFeatureProducer; - private final SplitController mSplitController; - SampleExtensionImpl(Context context) { mSettingsDevicePostureProducer = new SettingsDevicePostureProducer(context); mDevicePostureProducer = new PriorityDataProducer<>(List.of( @@ -77,8 +71,6 @@ class SampleExtensionImpl extends StubExtension { mDevicePostureProducer.addDataChangedCallback(this::onDisplayFeaturesChanged); mDisplayFeatureProducer.addDataChangedCallback(this::onDisplayFeaturesChanged); - - mSplitController = new SplitController(); } private int getFeatureState(DisplayFeature feature) { @@ -142,28 +134,4 @@ class SampleExtensionImpl extends StubExtension { onDisplayFeaturesChanged(); } - - @Override - public void setSplitRules(@NonNull List<ExtensionSplitRule> splitRules) { - mSplitController.setSplitRules(splitRules); - } - - @Override - @NonNull - public List<ExtensionSplitRule> getSplitRules() { - return new ArrayList<>(mSplitController.getSplitRules()); - } - - @Override - public void setSplitOrganizerCallback(@Nullable SplitOrganizerCallback callback) { - mSplitController.setSplitOrganizerCallback(callback); - } - - @Override - public void startActivityToSide(@NonNull Activity launchingActivity, @NonNull Intent intent, - @Nullable Bundle options, @NonNull ExtensionSplitPairRule splitPairRule, - int startRequestId) { - mSplitController.startActivityToSide(launchingActivity, intent, options, splitPairRule, - startRequestId); - } } diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/EmbeddingExtensionImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/EmbeddingExtensionImpl.java new file mode 100644 index 000000000000..9a8961f1d460 --- /dev/null +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/EmbeddingExtensionImpl.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package androidx.window.extensions.organizer; + +import androidx.annotation.NonNull; +import androidx.window.extensions.embedding.ActivityEmbeddingComponent; +import androidx.window.extensions.embedding.EmbeddingRule; +import androidx.window.extensions.embedding.SplitInfo; + +import java.util.List; +import java.util.Set; +import java.util.function.Consumer; + +/** + * Reference implementation of the activity embedding interface defined in WM Jetpack. + */ +public class EmbeddingExtensionImpl implements ActivityEmbeddingComponent { + + private final SplitController mSplitController; + + public EmbeddingExtensionImpl() { + mSplitController = new SplitController(); + } + + @Override + public void setEmbeddingRules(@NonNull Set<EmbeddingRule> rules) { + mSplitController.setEmbeddingRules(rules); + } + + @Override + public void setEmbeddingCallback(@NonNull Consumer<List<SplitInfo>> consumer) { + mSplitController.setEmbeddingCallback(consumer); + } +} diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/JetpackTaskFragmentOrganizer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/JetpackTaskFragmentOrganizer.java index 531df30a4e2c..4206d0375e30 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/JetpackTaskFragmentOrganizer.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/JetpackTaskFragmentOrganizer.java @@ -36,6 +36,7 @@ import android.window.WindowContainerTransaction; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.window.extensions.embedding.SplitRule; import java.util.Map; import java.util.concurrent.Executor; @@ -96,11 +97,11 @@ class JetpackTaskFragmentOrganizer extends TaskFragmentOrganizer { * @param activityIntent Intent to start the secondary Activity with. * @param activityOptions ActivityOptions to start the secondary Activity with. */ - void startActivityToSide(IBinder launchingFragmentToken, Rect launchingFragmentBounds, - Activity launchingActivity, IBinder secondaryFragmentToken, - Rect secondaryFragmentBounds, Intent activityIntent, - @Nullable Bundle activityOptions) { - final WindowContainerTransaction wct = new WindowContainerTransaction(); + void startActivityToSide(@NonNull WindowContainerTransaction wct, + @NonNull IBinder launchingFragmentToken, @NonNull Rect launchingFragmentBounds, + @NonNull Activity launchingActivity, @NonNull IBinder secondaryFragmentToken, + @NonNull Rect secondaryFragmentBounds, @NonNull Intent activityIntent, + @Nullable Bundle activityOptions, @NonNull SplitRule rule) { final IBinder ownerToken = launchingActivity.getActivityToken(); // Create or resize the launching TaskFragment. @@ -117,9 +118,7 @@ class JetpackTaskFragmentOrganizer extends TaskFragmentOrganizer { activityOptions); // Set adjacent to each other so that the containers below will be invisible. - wct.setAdjacentTaskFragments(launchingFragmentToken, secondaryFragmentToken); - - applyTransaction(wct); + setAdjacentTaskFragments(wct, launchingFragmentToken, secondaryFragmentToken, rule); } /** @@ -129,7 +128,7 @@ class JetpackTaskFragmentOrganizer extends TaskFragmentOrganizer { */ void expandTaskFragment(WindowContainerTransaction wct, IBinder fragmentToken) { resizeTaskFragment(wct, fragmentToken, new Rect()); - wct.setAdjacentTaskFragments(fragmentToken, null); + setAdjacentTaskFragments(wct, fragmentToken, null /* secondary */, null /* splitRule */); } /** @@ -189,6 +188,21 @@ class JetpackTaskFragmentOrganizer extends TaskFragmentOrganizer { wct.startActivityInTaskFragment(fragmentToken, ownerToken, activityIntent, activityOptions); } + void setAdjacentTaskFragments(@NonNull WindowContainerTransaction wct, + @NonNull IBinder primary, @Nullable IBinder secondary, @Nullable SplitRule splitRule) { + WindowContainerTransaction.TaskFragmentAdjacentOptions adjacentOptions = null; + final boolean finishSecondaryWithPrimary = + splitRule != null && SplitContainer.shouldFinishSecondaryWithPrimary(splitRule); + final boolean finishPrimaryWithSecondary = + splitRule != null && SplitContainer.shouldFinishPrimaryWithSecondary(splitRule); + if (finishSecondaryWithPrimary || finishPrimaryWithSecondary) { + adjacentOptions = new WindowContainerTransaction.TaskFragmentAdjacentOptions(); + adjacentOptions.setDelayPrimaryLastActivityRemoval(finishSecondaryWithPrimary); + adjacentOptions.setDelaySecondaryLastActivityRemoval(finishPrimaryWithSecondary); + } + wct.setAdjacentTaskFragments(primary, secondary, adjacentOptions); + } + TaskFragmentCreationParams createFragmentOptions(IBinder fragmentToken, IBinder ownerToken, Rect bounds, @WindowingMode int windowingMode) { if (mFragmentInfos.containsKey(fragmentToken)) { diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/SplitContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/SplitContainer.java index ade857358987..4fd2126dfa27 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/SplitContainer.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/SplitContainer.java @@ -19,7 +19,9 @@ package androidx.window.extensions.organizer; import android.annotation.NonNull; import android.app.Activity; -import androidx.window.extensions.ExtensionSplitPairRule; +import androidx.window.extensions.embedding.SplitPairRule; +import androidx.window.extensions.embedding.SplitPlaceholderRule; +import androidx.window.extensions.embedding.SplitRule; /** * Client-side descriptor of a split that holds two containers. @@ -27,20 +29,20 @@ import androidx.window.extensions.ExtensionSplitPairRule; class SplitContainer { private final TaskFragmentContainer mPrimaryContainer; private final TaskFragmentContainer mSecondaryContainer; - private final ExtensionSplitPairRule mSplitPairRule; + private final SplitRule mSplitRule; SplitContainer(@NonNull TaskFragmentContainer primaryContainer, @NonNull Activity primaryActivity, @NonNull TaskFragmentContainer secondaryContainer, - @NonNull ExtensionSplitPairRule splitPairRule) { + @NonNull SplitRule splitRule) { mPrimaryContainer = primaryContainer; mSecondaryContainer = secondaryContainer; - mSplitPairRule = splitPairRule; + mSplitRule = splitRule; - if (mSplitPairRule.finishPrimaryWithSecondary || mSplitPairRule.useAsPlaceholder) { + if (shouldFinishPrimaryWithSecondary(splitRule)) { mSecondaryContainer.addActivityToFinishOnExit(primaryActivity); } - if (mSplitPairRule.finishSecondaryWithPrimary || mSplitPairRule.useAsPlaceholder) { + if (shouldFinishSecondaryWithPrimary(splitRule)) { mPrimaryContainer.addContainerToFinishOnExit(mSecondaryContainer); } } @@ -56,7 +58,25 @@ class SplitContainer { } @NonNull - ExtensionSplitPairRule getSplitPairRule() { - return mSplitPairRule; + SplitRule getSplitRule() { + return mSplitRule; + } + + boolean isPlaceholderContainer() { + return (mSplitRule instanceof SplitPlaceholderRule); + } + + static boolean shouldFinishPrimaryWithSecondary(@NonNull SplitRule splitRule) { + final boolean isPlaceholderContainer = splitRule instanceof SplitPlaceholderRule; + final boolean shouldFinishPrimaryWithSecondary = (splitRule instanceof SplitPairRule) + && ((SplitPairRule) splitRule).shouldFinishPrimaryWithSecondary(); + return shouldFinishPrimaryWithSecondary || isPlaceholderContainer; + } + + static boolean shouldFinishSecondaryWithPrimary(@NonNull SplitRule splitRule) { + final boolean isPlaceholderContainer = splitRule instanceof SplitPlaceholderRule; + final boolean shouldFinishSecondaryWithPrimary = (splitRule instanceof SplitPairRule) + && ((SplitPairRule) splitRule).shouldFinishSecondaryWithPrimary(); + return shouldFinishSecondaryWithPrimary || isPlaceholderContainer; } } diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/SplitController.java index 802976fbfc6d..05c6792a3fc7 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/SplitController.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/SplitController.java @@ -24,7 +24,6 @@ import android.app.ActivityOptions; import android.app.ActivityThread; import android.app.Application.ActivityLifecycleCallbacks; import android.app.Instrumentation; -import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.res.Configuration; @@ -32,20 +31,24 @@ import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Looper; +import android.util.Pair; import android.window.TaskFragmentAppearedInfo; import android.window.TaskFragmentInfo; import android.window.WindowContainerTransaction; -import androidx.window.extensions.ExtensionInterface.SplitOrganizerCallback; -import androidx.window.extensions.ExtensionSplitActivityRule; -import androidx.window.extensions.ExtensionSplitInfo; -import androidx.window.extensions.ExtensionSplitPairRule; -import androidx.window.extensions.ExtensionSplitRule; -import androidx.window.extensions.ExtensionTaskFragment; +import androidx.window.extensions.embedding.ActivityRule; +import androidx.window.extensions.embedding.EmbeddingRule; +import androidx.window.extensions.embedding.SplitInfo; +import androidx.window.extensions.embedding.SplitPairRule; +import androidx.window.extensions.embedding.SplitPlaceholderRule; +import androidx.window.extensions.embedding.SplitRule; +import androidx.window.extensions.embedding.TaskFragment; import java.util.ArrayList; import java.util.List; +import java.util.Set; import java.util.concurrent.Executor; +import java.util.function.Consumer; /** * Main controller class that manages split states and presentation. @@ -55,12 +58,12 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen private final SplitPresenter mPresenter; // Currently applied split configuration. - private final List<ExtensionSplitRule> mSplitRules = new ArrayList<>(); + private final List<EmbeddingRule> mSplitRules = new ArrayList<>(); private final List<TaskFragmentContainer> mContainers = new ArrayList<>(); private final List<SplitContainer> mSplitContainers = new ArrayList<>(); // Callback to Jetpack to notify about changes to split states. - private SplitOrganizerCallback mSplitOrganizerCallback; + private @NonNull Consumer<List<SplitInfo>> mEmbeddingCallback; public SplitController() { mPresenter = new SplitPresenter(new MainThreadExecutor(), this); @@ -73,13 +76,14 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen instrumentation.addMonitor(new ActivityStartMonitor()); } - public void setSplitRules(@NonNull List<ExtensionSplitRule> splitRules) { + /** Updates the embedding rules applied to future activity launches. */ + public void setEmbeddingRules(@NonNull Set<EmbeddingRule> rules) { mSplitRules.clear(); - mSplitRules.addAll(splitRules); + mSplitRules.addAll(rules); } @NonNull - public List<ExtensionSplitRule> getSplitRules() { + public List<EmbeddingRule> getSplitRules() { return mSplitRules; } @@ -87,22 +91,20 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen * Starts an activity to side of the launchingActivity with the provided split config. */ public void startActivityToSide(@NonNull Activity launchingActivity, @NonNull Intent intent, - @Nullable Bundle options, @NonNull ExtensionSplitPairRule splitPairRule, - int startRequestId) { + @Nullable Bundle options, @NonNull SplitRule sideRule, + @NonNull Consumer<Exception> failureCallback) { try { - mPresenter.startActivityToSide(launchingActivity, intent, options, splitPairRule); + mPresenter.startActivityToSide(launchingActivity, intent, options, sideRule); } catch (Exception e) { - if (mSplitOrganizerCallback != null && startRequestId != -1) { - mSplitOrganizerCallback.onActivityFailedToStartInContainer(startRequestId, e); - } + failureCallback.accept(e); } } /** * Registers the split organizer callback to notify about changes to active splits. */ - public void setSplitOrganizerCallback(@NonNull SplitOrganizerCallback callback) { - mSplitOrganizerCallback = callback; + public void setEmbeddingCallback(@NonNull Consumer<List<SplitInfo>> callback) { + mEmbeddingCallback = callback; updateCallbackIfNecessary(); } @@ -160,14 +162,12 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen */ // TODO(b/190433398): Break down into smaller functions. void onActivityCreated(@NonNull Activity launchedActivity) { - final ComponentName componentName = launchedActivity.getComponentName(); - - final List<ExtensionSplitRule> splitRules = getSplitRules(); + final List<EmbeddingRule> splitRules = getSplitRules(); final TaskFragmentContainer currentContainer = getContainerWithActivity( launchedActivity.getActivityToken(), launchedActivity); // Check if the activity is configured to always be expanded. - if (shouldExpand(componentName, splitRules)) { + if (shouldExpand(launchedActivity, splitRules)) { if (shouldContainerBeExpanded(currentContainer)) { // Make sure that the existing container is expanded mPresenter.expandTaskFragment(currentContainer.getTaskFragmentToken()); @@ -224,8 +224,8 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen } } - final ExtensionSplitPairRule splitPairRule = getSplitRule( - activityBelow.getComponentName(), componentName, splitRules); + final SplitPairRule splitPairRule = getSplitRule(activityBelow, launchedActivity, + splitRules); if (splitPairRule == null) { return; } @@ -296,14 +296,18 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen } /** - * Creates and registers a new split with the provided containers and configuration. + * Creates and registers a new split with the provided containers and configuration. Finishes + * existing secondary containers if found for the given primary container. */ - void registerSplit(@NonNull TaskFragmentContainer primaryContainer, - @NonNull Activity primaryActivity, + void registerSplit(@NonNull WindowContainerTransaction wct, + @NonNull TaskFragmentContainer primaryContainer, @NonNull Activity primaryActivity, @NonNull TaskFragmentContainer secondaryContainer, - @NonNull ExtensionSplitPairRule splitPairRule) { + @NonNull SplitRule splitRule) { + if (splitRule instanceof SplitPairRule && ((SplitPairRule) splitRule).shouldClearTop()) { + removeExistingSecondaryContainers(wct, primaryContainer); + } SplitContainer splitContainer = new SplitContainer(primaryContainer, primaryActivity, - secondaryContainer, splitPairRule); + secondaryContainer, splitRule); mSplitContainers.add(splitContainer); } @@ -324,6 +328,25 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen } /** + * Removes a secondary container for the given primary container if an existing split is + * already registered. + */ + void removeExistingSecondaryContainers(@NonNull WindowContainerTransaction wct, + @NonNull TaskFragmentContainer primaryContainer) { + // If the primary container was already in a split - remove the secondary container that + // is now covered by the new one that replaced it. + final SplitContainer existingSplitContainer = getActiveSplitForContainer( + primaryContainer); + if (existingSplitContainer == null + || primaryContainer == existingSplitContainer.getSecondaryContainer()) { + return; + } + + existingSplitContainer.getSecondaryContainer().finish( + false /* shouldFinishDependent */, mPresenter, wct, this); + } + + /** * Returns the topmost not finished container. */ @Nullable @@ -434,22 +457,20 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen } // Check if there is enough space for launch - final ExtensionSplitPairRule placeholderRule = getPlaceholderRule( - activity.getComponentName()); + final SplitPlaceholderRule placeholderRule = getPlaceholderRule(activity); if (placeholderRule == null || !mPresenter.shouldShowSideBySide( mPresenter.getParentContainerBounds(activity), placeholderRule)) { return false; } - Intent placeholderIntent = new Intent(); - placeholderIntent.setComponent(placeholderRule.secondaryActivityName); // TODO(b/190433398): Handle failed request - startActivityToSide(activity, placeholderIntent, null, placeholderRule, -1); + startActivityToSide(activity, placeholderRule.getPlaceholderIntent(), null, + placeholderRule, null); return true; } private boolean dismissPlaceholderIfNecessary(@NonNull SplitContainer splitContainer) { - if (!splitContainer.getSplitPairRule().useAsPlaceholder) { + if (!splitContainer.isPlaceholderContainer()) { return false; } @@ -466,15 +487,14 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen * Returns the rule to launch a placeholder for the activity with the provided component name * if it is configured in the split config. */ - private ExtensionSplitPairRule getPlaceholderRule(@NonNull ComponentName componentName) { - for (ExtensionSplitRule rule : mSplitRules) { - if (!(rule instanceof ExtensionSplitPairRule)) { + private SplitPlaceholderRule getPlaceholderRule(@NonNull Activity activity) { + for (EmbeddingRule rule : mSplitRules) { + if (!(rule instanceof SplitPlaceholderRule)) { continue; } - ExtensionSplitPairRule pairRule = (ExtensionSplitPairRule) rule; - if (componentName.equals(pairRule.primaryActivityName) - && pairRule.useAsPlaceholder) { - return pairRule; + SplitPlaceholderRule placeholderRule = (SplitPlaceholderRule) rule; + if (placeholderRule.getActivityPredicate().test(activity)) { + return placeholderRule; } } return null; @@ -484,27 +504,27 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen * Notifies listeners about changes to split states if necessary. */ private void updateCallbackIfNecessary() { - if (mSplitOrganizerCallback == null) { + if (mEmbeddingCallback == null) { return; } // TODO(b/190433398): Check if something actually changed - mSplitOrganizerCallback.onSplitInfoChanged(getActiveSplitStates()); + mEmbeddingCallback.accept(getActiveSplitStates()); } /** * Returns a list of descriptors for currently active split states. */ - private List<ExtensionSplitInfo> getActiveSplitStates() { - List<ExtensionSplitInfo> splitStates = new ArrayList<>(); + private List<SplitInfo> getActiveSplitStates() { + List<SplitInfo> splitStates = new ArrayList<>(); for (SplitContainer container : mSplitContainers) { - ExtensionTaskFragment primaryContainer = - new ExtensionTaskFragment( + TaskFragment primaryContainer = + new TaskFragment( container.getPrimaryContainer().collectActivities()); - ExtensionTaskFragment secondaryContainer = - new ExtensionTaskFragment( + TaskFragment secondaryContainer = + new TaskFragment( container.getSecondaryContainer().collectActivities()); - ExtensionSplitInfo splitState = new ExtensionSplitInfo(primaryContainer, - secondaryContainer, container.getSplitPairRule().splitRatio); + SplitInfo splitState = new SplitInfo(primaryContainer, + secondaryContainer, container.getSplitRule().getSplitRatio()); splitStates.add(splitState); } return splitStates; @@ -528,23 +548,41 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen } /** - * Returns a split rule for the provided pair of component names if available. + * Returns a split rule for the provided pair of primary activity and secondary activity intent + * if available. */ @Nullable - private static ExtensionSplitPairRule getSplitRule(@NonNull ComponentName primaryActivityName, - @NonNull ComponentName secondaryActivityName, - @NonNull List<ExtensionSplitRule> splitRules) { - if (splitRules == null || primaryActivityName == null || secondaryActivityName == null) { - return null; + private static SplitPairRule getSplitRule(@NonNull Activity primaryActivity, + @NonNull Intent secondaryActivityIntent, @NonNull List<EmbeddingRule> splitRules) { + for (EmbeddingRule rule : splitRules) { + if (!(rule instanceof SplitPairRule)) { + continue; + } + SplitPairRule pairRule = (SplitPairRule) rule; + if (pairRule.getActivityIntentPredicate().test( + new Pair(primaryActivity, secondaryActivityIntent))) { + return pairRule; + } } + return null; + } - for (ExtensionSplitRule rule : splitRules) { - if (!(rule instanceof ExtensionSplitPairRule)) { + /** + * Returns a split rule for the provided pair of primary and secondary activities if available. + */ + @Nullable + private static SplitPairRule getSplitRule(@NonNull Activity primaryActivity, + @NonNull Activity secondaryActivity, @NonNull List<EmbeddingRule> splitRules) { + for (EmbeddingRule rule : splitRules) { + if (!(rule instanceof SplitPairRule)) { continue; } - ExtensionSplitPairRule pairRule = (ExtensionSplitPairRule) rule; - if (match(secondaryActivityName, pairRule.secondaryActivityName) - && match(primaryActivityName, pairRule.primaryActivityName)) { + SplitPairRule pairRule = (SplitPairRule) rule; + final Intent intent = secondaryActivity.getIntent(); + if (pairRule.getActivityPairPredicate().test( + new Pair(primaryActivity, secondaryActivity)) + && (intent == null || pairRule.getActivityIntentPredicate().test( + new Pair(primaryActivity, intent)))) { return pairRule; } } @@ -565,55 +603,26 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen * Returns {@code true} if an Activity with the provided component name should always be * expanded to occupy full task bounds. Such activity must not be put in a split. */ - private static boolean shouldExpand(@NonNull ComponentName componentName, - List<ExtensionSplitRule> splitRules) { + private static boolean shouldExpand(@NonNull Activity activity, + List<EmbeddingRule> splitRules) { if (splitRules == null) { return false; } - for (ExtensionSplitRule rule : splitRules) { - if (!(rule instanceof ExtensionSplitActivityRule)) { + for (EmbeddingRule rule : splitRules) { + if (!(rule instanceof ActivityRule)) { + continue; + } + ActivityRule activityRule = (ActivityRule) rule; + if (!activityRule.shouldAlwaysExpand()) { continue; } - ExtensionSplitActivityRule activityRule = (ExtensionSplitActivityRule) rule; - if (match(componentName, activityRule.activityName) - && activityRule.alwaysExpand) { + if (activityRule.getActivityPredicate().test(activity)) { return true; } } return false; } - /** Match check allowing wildcards for activity class name but not package name. */ - private static boolean match(@NonNull ComponentName activityComponent, - @NonNull ComponentName ruleComponent) { - if (activityComponent.toString().contains("*")) { - throw new IllegalArgumentException("Wildcard can only be part of the rule."); - } - final boolean packagesMatch = - activityComponent.getPackageName().equals(ruleComponent.getPackageName()); - final boolean classesMatch = - activityComponent.getClassName().equals(ruleComponent.getClassName()); - return packagesMatch && (classesMatch - || wildcardMatch(activityComponent.getClassName(), ruleComponent.getClassName())); - } - - /** - * Checks if the provided name matches the pattern. - */ - private static boolean wildcardMatch(@NonNull String name, @NonNull String pattern) { - if (!pattern.contains("*")) { - return false; - } - if (pattern.equals("*")) { - return true; - } - if (pattern.indexOf("*") != pattern.lastIndexOf("*") || !pattern.endsWith("*")) { - throw new IllegalArgumentException( - "Name pattern with a wildcard must only contain a single * in the end"); - } - return name.startsWith(pattern.substring(0, pattern.length() - 1)); - } - private final class LifecycleCallbacks implements ActivityLifecycleCallbacks { @Override @@ -676,8 +685,8 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen private class ActivityStartMonitor extends Instrumentation.ActivityMonitor { @Override - public Instrumentation.ActivityResult onStartActivity(Context who, Intent intent, - Bundle options) { + public Instrumentation.ActivityResult onStartActivity(@NonNull Context who, + @NonNull Intent intent, @NonNull Bundle options) { // TODO(b/190433398): Check if the activity is configured to always be expanded. // Check if activity should be put in a split with the activity that launched it. @@ -686,10 +695,24 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen } final Activity launchingActivity = (Activity) who; - final ExtensionSplitPairRule splitPairRule = getSplitRule( - launchingActivity.getComponentName(), intent.getComponent(), getSplitRules()); + if (!setLaunchingToSideContainer(launchingActivity, intent, options)) { + setLaunchingInSameContainer(launchingActivity, intent, options); + } + + return super.onStartActivity(who, intent, options); + } + + /** + * Returns {@code true} if the activity that is going to be started via the + * {@code intent} should be paired with the {@code launchingActivity} and is set to be + * launched in an empty side container. + */ + private boolean setLaunchingToSideContainer(Activity launchingActivity, Intent intent, + Bundle options) { + final SplitPairRule splitPairRule = getSplitRule(launchingActivity, intent, + getSplitRules()); if (splitPairRule == null) { - return super.onStartActivity(who, intent, options); + return false; } // Create a new split with an empty side container @@ -700,8 +723,52 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen // dedicated container. options.putBinder(ActivityOptions.KEY_LAUNCH_TASK_FRAGMENT_TOKEN, secondaryContainer.getTaskFragmentToken()); + return true; + } - return super.onStartActivity(who, intent, options); + /** + * Checks if the activity that is going to be started via the {@code intent} should be + * paired with the existing top activity which is currently paired with the + * {@code launchingActivity}. If so, set the activity to be launched in the same + * container of the {@code launchingActivity}. + */ + private void setLaunchingInSameContainer(Activity launchingActivity, Intent intent, + Bundle options) { + final TaskFragmentContainer launchingContainer = getContainerWithActivity( + launchingActivity.getActivityToken()); + if (launchingContainer == null) { + return; + } + + final SplitContainer splitContainer = getActiveSplitForContainer(launchingContainer); + if (splitContainer == null) { + return; + } + + if (splitContainer.getSecondaryContainer() != launchingContainer) { + return; + } + + // The launching activity is on the secondary container. Retrieve the primary + // activity from the other container. + Activity primaryActivity = + splitContainer.getPrimaryContainer().getTopNonFinishingActivity(); + if (primaryActivity == null) { + return; + } + + final SplitPairRule splitPairRule = getSplitRule(primaryActivity, intent, + getSplitRules()); + if (splitPairRule == null) { + return; + } + + // Amend the request to let the WM know that the activity should be placed in the + // dedicated container. This is necessary for the case that the activity is started + // into a new Task, or new Task will be escaped from the current host Task and be + // displayed in fullscreen. + options.putBinder(ActivityOptions.KEY_LAUNCH_TASK_FRAGMENT_TOKEN, + launchingContainer.getTaskFragmentToken()); } } } diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/SplitPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/SplitPresenter.java index 0f356291f50e..ac85ac8cbc34 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/SplitPresenter.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/SplitPresenter.java @@ -24,13 +24,16 @@ import android.content.res.Configuration; import android.graphics.Rect; import android.os.Bundle; import android.os.IBinder; +import android.view.WindowInsets; +import android.view.WindowMetrics; import android.window.TaskFragmentCreationParams; import android.window.WindowContainerTransaction; import androidx.annotation.IntDef; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import androidx.window.extensions.ExtensionSplitPairRule; +import androidx.window.extensions.embedding.SplitPairRule; +import androidx.window.extensions.embedding.SplitRule; import java.util.concurrent.Executor; @@ -89,7 +92,7 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { * @return The newly created secondary container. */ TaskFragmentContainer createNewSplitWithEmptySideContainer(@NonNull Activity primaryActivity, - @NonNull ExtensionSplitPairRule rule) { + @NonNull SplitPairRule rule) { final WindowContainerTransaction wct = new WindowContainerTransaction(); final Rect parentBounds = getParentContainerBounds(primaryActivity); @@ -106,11 +109,12 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { secondaryContainer.setLastRequestedBounds(secondaryRectBounds); // Set adjacent to each other so that the containers below will be invisible. - wct.setAdjacentTaskFragments( - primaryContainer.getTaskFragmentToken(), secondaryContainer.getTaskFragmentToken()); - applyTransaction(wct); + setAdjacentTaskFragments(wct, primaryContainer.getTaskFragmentToken(), + secondaryContainer.getTaskFragmentToken(), rule); + + mController.registerSplit(wct, primaryContainer, primaryActivity, secondaryContainer, rule); - mController.registerSplit(primaryContainer, primaryActivity, secondaryContainer, rule); + applyTransaction(wct); return secondaryContainer; } @@ -127,7 +131,7 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { * @param rule The split rule to be applied to the container. */ void createNewSplitContainer(@NonNull Activity primaryActivity, - @NonNull Activity secondaryActivity, @NonNull ExtensionSplitPairRule rule) { + @NonNull Activity secondaryActivity, @NonNull SplitPairRule rule) { final WindowContainerTransaction wct = new WindowContainerTransaction(); final Rect parentBounds = getParentContainerBounds(primaryActivity); @@ -140,11 +144,12 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { secondaryActivity, secondaryRectBounds, primaryContainer); // Set adjacent to each other so that the containers below will be invisible. - wct.setAdjacentTaskFragments( - primaryContainer.getTaskFragmentToken(), secondaryContainer.getTaskFragmentToken()); - applyTransaction(wct); + setAdjacentTaskFragments(wct, primaryContainer.getTaskFragmentToken(), + secondaryContainer.getTaskFragmentToken(), rule); + + mController.registerSplit(wct, primaryContainer, primaryActivity, secondaryContainer, rule); - mController.registerSplit(primaryContainer, primaryActivity, secondaryContainer, rule); + applyTransaction(wct); } /** @@ -190,7 +195,7 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { * @param rule The split rule to be applied to the container. */ void startActivityToSide(@NonNull Activity launchingActivity, @NonNull Intent activityIntent, - @Nullable Bundle activityOptions, @NonNull ExtensionSplitPairRule rule) { + @Nullable Bundle activityOptions, @NonNull SplitRule rule) { final Rect parentBounds = getParentContainerBounds(launchingActivity); final Rect primaryRectBounds = getBoundsForPosition(POSITION_LEFT, parentBounds, rule); final Rect secondaryRectBounds = getBoundsForPosition(POSITION_RIGHT, parentBounds, rule); @@ -202,20 +207,16 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { } TaskFragmentContainer secondaryContainer = mController.newContainer(null); - startActivityToSide( - primaryContainer.getTaskFragmentToken(), - primaryRectBounds, - launchingActivity, - secondaryContainer.getTaskFragmentToken(), - secondaryRectBounds, - activityIntent, - activityOptions); + final WindowContainerTransaction wct = new WindowContainerTransaction(); + mController.registerSplit(wct, primaryContainer, launchingActivity, secondaryContainer, + rule); + startActivityToSide(wct, primaryContainer.getTaskFragmentToken(), primaryRectBounds, + launchingActivity, secondaryContainer.getTaskFragmentToken(), secondaryRectBounds, + activityIntent, activityOptions, rule); + applyTransaction(wct); primaryContainer.setLastRequestedBounds(primaryRectBounds); secondaryContainer.setLastRequestedBounds(secondaryRectBounds); - - mController.registerSplit(primaryContainer, launchingActivity, secondaryContainer, - rule); } /** @@ -229,7 +230,7 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { @NonNull WindowContainerTransaction wct) { // Getting the parent bounds using the updated container - it will have the recent value. final Rect parentBounds = getParentContainerBounds(updatedContainer); - final ExtensionSplitPairRule rule = splitContainer.getSplitPairRule(); + final SplitRule rule = splitContainer.getSplitRule(); final Rect primaryRectBounds = getBoundsForPosition(POSITION_LEFT, parentBounds, rule); final Rect secondaryRectBounds = getBoundsForPosition(POSITION_RIGHT, parentBounds, rule); @@ -275,24 +276,24 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { boolean shouldShowSideBySide(@NonNull SplitContainer splitContainer) { final Rect parentBounds = getParentContainerBounds(splitContainer.getPrimaryContainer()); - return shouldShowSideBySide(parentBounds, splitContainer.getSplitPairRule()); + return shouldShowSideBySide(parentBounds, splitContainer.getSplitRule()); } - boolean shouldShowSideBySide(@Nullable Rect parentBounds, - @NonNull ExtensionSplitPairRule rule) { - return parentBounds != null && parentBounds.width() >= rule.minWidth - // TODO(b/190433398): Consider proper smallest width computation. - && Math.min(parentBounds.width(), parentBounds.height()) >= rule.minSmallestWidth; + boolean shouldShowSideBySide(@Nullable Rect parentBounds, @NonNull SplitRule rule) { + // TODO(b/190433398): Supply correct insets. + final WindowMetrics parentMetrics = new WindowMetrics(parentBounds, + new WindowInsets(new Rect())); + return rule.getParentWindowMetricsPredicate().test(parentMetrics); } @NonNull private Rect getBoundsForPosition(@Position int position, @NonNull Rect parentBounds, - @NonNull ExtensionSplitPairRule rule) { + @NonNull SplitRule rule) { if (!shouldShowSideBySide(parentBounds, rule)) { return new Rect(); } - float splitRatio = rule.splitRatio; + float splitRatio = rule.getSplitRatio(); switch (position) { case POSITION_LEFT: return new Rect( diff --git a/libs/WindowManager/Jetpack/window-extensions-release.aar b/libs/WindowManager/Jetpack/window-extensions-release.aar Binary files differindex fdbc5f61c451..097febf9770a 100644 --- a/libs/WindowManager/Jetpack/window-extensions-release.aar +++ b/libs/WindowManager/Jetpack/window-extensions-release.aar diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java index ab8a21c58e9c..b5dffba7a0f3 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java @@ -31,6 +31,7 @@ import android.app.ActivityManager.RunningTaskInfo; import android.app.TaskInfo; import android.content.Context; import android.content.LocusId; +import android.content.pm.ActivityInfo; import android.graphics.Rect; import android.os.Binder; import android.os.IBinder; @@ -46,6 +47,7 @@ import android.window.TaskOrganizer; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.util.FrameworkStatsLog; import com.android.wm.shell.common.ScreenshotUtils; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.sizecompatui.SizeCompatUIController; @@ -488,14 +490,40 @@ public class ShellTaskOrganizer extends TaskOrganizer implements } @Override + public void onSizeCompatRestartButtonAppeared(int taskId) { + final TaskAppearedInfo info; + synchronized (mLock) { + info = mTasks.get(taskId); + } + if (info == null) { + return; + } + logSizeCompatRestartButtonEventReported(info, + FrameworkStatsLog.SIZE_COMPAT_RESTART_BUTTON_EVENT_REPORTED__EVENT__APPEARED); + } + + @Override public void onSizeCompatRestartButtonClicked(int taskId) { final TaskAppearedInfo info; synchronized (mLock) { info = mTasks.get(taskId); } - if (info != null) { - restartTaskTopActivityProcessIfVisible(info.getTaskInfo().token); + if (info == null) { + return; + } + logSizeCompatRestartButtonEventReported(info, + FrameworkStatsLog.SIZE_COMPAT_RESTART_BUTTON_EVENT_REPORTED__EVENT__CLICKED); + restartTaskTopActivityProcessIfVisible(info.getTaskInfo().token); + } + + private void logSizeCompatRestartButtonEventReported(@NonNull TaskAppearedInfo info, + int event) { + ActivityInfo topActivityInfo = info.getTaskInfo().topActivityInfo; + if (topActivityInfo == null) { + return; } + FrameworkStatsLog.write(FrameworkStatsLog.SIZE_COMPAT_RESTART_BUTTON_EVENT_REPORTED, + topActivityInfo.applicationInfo.uid, event); } /** diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java index 8405385e174e..3800b8d234f3 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java @@ -19,6 +19,7 @@ package com.android.wm.shell.apppairs; import static android.app.ActivityTaskManager.INVALID_TASK_ID; import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; +import static android.view.WindowManagerPolicyConstants.SPLIT_DIVIDER_LAYER; import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_BOTTOM_OR_RIGHT; import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_TOP_OR_LEFT; @@ -181,7 +182,7 @@ class AppPair implements ShellTaskOrganizer.TaskListener, SplitLayout.SplitLayou // TODO: Is there more we need to do here? mSyncQueue.runInSync(t -> { - t.setLayer(dividerLeash, Integer.MAX_VALUE) + t.setLayer(dividerLeash, SPLIT_DIVIDER_LAYER) .setPosition(mTaskLeash1, mTaskInfo1.positionInParent.x, mTaskInfo1.positionInParent.y) .setPosition(mTaskLeash2, mTaskInfo2.positionInParent.x, diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java index d73ce6951e6d..b48bda3a6e48 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java @@ -699,10 +699,9 @@ public class BubbleData { if (DEBUG_BUBBLE_DATA) { Log.d(TAG, "setSelectedBubbleInternal: " + bubble); } - if (!mShowingOverflow && Objects.equals(bubble, mSelectedBubble)) { + if (Objects.equals(bubble, mSelectedBubble)) { return; } - // Otherwise, if we are showing the overflow menu, return to the previously selected bubble. boolean isOverflow = bubble != null && BubbleOverflow.KEY.equals(bubble.getKey()); if (bubble != null && !mBubbles.contains(bubble) @@ -771,6 +770,10 @@ public class BubbleData { Log.e(TAG, "Attempt to expand stack without selected bubble!"); return; } + if (mSelectedBubble.getKey().equals(mOverflow.getKey()) && !mBubbles.isEmpty()) { + // Show previously selected bubble instead of overflow menu when expanding. + setSelectedBubbleInternal(mBubbles.get(0)); + } if (mSelectedBubble instanceof Bubble) { ((Bubble) mSelectedBubble).markAsAccessedAt(mTimeSource.currentTimeMillis()); } @@ -779,16 +782,6 @@ public class BubbleData { // Apply ordering and grouping rules from expanded -> collapsed, then save // the result. mStateChange.orderChanged |= repackAll(); - // Save the state which should be returned to when expanded (with no other changes) - - if (mShowingOverflow) { - // Show previously selected bubble instead of overflow menu on next expansion. - if (!mSelectedBubble.getKey().equals(mOverflow.getKey())) { - setSelectedBubbleInternal(mSelectedBubble); - } else { - setSelectedBubbleInternal(mBubbles.get(0)); - } - } if (mBubbles.indexOf(mSelectedBubble) > 0) { // Move the selected bubble to the top while collapsed. int index = mBubbles.indexOf(mSelectedBubble); 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 4ac7dcf3d23a..7d7bfb2a92a3 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 @@ -696,8 +696,10 @@ public class BubbleExpandedView extends LinearLayout { ? mPointerHeight - mPointerOverlap : 0; final float paddingRight = (showVertically && !onLeft) - ? mPointerHeight - mPointerOverlap : 0; - final float paddingTop = showVertically ? 0 + ? mPointerHeight - mPointerOverlap + : 0; + final float paddingTop = showVertically + ? 0 : mPointerHeight - mPointerOverlap; setPadding((int) paddingLeft, (int) paddingTop, (int) paddingRight, 0); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java index 7d4fb21584c7..306224bd316c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java @@ -79,14 +79,18 @@ public class BubblePositioner { private int mBubbleSize; private int mSpacingBetweenBubbles; - private float mExpandedViewLargeScreenWidth; + + private int mExpandedViewMinHeight; + private int mExpandedViewLargeScreenWidth; + private int mExpandedViewLargeScreenInset; + private int mOverflowWidth; private int mExpandedViewPadding; private int mPointerMargin; private int mPointerWidth; private int mPointerHeight; + private int mPointerOverlap; private int mManageButtonHeight; - private int mExpandedViewMinHeight; private int mOverflowHeight; private int mMinimumFlyoutWidthLargeScreen; @@ -168,15 +172,20 @@ public class BubblePositioner { mBubbleSize = res.getDimensionPixelSize(R.dimen.bubble_size); mSpacingBetweenBubbles = res.getDimensionPixelSize(R.dimen.bubble_spacing); mDefaultMaxBubbles = res.getInteger(R.integer.bubbles_max_rendered); - mExpandedViewLargeScreenWidth = bounds.width() * EXPANDED_VIEW_LARGE_SCREEN_WIDTH_PERCENT; + mExpandedViewPadding = res.getDimensionPixelSize(R.dimen.bubble_expanded_view_padding); + mExpandedViewLargeScreenWidth = (int) (bounds.width() + * EXPANDED_VIEW_LARGE_SCREEN_WIDTH_PERCENT); + mExpandedViewLargeScreenInset = mIsLargeScreen + ? (bounds.width() - mExpandedViewLargeScreenWidth) / 2 + : mExpandedViewPadding; mOverflowWidth = mIsLargeScreen - ? (int) mExpandedViewLargeScreenWidth + ? mExpandedViewLargeScreenWidth : res.getDimensionPixelSize( R.dimen.bubble_expanded_view_phone_landscape_overflow_width); - mExpandedViewPadding = res.getDimensionPixelSize(R.dimen.bubble_expanded_view_padding); mPointerWidth = res.getDimensionPixelSize(R.dimen.bubble_pointer_width); mPointerHeight = res.getDimensionPixelSize(R.dimen.bubble_pointer_height); mPointerMargin = res.getDimensionPixelSize(R.dimen.bubble_pointer_margin); + mPointerOverlap = res.getDimensionPixelSize(R.dimen.bubble_pointer_overlap); mManageButtonHeight = res.getDimensionPixelSize(R.dimen.bubble_manage_button_total_height); mExpandedViewMinHeight = res.getDimensionPixelSize(R.dimen.bubble_expanded_default_height); mOverflowHeight = res.getDimensionPixelSize(R.dimen.bubble_overflow_height); @@ -294,66 +303,93 @@ public class BubblePositioner { } /** - * Calculates the left & right padding for the bubble expanded view. + * Calculates the padding for the bubble expanded view. * - * On larger screens the width of the expanded view is restricted via this padding. - * On landscape the bubble overflow expanded view is also restricted via this padding. + * Some specifics: + * On large screens the width of the expanded view is restricted via this padding. + * On phone landscape the bubble overflow expanded view is also restricted via this padding. + * On large screens & landscape no top padding is set, the top position is set via translation. + * On phone portrait top padding is set as the space between the tip of the pointer and the + * bubble. + * When the overflow is shown it doesn't have the manage button to pad out the bottom so + * padding is added. */ - public int[] getExpandedViewPadding(boolean onLeft, boolean isOverflow) { - int leftPadding = mInsets.left + mExpandedViewPadding; - int rightPadding = mInsets.right + mExpandedViewPadding; - final boolean isLargeOrOverflow = mIsLargeScreen || isOverflow; - final float expandedViewWidth = isOverflow - ? mOverflowWidth - : mExpandedViewLargeScreenWidth; - if (showBubblesVertically()) { - if (!onLeft) { - rightPadding += mBubbleSize - mPointerHeight; - leftPadding += isLargeOrOverflow - ? (mPositionRect.width() - rightPadding - expandedViewWidth) - : 0; - } else { - leftPadding += mBubbleSize - mPointerHeight; - rightPadding += isLargeOrOverflow - ? (mPositionRect.width() - leftPadding - expandedViewWidth) - : 0; + public int[] getExpandedViewContainerPadding(boolean onLeft, boolean isOverflow) { + final int pointerTotalHeight = mPointerHeight - mPointerOverlap; + if (mIsLargeScreen) { + // [left, top, right, bottom] + mPaddings[0] = onLeft + ? mExpandedViewLargeScreenInset - pointerTotalHeight + : mExpandedViewLargeScreenInset; + mPaddings[1] = 0; + mPaddings[2] = onLeft + ? mExpandedViewLargeScreenInset + : mExpandedViewLargeScreenInset - pointerTotalHeight; + // Overflow doesn't show manage button / get padding from it so add padding here for it + mPaddings[3] = isOverflow ? mExpandedViewPadding : 0; + return mPaddings; + } else { + int leftPadding = mInsets.left + mExpandedViewPadding; + int rightPadding = mInsets.right + mExpandedViewPadding; + final float expandedViewWidth = isOverflow + ? mOverflowWidth + : mExpandedViewLargeScreenWidth; + if (showBubblesVertically()) { + if (!onLeft) { + rightPadding += mBubbleSize - pointerTotalHeight; + leftPadding += isOverflow + ? (mPositionRect.width() - rightPadding - expandedViewWidth) + : 0; + } else { + leftPadding += mBubbleSize - pointerTotalHeight; + rightPadding += isOverflow + ? (mPositionRect.width() - leftPadding - expandedViewWidth) + : 0; + } } + // [left, top, right, bottom] + mPaddings[0] = leftPadding; + mPaddings[1] = showBubblesVertically() ? 0 : mPointerMargin; + mPaddings[2] = rightPadding; + mPaddings[3] = 0; + return mPaddings; } - // [left, top, right, bottom] - mPaddings[0] = leftPadding; - mPaddings[1] = showBubblesVertically() ? 0 : mPointerMargin; - mPaddings[2] = rightPadding; - mPaddings[3] = 0; - return mPaddings; } /** Gets the y position of the expanded view if it was top-aligned. */ private float getExpandedViewYTopAligned() { final int top = getAvailableRect().top; if (showBubblesVertically()) { - return top - mPointerWidth; + return top - mPointerWidth + mExpandedViewPadding; } else { return top + mBubbleSize + mPointerMargin; } } - /** The maximum height the expanded view can be. */ + public float getExpandedBubblesY() { + return getAvailableRect().top + mExpandedViewPadding; + } + + /** + * Calculate the maximum height the expanded view can be depending on where it's placed on + * the screen and the size of the elements around it (e.g. padding, pointer, manage button). + */ public int getMaxExpandedViewHeight(boolean isOverflow) { + // Subtract top insets because availableRect.height would account for that + int expandedContainerY = (int) getExpandedViewYTopAligned() - getInsets().top; int paddingTop = showBubblesVertically() ? 0 : mPointerHeight; - int settingsHeight = isOverflow ? 0 : mManageButtonHeight; // Subtract pointer size because it's laid out in LinearLayout with the expanded view. int pointerSize = showBubblesVertically() ? mPointerWidth : (mPointerHeight + mPointerMargin); - // Subtract top insets because availableRect.height would account for that - int expandedContainerY = (int) getExpandedViewYTopAligned() - getInsets().top; + int bottomPadding = isOverflow ? mExpandedViewPadding : mManageButtonHeight; return getAvailableRect().height() - expandedContainerY - paddingTop - - settingsHeight - - pointerSize; + - pointerSize + - bottomPadding; } /** @@ -362,11 +398,14 @@ public class BubblePositioner { */ public float getExpandedViewHeight(BubbleViewProvider bubble) { boolean isOverflow = bubble == null || BubbleOverflow.KEY.equals(bubble.getKey()); + if (isOverflow && showBubblesVertically() && !mIsLargeScreen) { + // overflow in landscape on phone is max + return MAX_HEIGHT; + } float desiredHeight = isOverflow ? mOverflowHeight : ((Bubble) bubble).getDesiredHeight(mContext); - int manageButtonHeight = isOverflow ? 0 : mManageButtonHeight; - desiredHeight = Math.max(manageButtonHeight + desiredHeight, mExpandedViewMinHeight); + desiredHeight = Math.max(desiredHeight, mExpandedViewMinHeight); if (desiredHeight > getMaxExpandedViewHeight(isOverflow)) { return MAX_HEIGHT; } @@ -391,7 +430,7 @@ public class BubblePositioner { return topAlignment; } // If we're here, we're showing vertically & developer has made height less than maximum. - int manageButtonHeight = isOverflow ? 0 : mManageButtonHeight; + int manageButtonHeight = isOverflow ? mExpandedViewPadding : mManageButtonHeight; float pointerPosition = getPointerPosition(bubblePosition); float bottomIfCentered = pointerPosition + (expandedViewHeight / 2) + manageButtonHeight; float topIfCentered = pointerPosition - (expandedViewHeight / 2); @@ -426,25 +465,40 @@ public class BubblePositioner { } /** - * When bubbles are expanded in portrait, they display at the top of the screen in a horizontal - * row. When in landscape or on a large screen, they show at the left or right side in a - * vertical row. This method accounts for screen orientation and will return an x or y value - * for the position of the bubble in the row. + * Returns the position of the bubble on-screen when the stack is expanded. * - * @param index bubble index in the row. - * @param numberOfBubbles the number of bubbles (including the overflow) in the row. - * @return the y position of the bubble if showing vertically and the x position if showing - * horizontally. + * @param index the index of the bubble in the stack. + * @param numberOfBubbles the total number of bubbles in the stack. + * @param onLeftEdge whether the stack would rest on the left edge of the screen when collapsed. + * @return the x, y position of the bubble on-screen when the stack is expanded. */ - public float getBubbleXOrYForOrientation(int index, int numberOfBubbles) { - final float positionInBar = index * (mBubbleSize + mSpacingBetweenBubbles); + public PointF getExpandedBubbleXY(int index, int numberOfBubbles, boolean onLeftEdge) { + final float positionInRow = index * (mBubbleSize + mSpacingBetweenBubbles); final float expandedStackSize = (numberOfBubbles * mBubbleSize) + ((numberOfBubbles - 1) * mSpacingBetweenBubbles); final float centerPosition = showBubblesVertically() ? mPositionRect.centerY() : mPositionRect.centerX(); + // alignment - centered on the edge final float rowStart = centerPosition - (expandedStackSize / 2f); - return rowStart + positionInBar; + float x; + float y; + if (showBubblesVertically()) { + y = rowStart + positionInRow; + int left = mIsLargeScreen + ? mExpandedViewLargeScreenInset - mExpandedViewPadding - mBubbleSize + : mPositionRect.left; + int right = mIsLargeScreen + ? mPositionRect.right - mExpandedViewLargeScreenInset + mExpandedViewPadding + : mPositionRect.right - mBubbleSize; + x = onLeftEdge + ? left + : right; + } else { + y = mPositionRect.top + mExpandedViewPadding; + x = rowStart + positionInRow; + } + return new PointF(x, y); } /** @@ -459,6 +513,17 @@ public class BubblePositioner { } /** + * @return whether the stack is considered on the left side of the screen. + */ + public boolean isStackOnLeft(PointF currentStackPosition) { + if (currentStackPosition == null) { + currentStackPosition = getRestingPosition(); + } + final int stackCenter = (int) currentStackPosition.x + mBubbleSize / 2; + return stackCenter < mScreenRect.width() / 2; + } + + /** * Sets the stack's most recent position along the edge of the screen. This is saved when the * last bubble is removed, so that the stack can be restored in its previous position. */ diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java index 3d2505ef7699..5a51eed04e1a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java @@ -778,8 +778,8 @@ public class BubbleStackView extends FrameLayout floatingContentCoordinator, this::getBubbleCount, onBubbleAnimatedOut, this::animateShadows /* onStackAnimationFinished */, mPositioner); - mExpandedAnimationController = new ExpandedAnimationController( - mPositioner, mExpandedViewPadding, onBubbleAnimatedOut); + mExpandedAnimationController = new ExpandedAnimationController(mPositioner, + onBubbleAnimatedOut); mSurfaceSynchronizer = synchronizer != null ? synchronizer : DEFAULT_SURFACE_SYNCHRONIZER; // Force LTR by default since most of the Bubbles UI is positioned manually by the user, or @@ -1869,28 +1869,28 @@ public class BubbleStackView extends FrameLayout maybeShowManageEdu(); } } /* after */); - - final float translationY = mPositioner.getExpandedViewY(mExpandedBubble, - getBubbleIndex(mExpandedBubble)); - mExpandedViewContainer.setTranslationX(0f); - mExpandedViewContainer.setTranslationY(translationY); - mExpandedViewContainer.setAlpha(1f); - int index; if (mExpandedBubble != null && BubbleOverflow.KEY.equals(mExpandedBubble.getKey())) { index = mBubbleData.getBubbles().size(); } else { index = getBubbleIndex(mExpandedBubble); } - // Position of the bubble we're expanding, once it's settled in its row. - final float bubbleWillBeAt = - mExpandedAnimationController.getBubbleXOrYForOrientation(index); + PointF p = mPositioner.getExpandedBubbleXY(index, mBubbleContainer.getChildCount(), + mStackOnLeftOrWillBe); + final float translationY = mPositioner.getExpandedViewY(mExpandedBubble, + mPositioner.showBubblesVertically() ? p.y : p.x); + mExpandedViewContainer.setTranslationX(0f); + mExpandedViewContainer.setTranslationY(translationY); + mExpandedViewContainer.setAlpha(1f); // How far horizontally the bubble will be animating. We'll wait a bit longer for bubbles // that are animating farther, so that the expanded view doesn't move as much. final float relevantStackPosition = showVertically ? mStackAnimationController.getStackPosition().y : mStackAnimationController.getStackPosition().x; + final float bubbleWillBeAt = showVertically + ? p.y + : p.x; final float distanceAnimated = Math.abs(bubbleWillBeAt - relevantStackPosition); // Wait for the path animation target to reach its end, and add a small amount of extra time @@ -1907,22 +1907,22 @@ public class BubbleStackView extends FrameLayout // Set the pivot point for the scale, so the expanded view animates out from the bubble. if (showVertically) { float pivotX; - float pivotY = bubbleWillBeAt + mBubbleSize / 2f; if (mStackOnLeftOrWillBe) { - pivotX = mPositioner.getAvailableRect().left + mBubbleSize + mExpandedViewPadding; + pivotX = p.x + mBubbleSize + mExpandedViewPadding; } else { - pivotX = mPositioner.getAvailableRect().right - mBubbleSize - mExpandedViewPadding; + pivotX = p.x - mExpandedViewPadding; } mExpandedViewContainerMatrix.setScale( 1f - EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT, 1f - EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT, - pivotX, pivotY); + pivotX, + p.y + mBubbleSize / 2f); } else { mExpandedViewContainerMatrix.setScale( 1f - EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT, 1f - EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT, - bubbleWillBeAt + mBubbleSize / 2f, - translationY); + p.x + mBubbleSize / 2f, + p.y + mBubbleSize + mExpandedViewPadding); } mExpandedViewContainer.setAnimationMatrix(mExpandedViewContainerMatrix); @@ -1959,6 +1959,7 @@ public class BubbleStackView extends FrameLayout mExpandedViewContainerMatrix); }) .withEndActions(() -> { + mExpandedViewContainer.setAnimationMatrix(null); afterExpandedViewAnimation(); if (mExpandedBubble != null && mExpandedBubble.getExpandedView() != null) { @@ -2009,12 +2010,11 @@ public class BubbleStackView extends FrameLayout index = mBubbleData.getBubbles().indexOf(mExpandedBubble); } // Value the bubble is animating from (back into the stack). - final float expandingFromBubbleAt = - mExpandedAnimationController.getBubbleXOrYForOrientation(index); - final boolean showVertically = mPositioner.showBubblesVertically(); + final PointF p = mPositioner.getExpandedBubbleXY(index, + mBubbleContainer.getChildCount(), mStackOnLeftOrWillBe); if (mPositioner.showBubblesVertically()) { float pivotX; - float pivotY = expandingFromBubbleAt + mBubbleSize / 2f; + float pivotY = p.y + mBubbleSize / 2f; if (mStackOnLeftOrWillBe) { pivotX = mPositioner.getAvailableRect().left + mBubbleSize + mExpandedViewPadding; } else { @@ -2026,8 +2026,8 @@ public class BubbleStackView extends FrameLayout } else { mExpandedViewContainerMatrix.setScale( 1f, 1f, - expandingFromBubbleAt + mBubbleSize / 2f, - mPositioner.getExpandedViewY(mExpandedBubble, index)); + p.x + mBubbleSize / 2f, + p.y + mBubbleSize + mExpandedViewPadding); } mExpandedViewAlphaAnimator.reverse(); @@ -2105,32 +2105,31 @@ public class BubbleStackView extends FrameLayout boolean isOverflow = mExpandedBubble != null && mExpandedBubble.getKey().equals(BubbleOverflow.KEY); - float expandingFromBubbleDestination = - mExpandedAnimationController.getBubbleXOrYForOrientation(isOverflow - ? getBubbleCount() - : mBubbleData.getBubbles().indexOf(mExpandedBubble)); - + PointF p = mPositioner.getExpandedBubbleXY(isOverflow + ? mBubbleContainer.getChildCount() - 1 + : mBubbleData.getBubbles().indexOf(mExpandedBubble), + mBubbleContainer.getChildCount(), mStackOnLeftOrWillBe); mExpandedViewContainer.setAlpha(1f); mExpandedViewContainer.setVisibility(View.VISIBLE); if (mPositioner.showBubblesVertically()) { float pivotX; - float pivotY = expandingFromBubbleDestination + mBubbleSize / 2f; + float pivotY = p.y + mBubbleSize / 2f; if (mStackOnLeftOrWillBe) { - pivotX = mPositioner.getAvailableRect().left + mBubbleSize + mExpandedViewPadding; + pivotX = p.x + mBubbleSize + mExpandedViewPadding; } else { - pivotX = mPositioner.getAvailableRect().right - mBubbleSize - mExpandedViewPadding; - + pivotX = p.x - mExpandedViewPadding; } mExpandedViewContainerMatrix.setScale( - 0f, 0f, + 1f - EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT, + 1f - EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT, pivotX, pivotY); } else { mExpandedViewContainerMatrix.setScale( 1f - EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT, 1f - EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT, - expandingFromBubbleDestination + mBubbleSize / 2f, - mPositioner.getExpandedViewY(mExpandedBubble, expandingFromBubbleDestination)); + p.x + mBubbleSize / 2f, + p.y + mBubbleSize + mExpandedViewPadding); } mExpandedViewContainer.setAnimationMatrix(mExpandedViewContainerMatrix); @@ -2155,6 +2154,7 @@ public class BubbleStackView extends FrameLayout .withEndActions(() -> { mExpandedViewTemporarilyHidden = false; mIsBubbleSwitchAnimating = false; + mExpandedViewContainer.setAnimationMatrix(null); }) .start(); }, 25); @@ -2764,16 +2764,17 @@ public class BubbleStackView extends FrameLayout } boolean isOverflowExpanded = mExpandedBubble != null && BubbleOverflow.KEY.equals(mExpandedBubble.getKey()); - int[] paddings = mPositioner.getExpandedViewPadding( + int[] paddings = mPositioner.getExpandedViewContainerPadding( mStackAnimationController.isStackOnLeftSide(), isOverflowExpanded); mExpandedViewContainer.setPadding(paddings[0], paddings[1], paddings[2], paddings[3]); if (mIsExpansionAnimating) { mExpandedViewContainer.setVisibility(mIsExpanded ? VISIBLE : GONE); } if (mExpandedBubble != null && mExpandedBubble.getExpandedView() != null) { + PointF p = mPositioner.getExpandedBubbleXY(getBubbleIndex(mExpandedBubble), + mBubbleContainer.getChildCount(), mStackOnLeftOrWillBe); mExpandedViewContainer.setTranslationY(mPositioner.getExpandedViewY(mExpandedBubble, - mExpandedAnimationController.getBubbleXOrYForOrientation( - getBubbleIndex(mExpandedBubble)))); + mPositioner.showBubblesVertically() ? p.y : p.x)); mExpandedViewContainer.setTranslationX(0f); mExpandedBubble.getExpandedView().updateView( mExpandedViewContainer.getLocationOnScreen()); @@ -2856,8 +2857,13 @@ public class BubbleStackView extends FrameLayout if (index == -1) { return; } - float bubblePosition = mExpandedAnimationController.getBubbleXOrYForOrientation(index); - mExpandedBubble.getExpandedView().setPointerPosition(bubblePosition, mStackOnLeftOrWillBe); + PointF bubblePosition = mPositioner.getExpandedBubbleXY(index, + mBubbleContainer.getChildCount(), + mStackOnLeftOrWillBe); + mExpandedBubble.getExpandedView().setPointerPosition(mPositioner.showBubblesVertically() + ? bubblePosition.y + : bubblePosition.x, + mStackOnLeftOrWillBe); } /** diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java index 007ddbf63141..c32be98866cf 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java @@ -76,8 +76,6 @@ public class ExpandedAnimationController /** Horizontal offset between bubbles, which we need to know to re-stack them. */ private float mStackOffsetPx; - /** Space between status bar and bubbles in the expanded state. */ - private float mBubblePaddingTop; /** Size of each bubble. */ private float mBubbleSizePx; /** Whether the expand / collapse animation is running. */ @@ -118,8 +116,6 @@ public class ExpandedAnimationController /** The bubble currently being dragged out of the row (to potentially be dismissed). */ private MagnetizedObject<View> mMagnetizedBubbleDraggingOut; - private int mExpandedViewPadding; - /** * Callback to run whenever any bubble is animated out. The BubbleStackView will check if the * end of this animation means we have no bubbles left, and notify the BubbleController. @@ -128,11 +124,10 @@ public class ExpandedAnimationController private BubblePositioner mPositioner; - public ExpandedAnimationController(BubblePositioner positioner, int expandedViewPadding, + public ExpandedAnimationController(BubblePositioner positioner, Runnable onBubbleAnimatedOutAction) { mPositioner = positioner; updateResources(); - mExpandedViewPadding = expandedViewPadding; mOnBubbleAnimatedOutAction = onBubbleAnimatedOutAction; mCollapsePoint = mPositioner.getDefaultStartPosition(); } @@ -199,7 +194,6 @@ public class ExpandedAnimationController return; } Resources res = mLayout.getContext().getResources(); - mBubblePaddingTop = res.getDimensionPixelSize(R.dimen.bubble_padding_top); mStackOffsetPx = res.getDimensionPixelSize(R.dimen.bubble_stack_offset); mBubbleSizePx = mPositioner.getBubbleSize(); } @@ -245,31 +239,22 @@ public class ExpandedAnimationController final Path path = new Path(); path.moveTo(bubble.getTranslationX(), bubble.getTranslationY()); - final float expandedY = mPositioner.showBubblesVertically() - ? getBubbleXOrYForOrientation(index) - : getExpandedY(); + boolean onLeft = mPositioner.isStackOnLeft(mCollapsePoint); + final PointF p = mPositioner.getExpandedBubbleXY(index, + mLayout.getChildCount(), + onLeft); if (expanding) { - // If we're expanding, first draw a line from the bubble's current position to the - // top of the screen. - path.lineTo(bubble.getTranslationX(), expandedY); + // If we're expanding, first draw a line from the bubble's current position to where + // it'll end up + path.lineTo(bubble.getTranslationX(), p.y); // Then, draw a line across the screen to the bubble's resting position. - if (mPositioner.showBubblesVertically()) { - Rect availableRect = mPositioner.getAvailableRect(); - boolean onLeft = mCollapsePoint != null - && mCollapsePoint.x < (availableRect.width() / 2f); - float translationX = onLeft - ? availableRect.left - : availableRect.right - mBubbleSizePx; - path.lineTo(translationX, getBubbleXOrYForOrientation(index)); - } else { - path.lineTo(getBubbleXOrYForOrientation(index), expandedY); - } + path.lineTo(p.x, p.y); } else { final float stackedX = mCollapsePoint.x; // If we're collapsing, draw a line from the bubble's current position to the side // of the screen where the bubble will be stacked. - path.lineTo(stackedX, expandedY); + path.lineTo(stackedX, p.y); // Then, draw a line down to the stack position. path.lineTo(stackedX, mCollapsePoint.y @@ -379,8 +364,9 @@ public class ExpandedAnimationController bubbleView.setTranslationY(y); } + final float expandedY = mPositioner.getExpandedBubblesY(); final boolean draggedOutEnough = - y > getExpandedY() + mBubbleSizePx || y < getExpandedY() - mBubbleSizePx; + y > expandedY + mBubbleSizePx || y < expandedY - mBubbleSizePx; if (draggedOutEnough != mBubbleDraggedOutEnough) { updateBubblePositions(); mBubbleDraggedOutEnough = draggedOutEnough; @@ -424,9 +410,10 @@ public class ExpandedAnimationController return; } final int index = mLayout.indexOfChild(bubbleView); - + final PointF p = mPositioner.getExpandedBubbleXY(index, mLayout.getChildCount(), + mPositioner.isStackOnLeft(mCollapsePoint)); animationForChildAtIndex(index) - .position(getBubbleXOrYForOrientation(index), getExpandedY()) + .position(p.x, p.y) .withPositionStartVelocities(velX, velY) .start(() -> bubbleView.setTranslationZ(0f) /* after */); @@ -443,17 +430,13 @@ public class ExpandedAnimationController } /** - * Animates the bubbles to {@link #getExpandedY()} position. Used in response to IME showing. + * Animates the bubbles to the y position. Used in response to IME showing. */ public void updateYPosition(Runnable after) { if (mLayout == null) return; animationsForChildrenFromIndex( - 0, (i, anim) -> anim.translationY(getExpandedY())).startAll(after); - } - - /** The Y value of the row of expanded bubbles. */ - public float getExpandedY() { - return mPositioner.getAvailableRect().top + mBubblePaddingTop; + 0, (i, anim) -> anim.translationY(mPositioner.getExpandedBubblesY())) + .startAll(after); } /** Description of current animation controller state. */ @@ -511,35 +494,36 @@ public class ExpandedAnimationController startOrUpdatePathAnimation(true /* expanding */); } else if (mAnimatingCollapse) { startOrUpdatePathAnimation(false /* expanding */); - } else if (mPositioner.showBubblesVertically()) { - child.setTranslationY(getBubbleXOrYForOrientation(index)); - if (!mPreparingToCollapse) { - // Only animate if we're not collapsing as that animation will handle placing the - // new bubble in the stacked position. - Rect availableRect = mPositioner.getAvailableRect(); - boolean onLeft = mCollapsePoint != null - && mCollapsePoint.x < (availableRect.width() / 2f); - float fromX = onLeft - ? -mBubbleSizePx * ANIMATE_TRANSLATION_FACTOR - : availableRect.right + mBubbleSizePx * ANIMATE_TRANSLATION_FACTOR; - float toX = onLeft - ? availableRect.left + mExpandedViewPadding - : availableRect.right - mBubbleSizePx - mExpandedViewPadding; - animationForChild(child) - .translationX(fromX, toX) - .start(); - updateBubblePositions(); - } } else { - child.setTranslationX(getBubbleXOrYForOrientation(index)); + boolean onLeft = mPositioner.isStackOnLeft(mCollapsePoint); + final PointF p = mPositioner.getExpandedBubbleXY(index, + mLayout.getChildCount(), + onLeft); + if (mPositioner.showBubblesVertically()) { + child.setTranslationY(p.y); + } else { + child.setTranslationX(p.x); + } if (!mPreparingToCollapse) { // Only animate if we're not collapsing as that animation will handle placing the // new bubble in the stacked position. - float toY = getExpandedY(); - float fromY = getExpandedY() - mBubbleSizePx * ANIMATE_TRANSLATION_FACTOR; - animationForChild(child) - .translationY(fromY, toY) - .start(); + if (mPositioner.showBubblesVertically()) { + Rect availableRect = mPositioner.getAvailableRect(); + float fromX = onLeft + ? -mBubbleSizePx * ANIMATE_TRANSLATION_FACTOR + : availableRect.right + mBubbleSizePx * ANIMATE_TRANSLATION_FACTOR; + animationForChild(child) + .translationX(fromX, p.y) + .start(); + } else { + // Only animate if we're not collapsing as that animation will handle placing + // the new bubble in the stacked position. + float fromY = mPositioner.getExpandedBubblesY() - mBubbleSizePx + * ANIMATE_TRANSLATION_FACTOR; + animationForChild(child) + .translationY(fromY, p.y) + .start(); + } updateBubblePositions(); } } @@ -588,7 +572,7 @@ public class ExpandedAnimationController if (mAnimatingExpand || mAnimatingCollapse) { return; } - + boolean onLeft = mPositioner.isStackOnLeft(mCollapsePoint); for (int i = 0; i < mLayout.getChildCount(); i++) { final View bubble = mLayout.getChildAt(i); @@ -598,39 +582,11 @@ public class ExpandedAnimationController return; } - if (mPositioner.showBubblesVertically()) { - Rect availableRect = mPositioner.getAvailableRect(); - boolean onLeft = mCollapsePoint != null - && mCollapsePoint.x < (availableRect.width() / 2f); - animationForChild(bubble) - .translationX(onLeft - ? availableRect.left - : availableRect.right - mBubbleSizePx) - .translationY(getBubbleXOrYForOrientation(i)) - .start(); - } else { - animationForChild(bubble) - .translationX(getBubbleXOrYForOrientation(i)) - .translationY(getExpandedY()) - .start(); - } - } - } - - /** - * When bubbles are expanded in portrait, they display at the top of the screen in a horizontal - * row. When in landscape or on a large screen, they show at the left or right side in a - * vertical row. This method accounts for screen orientation and will return an x or y value - * for the position of the bubble in the row. - * - * @param index bubble index in the row. - * @return the y position of the bubble if showing vertically and the x position if showing - * horizontally. - */ - public float getBubbleXOrYForOrientation(int index) { - if (mLayout == null) { - return 0; + final PointF p = mPositioner.getExpandedBubbleXY(i, mLayout.getChildCount(), onLeft); + animationForChild(bubble) + .translationX(p.x) + .translationY(p.y) + .start(); } - return mPositioner.getBubbleXOrYForOrientation(index, mLayout.getChildCount()); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java index 636e1452aa9b..9a08190675b6 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java @@ -305,10 +305,7 @@ public class StackAnimationController extends if (mLayout == null || !isStackPositionSet()) { return true; // Default to left, which is where it starts by default. } - - float stackCenter = mStackPosition.x + mBubbleSize / 2; - float screenCenter = mLayout.getWidth() / 2; - return stackCenter < screenCenter; + return mPositioner.isStackOnLeft(mStackPosition); } /** diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java index bb40f679b272..3b9bcd36bea8 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java @@ -23,6 +23,7 @@ import static android.view.WindowManager.DOCKED_INVALID; import static android.view.WindowManager.DOCKED_LEFT; import static android.view.WindowManager.DOCKED_RIGHT; import static android.view.WindowManager.DOCKED_TOP; +import static android.view.WindowManagerPolicyConstants.SPLIT_DIVIDER_LAYER; import static com.android.internal.policy.DividerSnapAlgorithm.SnapTarget.FLAG_DISMISS_END; import static com.android.internal.policy.DividerSnapAlgorithm.SnapTarget.FLAG_DISMISS_START; @@ -92,6 +93,7 @@ public final class SplitLayout { private final Rect mDividerBounds = new Rect(); private final Rect mBounds1 = new Rect(); private final Rect mBounds2 = new Rect(); + private final Rect mTmpBounds = new Rect(); private final SplitLayoutHandler mSplitLayoutHandler; private final SplitWindowManager mSplitWindowManager; private final DisplayImeController mDisplayImeController; @@ -158,6 +160,15 @@ public final class SplitLayout { return mDividePosition; } + /** + * Returns the divider position as a fraction from 0 to 1. + */ + public float getDividerPositionAsFraction() { + return Math.min(1f, Math.max(0f, isLandscape() + ? (float) ((mBounds1.right + mBounds2.left) / 2f) / mBounds2.right + : (float) ((mBounds1.bottom + mBounds2.top) / 2f) / mBounds2.bottom)); + } + /** Applies new configuration, returns {@code false} if there's no effect to the layout. */ public boolean updateConfiguration(Configuration configuration) { boolean affectsLayout = false; @@ -178,9 +189,10 @@ public final class SplitLayout { final int rotation = configuration.windowConfiguration.getRotation(); final Rect rootBounds = configuration.windowConfiguration.getBounds(); if (rotation != mRotation || !mRootBounds.equals(rootBounds)) { + mTmpBounds.set(mRootBounds); mRootBounds.set(rootBounds); mDividerSnapAlgorithm = getSnapAlgorithm(mContext, mRootBounds); - resetDividerPosition(); + initDividerPosition(mTmpBounds); affectsLayout = true; } @@ -192,6 +204,19 @@ public final class SplitLayout { return affectsLayout; } + private void initDividerPosition(Rect oldBounds) { + final float snapRatio = (float) mDividePosition + / (float) (isLandscape(oldBounds) ? oldBounds.width() : oldBounds.height()); + // Estimate position by previous ratio. + final float length = + (float) (isLandscape() ? mRootBounds.width() : mRootBounds.height()); + final int estimatePosition = (int) (length * snapRatio); + // Init divider position by estimated position using current bounds snap algorithm. + mDividePosition = mDividerSnapAlgorithm.calculateNonDismissingSnapTarget( + estimatePosition).position; + updateBounds(mDividePosition); + } + /** Updates recording bounds of divider window and both of the splits. */ private void updateBounds(int position) { mDividerBounds.set(mRootBounds); @@ -342,6 +367,13 @@ public final class SplitLayout { return bounds.width() > bounds.height(); } + /** + * Return if this layout is landscape. + */ + public boolean isLandscape() { + return isLandscape(mRootBounds); + } + /** Apply recorded surface layout to the {@link SurfaceControl.Transaction}. */ public void applySurfaceChanges(SurfaceControl.Transaction t, SurfaceControl leash1, SurfaceControl leash2, SurfaceControl dimLayer1, SurfaceControl dimLayer2) { @@ -349,7 +381,7 @@ public final class SplitLayout { if (dividerLeash != null) { t.setPosition(dividerLeash, mDividerBounds.left, mDividerBounds.top); // Resets layer of divider bar to make sure it is always on top. - t.setLayer(dividerLeash, Integer.MAX_VALUE); + t.setLayer(dividerLeash, SPLIT_DIVIDER_LAYER); } t.setPosition(leash1, mBounds1.left, mBounds1.top) .setWindowCrop(leash1, mBounds1.width(), mBounds1.height()); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java index 58bf22ad29b2..0c12d6c7bca2 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java @@ -34,6 +34,7 @@ import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMA import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; +import android.content.ClipData; import android.content.ClipDescription; import android.content.Context; import android.content.res.Configuration; @@ -48,6 +49,8 @@ import android.view.ViewGroup; import android.view.WindowManager; import android.widget.FrameLayout; +import com.android.internal.logging.InstanceId; +import com.android.internal.logging.UiEventLogger; import com.android.internal.protolog.common.ProtoLog; import com.android.wm.shell.R; import com.android.wm.shell.common.DisplayController; @@ -67,14 +70,17 @@ public class DragAndDropController implements DisplayController.OnDisplaysChange private final Context mContext; private final DisplayController mDisplayController; + private final DragAndDropEventLogger mLogger; private SplitScreenController mSplitScreen; private final SparseArray<PerDisplay> mDisplayDropTargets = new SparseArray<>(); private final SurfaceControl.Transaction mTransaction = new SurfaceControl.Transaction(); - public DragAndDropController(Context context, DisplayController displayController) { + public DragAndDropController(Context context, DisplayController displayController, + UiEventLogger uiEventLogger) { mContext = context; mDisplayController = displayController; + mLogger = new DragAndDropEventLogger(uiEventLogger); } public void initialize(Optional<SplitScreenController> splitscreen) { @@ -175,9 +181,10 @@ public class DragAndDropController implements DisplayController.OnDisplaysChange Slog.w(TAG, "Unexpected drag start during an active drag"); return false; } + InstanceId loggerSessionId = mLogger.logStart(event); pd.activeDragCount++; pd.dragLayout.prepare(mDisplayController.getDisplayLayout(displayId), - event.getClipData()); + event.getClipData(), loggerSessionId); setDropTargetWindowVisibility(pd, View.VISIBLE); break; case ACTION_DRAG_ENTERED: @@ -198,7 +205,9 @@ public class DragAndDropController implements DisplayController.OnDisplaysChange case ACTION_DRAG_ENDED: // TODO(b/169894807): Ensure sure it's not possible to get ENDED without DROP // or EXITED - if (!pd.dragLayout.hasDropped()) { + if (pd.dragLayout.hasDropped()) { + mLogger.logDrop(); + } else { pd.activeDragCount--; pd.dragLayout.hide(event, () -> { if (pd.activeDragCount == 0) { @@ -208,6 +217,7 @@ public class DragAndDropController implements DisplayController.OnDisplaysChange } }); } + mLogger.logEnd(); break; } return true; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropEventLogger.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropEventLogger.java new file mode 100644 index 000000000000..6e4b81563441 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropEventLogger.java @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.draganddrop; + +import static android.content.ClipDescription.MIMETYPE_APPLICATION_ACTIVITY; +import static android.content.ClipDescription.MIMETYPE_APPLICATION_SHORTCUT; +import static android.content.ClipDescription.MIMETYPE_APPLICATION_TASK; + +import android.content.ClipData; +import android.content.ClipDescription; +import android.content.pm.ActivityInfo; +import android.view.DragEvent; + +import com.android.internal.logging.InstanceId; +import com.android.internal.logging.InstanceIdSequence; +import com.android.internal.logging.UiEvent; +import com.android.internal.logging.UiEventLogger; + +/** + * Helper class that to log Drag & Drop UIEvents for a single session, see also go/uievent + */ +public class DragAndDropEventLogger { + + private final UiEventLogger mUiEventLogger; + // Used to generate instance ids for this drag if one is not provided + private final InstanceIdSequence mIdSequence; + + // Tracks the current drag session + private ActivityInfo mActivityInfo; + private InstanceId mInstanceId; + + public DragAndDropEventLogger(UiEventLogger uiEventLogger) { + mUiEventLogger = uiEventLogger; + mIdSequence = new InstanceIdSequence(Integer.MAX_VALUE); + } + + /** + * Logs the start of a drag. + */ + public InstanceId logStart(DragEvent event) { + final ClipDescription description = event.getClipDescription(); + final ClipData data = event.getClipData(); + final ClipData.Item item = data.getItemAt(0); + mInstanceId = item.getIntent().getParcelableExtra( + ClipDescription.EXTRA_LOGGING_INSTANCE_ID); + if (mInstanceId == null) { + mInstanceId = mIdSequence.newInstanceId(); + } + mActivityInfo = item.getActivityInfo(); + mUiEventLogger.logWithInstanceId(getStartEnum(description), + mActivityInfo.applicationInfo.uid, + mActivityInfo.applicationInfo.packageName, mInstanceId); + return mInstanceId; + } + + /** + * Logs a successful drop. + */ + public void logDrop() { + mUiEventLogger.logWithInstanceId(DragAndDropUiEventEnum.GLOBAL_APP_DRAG_DROPPED, + mActivityInfo.applicationInfo.uid, + mActivityInfo.applicationInfo.packageName, mInstanceId); + } + + /** + * Logs the end of a drag. + */ + public void logEnd() { + mUiEventLogger.logWithInstanceId(DragAndDropUiEventEnum.GLOBAL_APP_DRAG_END, + mActivityInfo.applicationInfo.uid, + mActivityInfo.applicationInfo.packageName, mInstanceId); + } + + /** + * Returns the start logging enum for the given drag description. + */ + private DragAndDropUiEventEnum getStartEnum(ClipDescription description) { + if (description.hasMimeType(MIMETYPE_APPLICATION_ACTIVITY)) { + return DragAndDropUiEventEnum.GLOBAL_APP_DRAG_START_ACTIVITY; + } else if (description.hasMimeType(MIMETYPE_APPLICATION_SHORTCUT)) { + return DragAndDropUiEventEnum.GLOBAL_APP_DRAG_START_SHORTCUT; + } else if (description.hasMimeType(MIMETYPE_APPLICATION_TASK)) { + return DragAndDropUiEventEnum.GLOBAL_APP_DRAG_START_TASK; + } + throw new IllegalArgumentException("Not an app drag"); + } + + /** + * Enums for logging Drag & Drop UiEvents + */ + public enum DragAndDropUiEventEnum implements UiEventLogger.UiEventEnum { + @UiEvent(doc = "Starting a global drag and drop of an activity") + GLOBAL_APP_DRAG_START_ACTIVITY(884), + + @UiEvent(doc = "Starting a global drag and drop of a shortcut") + GLOBAL_APP_DRAG_START_SHORTCUT(885), + + @UiEvent(doc = "Starting a global drag and drop of a task") + GLOBAL_APP_DRAG_START_TASK(888), + + @UiEvent(doc = "A global app drag was successfully dropped") + GLOBAL_APP_DRAG_DROPPED(887), + + @UiEvent(doc = "Ending a global app drag and drop") + GLOBAL_APP_DRAG_END(886); + + private final int mId; + + DragAndDropUiEventEnum(int id) { + mId = id; + } + + @Override + public int getId() { + return mId; + } + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java index 5b9d7b80f449..102b90ff5d3d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java @@ -63,6 +63,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; +import com.android.internal.logging.InstanceId; import com.android.wm.shell.common.DisplayLayout; import com.android.wm.shell.common.split.SplitLayout.SplitPosition; import com.android.wm.shell.splitscreen.SplitScreen.StageType; @@ -86,6 +87,7 @@ public class DragAndDropPolicy { private final SplitScreenController mSplitScreen; private final ArrayList<DragAndDropPolicy.Target> mTargets = new ArrayList<>(); + private InstanceId mLoggerSessionId; private DragSession mSession; public DragAndDropPolicy(Context context, SplitScreenController splitScreen) { @@ -104,7 +106,8 @@ public class DragAndDropPolicy { /** * Starts a new drag session with the given initial drag data. */ - void start(DisplayLayout displayLayout, ClipData data) { + void start(DisplayLayout displayLayout, ClipData data, InstanceId loggerSessionId) { + mLoggerSessionId = loggerSessionId; mSession = new DragSession(mContext, mActivityTaskManager, displayLayout, data); // TODO(b/169894807): Also update the session data with task stack changes mSession.update(); @@ -207,6 +210,8 @@ public class DragAndDropPolicy { // Launch in the side stage if we are not in split-screen already. stage = STAGE_TYPE_SIDE; } + // Add some data for logging splitscreen once it is invoked + mSplitScreen.logOnDroppedToSplit(position, mLoggerSessionId); } final ClipDescription description = data.getDescription(); @@ -294,7 +299,12 @@ public class DragAndDropPolicy { @StageType int stage, @SplitPosition int position, @Nullable Bundle options); void enterSplitScreen(int taskId, boolean leftOrTop); - void exitSplitScreen(); + + /** + * Exits splitscreen, with an associated exit trigger from the SplitscreenUIChanged proto + * for logging. + */ + void exitSplitScreen(int exitTrigger); } /** @@ -347,7 +357,7 @@ public class DragAndDropPolicy { } @Override - public void exitSplitScreen() { + public void exitSplitScreen(int exitTrigger) { throw new UnsupportedOperationException("exitSplitScreen not implemented by starter"); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java index b3423362347f..efc9ed0f75b2 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java @@ -38,6 +38,7 @@ import android.view.WindowInsets.Type; import androidx.annotation.NonNull; +import com.android.internal.logging.InstanceId; import com.android.internal.protolog.common.ProtoLog; import com.android.wm.shell.R; import com.android.wm.shell.common.DisplayLayout; @@ -98,8 +99,9 @@ public class DragLayout extends View { return mHasDropped; } - public void prepare(DisplayLayout displayLayout, ClipData initialData) { - mPolicy.start(displayLayout, initialData); + public void prepare(DisplayLayout displayLayout, ClipData initialData, + InstanceId loggerSessionId) { + mPolicy.start(displayLayout, initialData, loggerSessionId); mHasDropped = false; mCurrentTarget = null; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedBackgroundPanelOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedBackgroundPanelOrganizer.java index d0491e95ec01..9e1c61aac868 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedBackgroundPanelOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedBackgroundPanelOrganizer.java @@ -16,6 +16,8 @@ package com.android.wm.shell.onehanded; +import static com.android.wm.shell.onehanded.OneHandedState.STATE_ACTIVE; + import android.animation.ValueAnimator; import android.content.Context; import android.graphics.Color; @@ -46,7 +48,7 @@ import java.util.concurrent.Executor; * the screen has entered one handed mode. */ public class OneHandedBackgroundPanelOrganizer extends DisplayAreaOrganizer - implements OneHandedAnimationCallback { + implements OneHandedAnimationCallback, OneHandedState.OnStateChangedListener { private static final String TAG = "OneHandedBackgroundPanelOrganizer"; private static final int THEME_COLOR_OFFSET = 10; private static final int ALPHA_ANIMATION_DURATION = 200; @@ -56,6 +58,7 @@ public class OneHandedBackgroundPanelOrganizer extends DisplayAreaOrganizer private final OneHandedSurfaceTransactionHelper.SurfaceControlTransactionFactory mTransactionFactory; + private @OneHandedState.State int mCurrentState; private ValueAnimator mAlphaAnimator; private float mTranslationFraction; @@ -180,6 +183,9 @@ public class OneHandedBackgroundPanelOrganizer extends DisplayAreaOrganizer * Called when transition finished. */ public void onStopFinished() { + if (mAlphaAnimator == null) { + return; + } mAlphaAnimator.start(); } @@ -224,6 +230,10 @@ public class OneHandedBackgroundPanelOrganizer extends DisplayAreaOrganizer */ public void onConfigurationChanged() { updateThemeColors(); + + if (mCurrentState != STATE_ACTIVE) { + return; + } showBackgroundPanelLayer(); } @@ -242,6 +252,11 @@ public class OneHandedBackgroundPanelOrganizer extends DisplayAreaOrganizer return Math.max(origColor - THEME_COLOR_OFFSET, 0) / 255.0f; } + @Override + public void onStateChanged(int newState) { + mCurrentState = newState; + } + void dump(@NonNull PrintWriter pw) { final String innerPrefix = " "; pw.println(TAG); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java index b0fe856df7c8..954ca14b4960 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java @@ -307,6 +307,7 @@ public class OneHandedController implements RemoteCallable<OneHandedController> mAccessibilityManager.addAccessibilityStateChangeListener( mAccessibilityStateChangeListener); + mState.addSListeners(mBackgroundPanelOrganizer); mState.addSListeners(mTutorialHandler); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java index 1d238a116059..96867761cc7e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java @@ -613,7 +613,6 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, private void onEndOfSwipePipToHomeTransition() { if (Transitions.ENABLE_SHELL_TRANSITIONS) { - mPipTransitionState.setInSwipePipToHomeTransition(false); mSwipePipToHomeOverlay = null; return; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java index ea587c69c0c0..6fec1fbda7b0 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java @@ -170,9 +170,12 @@ public class PipTransition extends PipTransitionController { if (request.getType() == TRANSIT_PIP) { WindowContainerTransaction wct = new WindowContainerTransaction(); mPipTransitionState.setTransitionState(PipTransitionState.ENTRY_SCHEDULED); - final Rect destinationBounds = mPipBoundsAlgorithm.getEntryDestinationBounds(); - wct.setActivityWindowingMode(request.getTriggerTask().token, WINDOWING_MODE_UNDEFINED); - wct.setBounds(request.getTriggerTask().token, destinationBounds); + if (mOneShotAnimationType == ANIM_TYPE_ALPHA) { + wct.setActivityWindowingMode(request.getTriggerTask().token, + WINDOWING_MODE_UNDEFINED); + final Rect destinationBounds = mPipBoundsAlgorithm.getEntryDestinationBounds(); + wct.setBounds(request.getTriggerTask().token, destinationBounds); + } return wct; } else { return null; @@ -243,6 +246,7 @@ public class PipTransition extends PipTransitionController { onFinishResize(taskInfo, destinationBounds, TRANSITION_DIRECTION_TO_PIP, tx); sendOnPipTransitionFinished(TRANSITION_DIRECTION_TO_PIP); mFinishCallback = null; + mPipTransitionState.setInSwipePipToHomeTransition(false); return true; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java index 92c0099c6557..ac02075a49d8 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java @@ -27,6 +27,7 @@ import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTI import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_LEAVE_PIP; import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN; import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_REMOVE_STACK; +import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_SAME; import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_SNAP_AFTER_RESIZE; import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_TO_PIP; import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_USER_RESIZE; @@ -84,6 +85,7 @@ import com.android.wm.shell.pip.PipSnapAlgorithm; import com.android.wm.shell.pip.PipTaskOrganizer; import com.android.wm.shell.pip.PipTransitionController; import com.android.wm.shell.pip.PipUtils; +import com.android.wm.shell.transition.Transitions; import java.io.PrintWriter; import java.util.Objects; @@ -455,11 +457,18 @@ public class PipController implements PipTransitionController.PipTransitionCallb return; } Runnable updateDisplayLayout = () -> { + final boolean fromRotation = Transitions.ENABLE_SHELL_TRANSITIONS + && mPipBoundsState.getDisplayLayout().rotation() != layout.rotation(); mPipBoundsState.setDisplayLayout(layout); + final WindowContainerTransaction wct = + fromRotation ? new WindowContainerTransaction() : null; updateMovementBounds(null /* toBounds */, - false /* fromRotation */, false /* fromImeAdjustment */, + fromRotation, false /* fromImeAdjustment */, false /* fromShelfAdjustment */, - null /* windowContainerTransaction */); + wct /* windowContainerTransaction */); + if (wct != null) { + mPipTaskOrganizer.applyFinishBoundsResize(wct, TRANSITION_DIRECTION_SAME); + } }; if (mPipTaskOrganizer.isInPip() && saveRestoreSnapFraction) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIController.java index 1fc4d12def1f..ab3cbd655ea1 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIController.java @@ -47,6 +47,8 @@ public class SizeCompatUIController implements DisplayController.OnDisplaysChang /** Callback for size compat UI interaction. */ public interface SizeCompatUICallback { + /** Called when the size compat restart button appears. */ + void onSizeCompatRestartButtonAppeared(int taskId); /** Called when the size compat restart button is clicked. */ void onSizeCompatRestartButtonClicked(int taskId); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUILayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUILayout.java index a5e96d14dde6..7cf95593dbaa 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUILayout.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUILayout.java @@ -28,6 +28,7 @@ import android.content.res.Configuration; import android.graphics.PixelFormat; import android.graphics.Rect; import android.os.Binder; +import android.util.Log; import android.view.SurfaceControl; import android.view.View; import android.view.WindowManager; @@ -45,7 +46,7 @@ import com.android.wm.shell.common.SyncTransactionQueue; class SizeCompatUILayout { private static final String TAG = "SizeCompatUILayout"; - private final SyncTransactionQueue mSyncQueue; + final SyncTransactionQueue mSyncQueue; private final SizeCompatUIController.SizeCompatUICallback mCallback; private Context mContext; private Configuration mTaskConfig; @@ -105,6 +106,8 @@ class SizeCompatUILayout { mShouldShowHint = false; createSizeCompatHint(); } + + mCallback.onSizeCompatRestartButtonAppeared(mTaskId); } /** Creates the restart button hint window. */ @@ -306,6 +309,10 @@ class SizeCompatUILayout { private void updateSurfacePosition(SurfaceControl leash, int positionX, int positionY) { mSyncQueue.runInSync(t -> { + if (!leash.isValid()) { + Log.w(TAG, "The leash has been released."); + return; + } t.setPosition(leash, positionX, positionY); // The size compat UI should be the topmost child of the Task in case there can be more // than one children. diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIWindowManager.java index f634c4586e39..82f69c3e2985 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIWindowManager.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIWindowManager.java @@ -110,7 +110,8 @@ class SizeCompatUIWindowManager extends WindowlessWindowManager { } if (mLeash != null) { - new SurfaceControl.Transaction().remove(mLeash).apply(); + final SurfaceControl leash = mLeash; + mLayout.mSyncQueue.runInSync(t -> t.remove(leash)); mLeash = null; } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl index df1f5e67e4e2..6ec514bd8331 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl @@ -95,6 +95,8 @@ interface ISplitScreen { * Blocking call that notifies and gets additional split-screen targets when entering * recents (for example: the dividerBar). * @param cancel is true if leaving recents back to split (eg. the gesture was cancelled). + * @param appTargets apps that will be re-parented to display area */ - RemoteAnimationTarget[] onGoingToRecentsLegacy(boolean cancel) = 12; + RemoteAnimationTarget[] onGoingToRecentsLegacy(boolean cancel, + in RemoteAnimationTarget[] appTargets) = 12; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java index 7804c777bc1b..67223c3590a5 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java @@ -43,6 +43,7 @@ import android.view.RemoteAnimationAdapter; import android.view.RemoteAnimationTarget; import android.view.SurfaceControl; import android.view.WindowManager; +import android.view.SurfaceSession; import android.window.IRemoteTransition; import android.window.WindowContainerTransaction; @@ -50,6 +51,8 @@ import androidx.annotation.BinderThread; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import com.android.internal.logging.InstanceId; +import com.android.internal.util.FrameworkStatsLog; import com.android.wm.shell.RootTaskDisplayAreaOrganizer; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.DisplayImeController; @@ -60,7 +63,6 @@ import com.android.wm.shell.common.TransactionPool; import com.android.wm.shell.common.annotations.ExternalThread; import com.android.wm.shell.common.split.SplitLayout.SplitPosition; import com.android.wm.shell.draganddrop.DragAndDropPolicy; -import com.android.wm.shell.splitscreen.ISplitScreenListener; import com.android.wm.shell.transition.LegacyTransitions; import com.android.wm.shell.transition.Transitions; @@ -85,6 +87,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, private final DisplayImeController mDisplayImeController; private final Transitions mTransitions; private final TransactionPool mTransactionPool; + private final SplitscreenEventLogger mLogger; private StageCoordinator mStageCoordinator; @@ -101,6 +104,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, mDisplayImeController = displayImeController; mTransitions = transitions; mTransactionPool = transactionPool; + mLogger = new SplitscreenEventLogger(); } public SplitScreen asSplitScreen() { @@ -122,7 +126,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, // TODO: Multi-display mStageCoordinator = new StageCoordinator(mContext, DEFAULT_DISPLAY, mSyncQueue, mRootTDAOrganizer, mTaskOrganizer, mDisplayImeController, mTransitions, - mTransactionPool); + mTransactionPool, mLogger); } } @@ -164,8 +168,8 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, leftOrTop ? SPLIT_POSITION_TOP_OR_LEFT : SPLIT_POSITION_BOTTOM_OR_RIGHT); } - public void exitSplitScreen() { - mStageCoordinator.exitSplitScreen(); + public void exitSplitScreen(int exitReason) { + mStageCoordinator.exitSplitScreen(exitReason); } public void onKeyguardOccludedChanged(boolean occluded) { @@ -281,11 +285,34 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, mSyncQueue.queue(transition, WindowManager.TRANSIT_OPEN, wct); } - RemoteAnimationTarget[] onGoingToRecentsLegacy(boolean cancel) { + RemoteAnimationTarget[] onGoingToRecentsLegacy(boolean cancel, RemoteAnimationTarget[] apps) { if (!isSplitScreenVisible()) return null; + final SurfaceControl.Builder builder = new SurfaceControl.Builder(new SurfaceSession()) + .setContainerLayer() + .setName("RecentsAnimationSplitTasks") + .setHidden(false) + .setCallsite("SplitScreenController#onGoingtoRecentsLegacy"); + mRootTDAOrganizer.attachToDisplayArea(DEFAULT_DISPLAY, builder); + SurfaceControl sc = builder.build(); + SurfaceControl.Transaction transaction = new SurfaceControl.Transaction(); + for (RemoteAnimationTarget appTarget : apps) { + // TODO(b/195958376) set the correct layer/z-order in transaction for the new surface + transaction.reparent(appTarget.leash, sc); + transaction.setPosition(appTarget.leash, appTarget.screenSpaceBounds.left, + appTarget.screenSpaceBounds.top); + } + transaction.apply(); + transaction.close(); return new RemoteAnimationTarget[]{mStageCoordinator.getDividerBarLegacyTarget()}; } + /** + * Sets drag info to be logged when splitscreen is entered. + */ + public void logOnDroppedToSplit(@SplitPosition int position, InstanceId dragSessionId) { + mStageCoordinator.logOnDroppedToSplit(position, dragSessionId); + } + public void dump(@NonNull PrintWriter pw, String prefix) { pw.println(prefix + TAG); if (mStageCoordinator != null) { @@ -478,7 +505,8 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, public void exitSplitScreen() { executeRemoteCallWithTaskPermission(mController, "exitSplitScreen", (controller) -> { - controller.exitSplitScreen(); + controller.exitSplitScreen( + FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__RETURN_HOME); }); } @@ -554,10 +582,11 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, } @Override - public RemoteAnimationTarget[] onGoingToRecentsLegacy(boolean cancel) { + public RemoteAnimationTarget[] onGoingToRecentsLegacy(boolean cancel, + RemoteAnimationTarget[] apps) { final RemoteAnimationTarget[][] out = new RemoteAnimationTarget[][]{null}; executeRemoteCallWithTaskPermission(mController, "onGoingToRecentsLegacy", - (controller) -> out[0] = controller.onGoingToRecentsLegacy(cancel), + (controller) -> out[0] = controller.onGoingToRecentsLegacy(cancel, apps), true /* blocking */); return out[0]; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitscreenEventLogger.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitscreenEventLogger.java new file mode 100644 index 000000000000..319079baaccf --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitscreenEventLogger.java @@ -0,0 +1,324 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.splitscreen; + +import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__ENTER_REASON__OVERVIEW; +import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_TOP_OR_LEFT; +import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_UNDEFINED; + +import com.android.internal.logging.InstanceId; +import com.android.internal.logging.InstanceIdSequence; +import com.android.internal.util.FrameworkStatsLog; +import com.android.wm.shell.common.split.SplitLayout.SplitPosition; + +/** + * Helper class that to log Drag & Drop UIEvents for a single session, see also go/uievent + */ +public class SplitscreenEventLogger { + + // Used to generate instance ids for this drag if one is not provided + private final InstanceIdSequence mIdSequence; + + // The instance id for the current splitscreen session (from start to end) + private InstanceId mLoggerSessionId; + + // Drag info + private @SplitPosition int mDragEnterPosition; + private InstanceId mDragEnterSessionId; + + // For deduping async events + private int mLastMainStagePosition = -1; + private int mLastMainStageUid = -1; + private int mLastSideStagePosition = -1; + private int mLastSideStageUid = -1; + private float mLastSplitRatio = -1f; + + public SplitscreenEventLogger() { + mIdSequence = new InstanceIdSequence(Integer.MAX_VALUE); + } + + /** + * Return whether a splitscreen session has started. + */ + public boolean hasStartedSession() { + return mLoggerSessionId != null; + } + + /** + * May be called before logEnter() to indicate that the session was started from a drag. + */ + public void enterRequestedByDrag(@SplitPosition int position, InstanceId dragSessionId) { + mDragEnterPosition = position; + mDragEnterSessionId = dragSessionId; + } + + /** + * Logs when the user enters splitscreen. + */ + public void logEnter(float splitRatio, + @SplitPosition int mainStagePosition, int mainStageUid, + @SplitPosition int sideStagePosition, int sideStageUid, + boolean isLandscape) { + mLoggerSessionId = mIdSequence.newInstanceId(); + int enterReason = mDragEnterPosition != SPLIT_POSITION_UNDEFINED + ? getDragEnterReasonFromSplitPosition(mDragEnterPosition, isLandscape) + : SPLITSCREEN_UICHANGED__ENTER_REASON__OVERVIEW; + updateMainStageState(getMainStagePositionFromSplitPosition(mainStagePosition, isLandscape), + mainStageUid); + updateSideStageState(getSideStagePositionFromSplitPosition(sideStagePosition, isLandscape), + sideStageUid); + updateSplitRatioState(splitRatio); + FrameworkStatsLog.write(FrameworkStatsLog.SPLITSCREEN_UI_CHANGED, + FrameworkStatsLog.SPLITSCREEN_UICHANGED__ACTION__ENTER, + enterReason, + 0 /* exitReason */, + splitRatio, + mLastMainStagePosition, + mLastMainStageUid, + mLastSideStagePosition, + mLastSideStageUid, + mDragEnterSessionId != null ? mDragEnterSessionId.getId() : 0, + mLoggerSessionId.getId()); + } + + /** + * Logs when the user exits splitscreen. Only one of the main or side stages should be + * specified to indicate which position was focused as a part of exiting (both can be unset). + */ + public void logExit(int exitReason, @SplitPosition int mainStagePosition, int mainStageUid, + @SplitPosition int sideStagePosition, int sideStageUid, boolean isLandscape) { + if (mLoggerSessionId == null) { + // Ignore changes until we've started logging the session + return; + } + if ((mainStagePosition != SPLIT_POSITION_UNDEFINED + && sideStagePosition != SPLIT_POSITION_UNDEFINED) + || (mainStageUid != 0 && sideStageUid != 0)) { + throw new IllegalArgumentException("Only main or side stage should be set"); + } + FrameworkStatsLog.write(FrameworkStatsLog.SPLITSCREEN_UI_CHANGED, + FrameworkStatsLog.SPLITSCREEN_UICHANGED__ACTION__EXIT, + 0 /* enterReason */, + exitReason, + 0f /* splitRatio */, + getMainStagePositionFromSplitPosition(mainStagePosition, isLandscape), + mainStageUid, + getSideStagePositionFromSplitPosition(sideStagePosition, isLandscape), + sideStageUid, + 0 /* dragInstanceId */, + mLoggerSessionId.getId()); + + // Reset states + mLoggerSessionId = null; + mDragEnterPosition = SPLIT_POSITION_UNDEFINED; + mDragEnterSessionId = null; + mLastMainStagePosition = -1; + mLastMainStageUid = -1; + mLastSideStagePosition = -1; + mLastSideStageUid = -1; + } + + /** + * Logs when an app in the main stage changes. + */ + public void logMainStageAppChange(@SplitPosition int mainStagePosition, int mainStageUid, + boolean isLandscape) { + if (mLoggerSessionId == null) { + // Ignore changes until we've started logging the session + return; + } + if (!updateMainStageState(getMainStagePositionFromSplitPosition(mainStagePosition, + isLandscape), mainStageUid)) { + // Ignore if there are no user perceived changes + return; + } + + FrameworkStatsLog.write(FrameworkStatsLog.SPLITSCREEN_UI_CHANGED, + FrameworkStatsLog.SPLITSCREEN_UICHANGED__ACTION__APP_CHANGE, + 0 /* enterReason */, + 0 /* exitReason */, + 0f /* splitRatio */, + mLastMainStagePosition, + mLastMainStageUid, + 0 /* sideStagePosition */, + 0 /* sideStageUid */, + 0 /* dragInstanceId */, + mLoggerSessionId.getId()); + } + + /** + * Logs when an app in the side stage changes. + */ + public void logSideStageAppChange(@SplitPosition int sideStagePosition, int sideStageUid, + boolean isLandscape) { + if (mLoggerSessionId == null) { + // Ignore changes until we've started logging the session + return; + } + if (!updateSideStageState(getSideStagePositionFromSplitPosition(sideStagePosition, + isLandscape), sideStageUid)) { + // Ignore if there are no user perceived changes + return; + } + + FrameworkStatsLog.write(FrameworkStatsLog.SPLITSCREEN_UI_CHANGED, + FrameworkStatsLog.SPLITSCREEN_UICHANGED__ACTION__APP_CHANGE, + 0 /* enterReason */, + 0 /* exitReason */, + 0f /* splitRatio */, + 0 /* mainStagePosition */, + 0 /* mainStageUid */, + mLastSideStagePosition, + mLastSideStageUid, + 0 /* dragInstanceId */, + mLoggerSessionId.getId()); + } + + /** + * Logs when the splitscreen ratio changes. + */ + public void logResize(float splitRatio) { + if (mLoggerSessionId == null) { + // Ignore changes until we've started logging the session + return; + } + if (splitRatio <= 0f || splitRatio >= 1f) { + // Don't bother reporting resizes that end up dismissing the split, that will be logged + // via the exit event + return; + } + if (!updateSplitRatioState(splitRatio)) { + // Ignore if there are no user perceived changes + return; + } + FrameworkStatsLog.write(FrameworkStatsLog.SPLITSCREEN_UI_CHANGED, + FrameworkStatsLog.SPLITSCREEN_UICHANGED__ACTION__RESIZE, + 0 /* enterReason */, + 0 /* exitReason */, + mLastSplitRatio, + 0 /* mainStagePosition */, 0 /* mainStageUid */, + 0 /* sideStagePosition */, 0 /* sideStageUid */, + 0 /* dragInstanceId */, + mLoggerSessionId.getId()); + } + + /** + * Logs when the apps in splitscreen are swapped. + */ + public void logSwap(@SplitPosition int mainStagePosition, int mainStageUid, + @SplitPosition int sideStagePosition, int sideStageUid, boolean isLandscape) { + if (mLoggerSessionId == null) { + // Ignore changes until we've started logging the session + return; + } + + updateMainStageState(getMainStagePositionFromSplitPosition(mainStagePosition, isLandscape), + mainStageUid); + updateSideStageState(getSideStagePositionFromSplitPosition(sideStagePosition, isLandscape), + sideStageUid); + FrameworkStatsLog.write(FrameworkStatsLog.SPLITSCREEN_UI_CHANGED, + FrameworkStatsLog.SPLITSCREEN_UICHANGED__ACTION__SWAP, + 0 /* enterReason */, + 0 /* exitReason */, + 0f /* splitRatio */, + mLastMainStagePosition, + mLastMainStageUid, + mLastSideStagePosition, + mLastSideStageUid, + 0 /* dragInstanceId */, + mLoggerSessionId.getId()); + } + + private boolean updateMainStageState(int mainStagePosition, int mainStageUid) { + boolean changed = (mLastMainStagePosition != mainStagePosition) + || (mLastMainStageUid != mainStageUid); + if (!changed) { + return false; + } + + mLastMainStagePosition = mainStagePosition; + mLastMainStageUid = mainStageUid; + return true; + } + + private boolean updateSideStageState(int sideStagePosition, int sideStageUid) { + boolean changed = (mLastSideStagePosition != sideStagePosition) + || (mLastSideStageUid != sideStageUid); + if (!changed) { + return false; + } + + mLastSideStagePosition = sideStagePosition; + mLastSideStageUid = sideStageUid; + return true; + } + + private boolean updateSplitRatioState(float splitRatio) { + boolean changed = Float.compare(mLastSplitRatio, splitRatio) != 0; + if (!changed) { + return false; + } + + mLastSplitRatio = splitRatio; + return true; + } + + public int getDragEnterReasonFromSplitPosition(@SplitPosition int position, + boolean isLandscape) { + if (isLandscape) { + return position == SPLIT_POSITION_TOP_OR_LEFT + ? FrameworkStatsLog.SPLITSCREEN_UICHANGED__ENTER_REASON__DRAG_LEFT + : FrameworkStatsLog.SPLITSCREEN_UICHANGED__ENTER_REASON__DRAG_RIGHT; + } else { + return position == SPLIT_POSITION_TOP_OR_LEFT + ? FrameworkStatsLog.SPLITSCREEN_UICHANGED__ENTER_REASON__DRAG_TOP + : FrameworkStatsLog.SPLITSCREEN_UICHANGED__ENTER_REASON__DRAG_BOTTOM; + } + } + + private int getMainStagePositionFromSplitPosition(@SplitPosition int position, + boolean isLandscape) { + if (position == SPLIT_POSITION_UNDEFINED) { + return 0; + } + if (isLandscape) { + return position == SPLIT_POSITION_TOP_OR_LEFT + ? FrameworkStatsLog.SPLITSCREEN_UICHANGED__MAIN_STAGE_POSITION__LEFT + : FrameworkStatsLog.SPLITSCREEN_UICHANGED__MAIN_STAGE_POSITION__RIGHT; + } else { + return position == SPLIT_POSITION_TOP_OR_LEFT + ? FrameworkStatsLog.SPLITSCREEN_UICHANGED__MAIN_STAGE_POSITION__TOP + : FrameworkStatsLog.SPLITSCREEN_UICHANGED__MAIN_STAGE_POSITION__BOTTOM; + } + } + + private int getSideStagePositionFromSplitPosition(@SplitPosition int position, + boolean isLandscape) { + if (position == SPLIT_POSITION_UNDEFINED) { + return 0; + } + if (isLandscape) { + return position == SPLIT_POSITION_TOP_OR_LEFT + ? FrameworkStatsLog.SPLITSCREEN_UICHANGED__SIDE_STAGE_POSITION__LEFT + : FrameworkStatsLog.SPLITSCREEN_UICHANGED__SIDE_STAGE_POSITION__RIGHT; + } else { + return position == SPLIT_POSITION_TOP_OR_LEFT + ? FrameworkStatsLog.SPLITSCREEN_UICHANGED__SIDE_STAGE_POSITION__TOP + : FrameworkStatsLog.SPLITSCREEN_UICHANGED__SIDE_STAGE_POSITION__BOTTOM; + } + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java index 363a4b14005a..736fae41f2ed 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java @@ -25,7 +25,14 @@ import static android.view.WindowManager.TRANSIT_OPEN; import static android.view.WindowManager.TRANSIT_TO_BACK; import static android.view.WindowManager.TRANSIT_TO_FRONT; import static android.view.WindowManager.transitTypeToString; - +import static android.view.WindowManagerPolicyConstants.SPLIT_DIVIDER_LAYER; + +import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__APP_DOES_NOT_SUPPORT_MULTIWINDOW; +import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__APP_FINISHED; +import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__DEVICE_FOLDED; +import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__DRAG_DIVIDER; +import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__RETURN_HOME; +import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__SCREEN_LOCKED_SHOW_ON_TOP; import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_BOTTOM_OR_RIGHT; import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_TOP_OR_LEFT; import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_UNDEFINED; @@ -73,6 +80,7 @@ import android.window.WindowContainerTransaction; import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.logging.InstanceId; import com.android.internal.protolog.common.ProtoLog; import com.android.wm.shell.RootTaskDisplayAreaOrganizer; import com.android.wm.shell.ShellTaskOrganizer; @@ -129,6 +137,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, private final List<SplitScreen.SplitScreenListener> mListeners = new ArrayList<>(); private final DisplayImeController mDisplayImeController; private final SplitScreenTransitions mSplitTransitions; + private final SplitscreenEventLogger mLogger; private boolean mExitSplitScreenOnHide; private boolean mKeyguardOccluded; @@ -155,12 +164,13 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, StageCoordinator(Context context, int displayId, SyncTransactionQueue syncQueue, RootTaskDisplayAreaOrganizer rootTDAOrganizer, ShellTaskOrganizer taskOrganizer, DisplayImeController displayImeController, Transitions transitions, - TransactionPool transactionPool) { + TransactionPool transactionPool, SplitscreenEventLogger logger) { mContext = context; mDisplayId = displayId; mSyncQueue = syncQueue; mRootTDAOrganizer = rootTDAOrganizer; mTaskOrganizer = taskOrganizer; + mLogger = logger; mMainStage = new MainStage( mTaskOrganizer, mDisplayId, @@ -189,7 +199,8 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, StageCoordinator(Context context, int displayId, SyncTransactionQueue syncQueue, RootTaskDisplayAreaOrganizer rootTDAOrganizer, ShellTaskOrganizer taskOrganizer, MainStage mainStage, SideStage sideStage, DisplayImeController displayImeController, - SplitLayout splitLayout, Transitions transitions, TransactionPool transactionPool) { + SplitLayout splitLayout, Transitions transitions, TransactionPool transactionPool, + SplitscreenEventLogger logger) { mContext = context; mDisplayId = displayId; mSyncQueue = syncQueue; @@ -202,6 +213,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, mSplitLayout = splitLayout; mSplitTransitions = new SplitScreenTransitions(transactionPool, transitions, mOnTransitionAnimationComplete); + mLogger = logger; transitions.addHandler(this); } @@ -449,19 +461,20 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, void onKeyguardVisibilityChanged(boolean showing) { if (!showing && mMainStage.isActive() && mTopStageAfterFoldDismiss != STAGE_TYPE_UNDEFINED) { - exitSplitScreen(mTopStageAfterFoldDismiss == STAGE_TYPE_MAIN ? mMainStage : mSideStage); + exitSplitScreen(mTopStageAfterFoldDismiss == STAGE_TYPE_MAIN ? mMainStage : mSideStage, + SPLITSCREEN_UICHANGED__EXIT_REASON__DEVICE_FOLDED); } } - void exitSplitScreen() { - exitSplitScreen(null /* childrenToTop */); + void exitSplitScreen(int exitReason) { + exitSplitScreen(null /* childrenToTop */, exitReason); } void exitSplitScreenOnHide(boolean exitSplitScreenOnHide) { mExitSplitScreenOnHide = exitSplitScreenOnHide; } - private void exitSplitScreen(StageTaskListener childrenToTop) { + private void exitSplitScreen(StageTaskListener childrenToTop, int exitReason) { final WindowContainerTransaction wct = new WindowContainerTransaction(); mSideStage.removeAllTasks(wct, childrenToTop == mSideStage); mMainStage.deactivate(wct, childrenToTop == mMainStage); @@ -469,6 +482,11 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, // Reset divider position. mSplitLayout.resetDividerPosition(); mTopStageAfterFoldDismiss = STAGE_TYPE_UNDEFINED; + if (childrenToTop != null) { + logExitToStage(exitReason, childrenToTop == mMainStage); + } else { + logExit(exitReason); + } } /** @@ -521,9 +539,8 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, } } - private void onStageChildTaskStatusChanged( - StageListenerImpl stageListener, int taskId, boolean present, boolean visible) { - + private void onStageChildTaskStatusChanged(StageListenerImpl stageListener, int taskId, + boolean present, boolean visible) { int stage; if (present) { stage = stageListener == mSideStageListener ? STAGE_TYPE_SIDE : STAGE_TYPE_MAIN; @@ -531,6 +548,13 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, // No longer on any stage stage = STAGE_TYPE_UNDEFINED; } + if (stage == STAGE_TYPE_MAIN) { + mLogger.logMainStageAppChange(getMainStagePosition(), mMainStage.getTopChildTaskUid(), + mSplitLayout.isLandscape()); + } else { + mLogger.logSideStageAppChange(getSideStagePosition(), mSideStage.getTopChildTaskUid(), + mSplitLayout.isLandscape()); + } for (int i = mListeners.size() - 1; i >= 0; --i) { mListeners.get(i).onTaskStageChanged(taskId, stage, visible); @@ -597,7 +621,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, // Don't dismiss staged split when both stages are not visible due to sleeping display, // like the cases keyguard showing or screen off. || (!mMainStage.mRootTaskInfo.isSleeping && !mSideStage.mRootTaskInfo.isSleeping)) { - exitSplitScreen(); + exitSplitScreen(SPLITSCREEN_UICHANGED__EXIT_REASON__RETURN_HOME); } } else if (mKeyguardOccluded) { // At least one of the stages is visible while keyguard occluded. Dismiss split because @@ -605,7 +629,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, // task contains show-when-locked activity remains on top after split dismissed. final StageTaskListener toTop = mainStageVisible ? mMainStage : (sideStageVisible ? mSideStage : null); - exitSplitScreen(toTop); + exitSplitScreen(toTop, SPLITSCREEN_UICHANGED__EXIT_REASON__SCREEN_LOCKED_SHOW_ON_TOP); } // When both stage's visibility changed to visible, main stage might receives visibility @@ -672,7 +696,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, if (mDividerVisible) { t.show(dividerLeash) - .setLayer(dividerLeash, Integer.MAX_VALUE) + .setLayer(dividerLeash, SPLIT_DIVIDER_LAYER) .setPosition(dividerLeash, mSplitLayout.getDividerBounds().left, mSplitLayout.getDividerBounds().top); @@ -682,17 +706,16 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, } - private void onStageHasChildrenChanged(StageListenerImpl stageListener) { final boolean hasChildren = stageListener.mHasChildren; final boolean isSideStage = stageListener == mSideStageListener; if (!hasChildren) { if (isSideStage && mMainStageListener.mVisible) { // Exit to main stage if side stage no longer has children. - exitSplitScreen(mMainStage); + exitSplitScreen(mMainStage, SPLITSCREEN_UICHANGED__EXIT_REASON__APP_FINISHED); } else if (!isSideStage && mSideStageListener.mVisible) { // Exit to side stage if main stage no longer has children. - exitSplitScreen(mSideStage); + exitSplitScreen(mSideStage, SPLITSCREEN_UICHANGED__EXIT_REASON__APP_FINISHED); } } else if (isSideStage) { final WindowContainerTransaction wct = new WindowContainerTransaction(); @@ -701,6 +724,13 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, mSideStage.setBounds(getSideStageBounds(), wct); mTaskOrganizer.applyTransaction(wct); } + if (!mLogger.hasStartedSession() && mMainStageListener.mHasChildren + && mSideStageListener.mHasChildren) { + mLogger.logEnter(mSplitLayout.getDividerPositionAsFraction(), + getMainStagePosition(), mMainStage.getTopChildTaskUid(), + getSideStagePosition(), mSideStage.getTopChildTaskUid(), + mSplitLayout.isLandscape()); + } } @VisibleForTesting @@ -719,13 +749,17 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, onSnappedToDismissTransition(mainStageToTop); return; } - exitSplitScreen(mainStageToTop ? mMainStage : mSideStage); + exitSplitScreen(mainStageToTop ? mMainStage : mSideStage, + SPLITSCREEN_UICHANGED__EXIT_REASON__DRAG_DIVIDER); } @Override public void onDoubleTappedDivider() { setSideStagePosition(mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? SPLIT_POSITION_BOTTOM_OR_RIGHT : SPLIT_POSITION_TOP_OR_LEFT, null /* wct */); + mLogger.logSwap(getMainStagePosition(), mMainStage.getTopChildTaskUid(), + getSideStagePosition(), mSideStage.getTopChildTaskUid(), + mSplitLayout.isLandscape()); } @Override @@ -739,6 +773,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, updateWindowBounds(layout, wct); mSyncQueue.queue(wct); mSyncQueue.runInSync(t -> updateSurfaceBounds(layout, t)); + mLogger.logResize(mSplitLayout.getDividerPositionAsFraction()); } /** @@ -1098,7 +1133,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, // Be default, make it visible. The remote animator can adjust alpha if it plans to animate. if (show) { t.setAlpha(leash, 1.f); - t.setLayer(leash, Integer.MAX_VALUE); + t.setLayer(leash, SPLIT_DIVIDER_LAYER); t.setPosition(leash, bounds.left, bounds.top); t.show(leash); } @@ -1138,6 +1173,36 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, mMainStageListener.mHasChildren = mSideStageListener.mHasChildren = visible; } + /** + * Sets drag info to be logged when splitscreen is next entered. + */ + public void logOnDroppedToSplit(@SplitPosition int position, InstanceId dragSessionId) { + mLogger.enterRequestedByDrag(position, dragSessionId); + } + + /** + * Logs the exit of splitscreen. + */ + private void logExit(int exitReason) { + mLogger.logExit(exitReason, + SPLIT_POSITION_UNDEFINED, 0 /* mainStageUid */, + SPLIT_POSITION_UNDEFINED, 0 /* sideStageUid */, + mSplitLayout.isLandscape()); + } + + /** + * Logs the exit of splitscreen to a specific stage. This must be called before the exit is + * executed. + */ + private void logExitToStage(int exitReason, boolean toMainStage) { + mLogger.logExit(exitReason, + toMainStage ? getMainStagePosition() : SPLIT_POSITION_UNDEFINED, + toMainStage ? mMainStage.getTopChildTaskUid() : 0 /* mainStageUid */, + !toMainStage ? getSideStagePosition() : SPLIT_POSITION_UNDEFINED, + !toMainStage ? mSideStage.getTopChildTaskUid() : 0 /* sideStageUid */, + mSplitLayout.isLandscape()); + } + class StageListenerImpl implements StageTaskListener.StageListenerCallbacks { boolean mHasRootTask = false; boolean mVisible = false; @@ -1177,7 +1242,8 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, @Override public void onNoLongerSupportMultiWindow() { if (mMainStage.isActive()) { - StageCoordinator.this.exitSplitScreen(); + StageCoordinator.this.exitSplitScreen( + SPLITSCREEN_UICHANGED__EXIT_REASON__APP_DOES_NOT_SUPPORT_MULTIWINDOW); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java index c47353a863b2..3512a0c3727b 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java @@ -97,6 +97,20 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener { return mChildrenTaskInfo.contains(taskId); } + /** + * Returns the top activity uid for the top child task. + */ + int getTopChildTaskUid() { + for (int i = mChildrenTaskInfo.size() - 1; i >= 0; --i) { + final ActivityManager.RunningTaskInfo info = mChildrenTaskInfo.valueAt(i); + if (info.topActivityInfo == null) { + continue; + } + return info.topActivityInfo.applicationInfo.uid; + } + return 0; + } + /** @return {@code true} if this listener contains the currently focused task. */ boolean isFocused() { if (mRootTaskInfo.isFocused) return true; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java index b584038bf0e5..4ba6acaba025 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java @@ -29,7 +29,6 @@ import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLES import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_UNSPECIFIED; import static android.view.WindowManager.TRANSIT_CHANGE; import static android.view.WindowManager.TRANSIT_CLOSE; -import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY; import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE; import static android.view.WindowManager.TRANSIT_OPEN; import static android.view.WindowManager.TRANSIT_RELAUNCH; @@ -249,10 +248,9 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { @NonNull Transitions.TransitionFinishCallback finishCallback) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "start default transition animation, info = %s", info); - - // Fallback for screen wake. This just immediately finishes since there is no - // animation for screen-wake. - if (info.getType() == WindowManager.TRANSIT_WAKE) { + // If keyguard goes away, we should loadKeyguardExitAnimation. Otherwise this just + // immediately finishes since there is no animation for screen-wake. + if (info.getType() == WindowManager.TRANSIT_WAKE && !info.isKeyguardGoingAway()) { startTransaction.apply(); finishCallback.onTransitionFinished(null /* wct */, null /* wctCB */); return true; @@ -354,7 +352,7 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { final int overrideType = options != null ? options.getType() : ANIM_NONE; final boolean canCustomContainer = isTask ? !sDisableCustomTaskAnimationProperty : true; - if (type == TRANSIT_KEYGUARD_GOING_AWAY) { + if (info.isKeyguardGoingAway()) { a = mTransitionAnimation.loadKeyguardExitAnimation(flags, (changeFlags & FLAG_SHOW_WALLPAPER) != 0); } else if (type == TRANSIT_KEYGUARD_UNOCCLUDE) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java index a13b03d91cdf..ada2ed27c114 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java @@ -19,6 +19,7 @@ package com.android.wm.shell.transition; import static android.hardware.HardwareBuffer.RGBA_8888; import static android.hardware.HardwareBuffer.USAGE_PROTECTED_CONTENT; import static android.util.RotationUtils.deltaRotation; +import static android.view.WindowManagerPolicyConstants.SCREEN_FREEZE_LAYER_BASE; import static com.android.wm.shell.transition.DefaultTransitionHandler.startSurfaceAnimation; import static com.android.wm.shell.transition.Transitions.TAG; @@ -81,19 +82,6 @@ import java.util.Arrays; * </ul> */ class ScreenRotationAnimation { - - /** How much to multiply the policy's type layer, to reserve room - * for multiple windows of the same type and Z-ordering adjustment - * with TYPE_LAYER_OFFSET. */ - static final int TYPE_LAYER_MULTIPLIER = 10000; - static final int WINDOW_FREEZE_LAYER = TYPE_LAYER_MULTIPLIER * 200; - - /* - * Layers for screen rotation animation. We put these layers above - * WINDOW_FREEZE_LAYER so that screen freeze will cover all windows. - */ - private static final int SCREEN_FREEZE_LAYER_BASE = WINDOW_FREEZE_LAYER + TYPE_LAYER_MULTIPLIER; - static final int MAX_ANIMATION_DURATION = 10 * 1000; private final Context mContext; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java index 09dfabf9bfdf..8d21ce25bcd0 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java @@ -19,6 +19,7 @@ package com.android.wm.shell.transition; 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_KEYGUARD_GOING_AWAY; import static android.view.WindowManager.TRANSIT_OPEN; import static android.view.WindowManager.TRANSIT_TO_BACK; import static android.view.WindowManager.TRANSIT_TO_FRONT; @@ -230,7 +231,7 @@ public class Transitions implements RemoteCallable<Transitions> { public static boolean isOpeningType(@WindowManager.TransitionType int type) { return type == TRANSIT_OPEN || type == TRANSIT_TO_FRONT - || type == WindowManager.TRANSIT_KEYGUARD_GOING_AWAY; + || type == TRANSIT_KEYGUARD_GOING_AWAY; } /** @return true if the transition was triggered by closing something vs opening something */ diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SplitScreenHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SplitScreenHelper.kt index 901b7a393291..2d996ca1d6f7 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SplitScreenHelper.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SplitScreenHelper.kt @@ -18,6 +18,7 @@ package com.android.wm.shell.flicker.helpers import android.app.Instrumentation import android.content.ComponentName +import android.content.res.Resources import com.android.wm.shell.flicker.testapp.Components class SplitScreenHelper( @@ -30,6 +31,11 @@ class SplitScreenHelper( const val TEST_REPETITIONS = 1 const val TIMEOUT_MS = 3_000L + // TODO: remove all legacy split screen flicker tests when legacy split screen is fully + // deprecated. + fun isUsingLegacySplit(): Boolean = + Resources.getSystem().getBoolean(com.android.internal.R.bool.config_useLegacySplit) + fun getPrimary(instrumentation: Instrumentation): SplitScreenHelper = SplitScreenHelper(instrumentation, Components.SplitScreenActivity.LABEL, diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenTransition.kt index 666d259f2b40..311769313a7a 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenTransition.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenTransition.kt @@ -39,6 +39,7 @@ import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.setDevEn import com.android.wm.shell.flicker.helpers.SplitScreenHelper import org.junit.After import org.junit.Assume.assumeFalse +import org.junit.Assume.assumeTrue import org.junit.Before import org.junit.Test @@ -56,6 +57,8 @@ abstract class LegacySplitScreenTransition(protected val testSpec: FlickerTestPa @Before open fun setup() { + // Only run legacy split tests when the system is using legacy split screen. + assumeTrue(SplitScreenHelper.isUsingLegacySplit()) // Legacy split is having some issue with Shell transition, and will be deprecated soon. assumeFalse(isShellTransitionsEnabled()) prevDevEnableNonResizableMultiWindow = getDevEnableNonResizableMultiWindow(context) diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt index e0ec75778342..45cb152e5b0d 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt @@ -32,6 +32,8 @@ import com.android.server.wm.flicker.helpers.setRotation import com.android.server.wm.flicker.navBarLayerRotatesAndScales import com.android.server.wm.flicker.startRotation import com.android.server.wm.flicker.statusBarLayerRotatesScales +import com.android.server.wm.traces.common.Region +import com.android.server.wm.traces.parser.minus import com.android.wm.shell.flicker.helpers.FixedAppHelper import org.junit.FixMethodOrder import org.junit.Test @@ -94,7 +96,10 @@ class PipRotationTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) @Test fun appLayerRotates_StartingBounds() { testSpec.assertLayersStart { - visibleRegion(fixedApp.component).coversExactly(startingBounds) + val pipRegion = visibleRegion(pipApp.component).region + val expectedWithoutPip = Region(startingBounds.bounds.left, startingBounds.bounds.top, + startingBounds.bounds.right, startingBounds.bounds.bottom).minus(pipRegion) + visibleRegion(fixedApp.component).coversExactly(expectedWithoutPip) visibleRegion(pipApp.component).coversAtMost(startingBounds) } } @@ -103,7 +108,10 @@ class PipRotationTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) @Test fun appLayerRotates_EndingBounds() { testSpec.assertLayersEnd { - visibleRegion(fixedApp.component).coversExactly(endingBounds) + val pipRegion = visibleRegion(pipApp.component).region + val expectedWithoutPip = Region(endingBounds.bounds.left, endingBounds.bounds.top, + endingBounds.bounds.right, endingBounds.bounds.bottom).minus(pipRegion) + visibleRegion(fixedApp.component).coversExactly(expectedWithoutPip) visibleRegion(pipApp.component).coversAtMost(endingBounds) } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java index 3e3195fe8dc5..b0312e6d6f3c 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java @@ -869,6 +869,35 @@ public class BubbleDataTest extends ShellTestCase { assertNotNull(mBubbleData.getOverflowBubbleWithKey(mBubbleA2.getKey())); } + /** + * Verifies that after the stack is collapsed with the overflow selected, it will select + * the top bubble upon next expansion. + */ + @Test + public void test_collapseWithOverflowSelected_nextExpansion() { + sendUpdatedEntryAtTime(mEntryA1, 1000); + sendUpdatedEntryAtTime(mEntryA2, 2000); + mBubbleData.setExpanded(true); + + mBubbleData.setListener(mListener); + + // Select the overflow + mBubbleData.setShowingOverflow(true); + mBubbleData.setSelectedBubble(mBubbleData.getOverflow()); + verifyUpdateReceived(); + assertSelectionChangedTo(mBubbleData.getOverflow()); + + // Collapse + mBubbleData.setExpanded(false); + verifyUpdateReceived(); + assertSelectionNotChanged(); + + // Expand (here we should select the new bubble) + mBubbleData.setExpanded(true); + verifyUpdateReceived(); + assertSelectionChangedTo(mBubbleA2); + } + private void verifyUpdateReceived() { verify(mListener).applyUpdate(mUpdateCaptor.capture()); reset(mListener); @@ -902,7 +931,7 @@ public class BubbleDataTest extends ShellTestCase { assertWithMessage("selectionChanged").that(update.selectionChanged).isFalse(); } - private void assertSelectionChangedTo(Bubble bubble) { + private void assertSelectionChangedTo(BubbleViewProvider bubble) { BubbleData.Update update = mUpdateCaptor.getValue(); assertWithMessage("selectionChanged").that(update.selectionChanged).isTrue(); assertWithMessage("selectedBubble").that(update.selectedBubble).isEqualTo(bubble); @@ -925,7 +954,6 @@ public class BubbleDataTest extends ShellTestCase { assertThat(update.overflowBubbles).isEqualTo(bubbles); } - private BubbleEntry createBubbleEntry(int userId, String notifKey, String packageName, NotificationListenerService.Ranking ranking) { return createBubbleEntry(userId, notifKey, packageName, ranking, 1000); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationControllerTest.java index 1eba3c266358..9732a8890e0e 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationControllerTest.java @@ -19,6 +19,7 @@ package com.android.wm.shell.bubbles.animation; import static org.junit.Assert.assertEquals; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import android.annotation.SuppressLint; import android.content.res.Configuration; @@ -41,7 +42,6 @@ import org.junit.Before; import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; -import org.mockito.Spy; @SmallTest @RunWith(AndroidTestingRunner.class) @@ -49,26 +49,26 @@ public class ExpandedAnimationControllerTest extends PhysicsAnimationLayoutTestC private int mDisplayWidth = 500; private int mDisplayHeight = 1000; - private int mExpandedViewPadding = 10; private Runnable mOnBubbleAnimatedOutAction = mock(Runnable.class); - @Spy ExpandedAnimationController mExpandedController; private int mStackOffset; private PointF mExpansionPoint; + private BubblePositioner mPositioner; @SuppressLint("VisibleForTests") @Before public void setUp() throws Exception { super.setUp(); - BubblePositioner positioner = new BubblePositioner(getContext(), mock(WindowManager.class)); - positioner.updateInternal(Configuration.ORIENTATION_PORTRAIT, + mPositioner = new BubblePositioner(getContext(), mock(WindowManager.class)); + mPositioner.updateInternal(Configuration.ORIENTATION_PORTRAIT, Insets.of(0, 0, 0, 0), new Rect(0, 0, mDisplayWidth, mDisplayHeight)); - mExpandedController = new ExpandedAnimationController(positioner, mExpandedViewPadding, + mExpandedController = new ExpandedAnimationController(mPositioner, mOnBubbleAnimatedOutAction); + spyOn(mExpandedController); addOneMoreThanBubbleLimitBubbles(); mLayout.setActiveController(mExpandedController); @@ -141,13 +141,16 @@ public class ExpandedAnimationControllerTest extends PhysicsAnimationLayoutTestC /** Check that children are in the correct positions for being expanded. */ private void testBubblesInCorrectExpandedPositions() { + boolean onLeft = mPositioner.isStackOnLeft(mExpansionPoint); // Check all the visible bubbles to see if they're in the right place. for (int i = 0; i < mLayout.getChildCount(); i++) { - float expectedPosition = mExpandedController.getBubbleXOrYForOrientation(i); - assertEquals(expectedPosition, + PointF expectedPosition = mPositioner.getExpandedBubbleXY(i, + mLayout.getChildCount(), + onLeft); + assertEquals(expectedPosition.x, mLayout.getChildAt(i).getTranslationX(), 2f); - assertEquals(expectedPosition, + assertEquals(expectedPosition.y, mLayout.getChildAt(i).getTranslationY(), 2f); } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java index 1a70f7611c6f..734b97b69c87 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java @@ -65,6 +65,7 @@ import android.view.DisplayInfo; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; +import com.android.internal.logging.InstanceId; import com.android.wm.shell.common.DisplayLayout; import com.android.wm.shell.draganddrop.DragAndDropPolicy.Target; import com.android.wm.shell.splitscreen.SplitScreenController; @@ -96,6 +97,9 @@ public class DragAndDropPolicyTest { @Mock private SplitScreenController mSplitScreenStarter; + @Mock + private InstanceId mLoggerSessionId; + private DisplayLayout mLandscapeDisplayLayout; private DisplayLayout mPortraitDisplayLayout; private Insets mInsets; @@ -201,7 +205,7 @@ public class DragAndDropPolicyTest { @Test public void testDragAppOverFullscreenHome_expectOnlyFullscreenTarget() { setRunningTask(mHomeTask); - mPolicy.start(mLandscapeDisplayLayout, mActivityClipData); + mPolicy.start(mLandscapeDisplayLayout, mActivityClipData, mLoggerSessionId); ArrayList<Target> targets = assertExactTargetTypes( mPolicy.getTargets(mInsets), TYPE_FULLSCREEN); @@ -213,7 +217,7 @@ public class DragAndDropPolicyTest { @Test public void testDragAppOverFullscreenApp_expectSplitScreenTargets() { setRunningTask(mFullscreenAppTask); - mPolicy.start(mLandscapeDisplayLayout, mActivityClipData); + mPolicy.start(mLandscapeDisplayLayout, mActivityClipData, mLoggerSessionId); ArrayList<Target> targets = assertExactTargetTypes( mPolicy.getTargets(mInsets), TYPE_SPLIT_LEFT, TYPE_SPLIT_RIGHT); @@ -230,7 +234,7 @@ public class DragAndDropPolicyTest { @Test public void testDragAppOverFullscreenAppPhone_expectVerticalSplitScreenTargets() { setRunningTask(mFullscreenAppTask); - mPolicy.start(mPortraitDisplayLayout, mActivityClipData); + mPolicy.start(mPortraitDisplayLayout, mActivityClipData, mLoggerSessionId); ArrayList<Target> targets = assertExactTargetTypes( mPolicy.getTargets(mInsets), TYPE_SPLIT_TOP, TYPE_SPLIT_BOTTOM); @@ -248,7 +252,7 @@ public class DragAndDropPolicyTest { public void testDragAppOverSplitApp_expectSplitTargets_DropLeft() { setInSplitScreen(true); setRunningTask(mSplitPrimaryAppTask); - mPolicy.start(mLandscapeDisplayLayout, mActivityClipData); + mPolicy.start(mLandscapeDisplayLayout, mActivityClipData, mLoggerSessionId); ArrayList<Target> targets = assertExactTargetTypes( mPolicy.getTargets(mInsets), TYPE_SPLIT_LEFT, TYPE_SPLIT_RIGHT); @@ -261,7 +265,7 @@ public class DragAndDropPolicyTest { public void testDragAppOverSplitApp_expectSplitTargets_DropRight() { setInSplitScreen(true); setRunningTask(mSplitPrimaryAppTask); - mPolicy.start(mLandscapeDisplayLayout, mActivityClipData); + mPolicy.start(mLandscapeDisplayLayout, mActivityClipData, mLoggerSessionId); ArrayList<Target> targets = assertExactTargetTypes( mPolicy.getTargets(mInsets), TYPE_SPLIT_LEFT, TYPE_SPLIT_RIGHT); @@ -274,7 +278,7 @@ public class DragAndDropPolicyTest { public void testDragAppOverSplitAppPhone_expectVerticalSplitTargets_DropTop() { setInSplitScreen(true); setRunningTask(mSplitPrimaryAppTask); - mPolicy.start(mPortraitDisplayLayout, mActivityClipData); + mPolicy.start(mPortraitDisplayLayout, mActivityClipData, mLoggerSessionId); ArrayList<Target> targets = assertExactTargetTypes( mPolicy.getTargets(mInsets), TYPE_SPLIT_TOP, TYPE_SPLIT_BOTTOM); @@ -287,7 +291,7 @@ public class DragAndDropPolicyTest { public void testDragAppOverSplitAppPhone_expectVerticalSplitTargets_DropBottom() { setInSplitScreen(true); setRunningTask(mSplitPrimaryAppTask); - mPolicy.start(mPortraitDisplayLayout, mActivityClipData); + mPolicy.start(mPortraitDisplayLayout, mActivityClipData, mLoggerSessionId); ArrayList<Target> targets = assertExactTargetTypes( mPolicy.getTargets(mInsets), TYPE_SPLIT_TOP, TYPE_SPLIT_BOTTOM); @@ -299,7 +303,7 @@ public class DragAndDropPolicyTest { @Test public void testTargetHitRects() { setRunningTask(mFullscreenAppTask); - mPolicy.start(mLandscapeDisplayLayout, mActivityClipData); + mPolicy.start(mLandscapeDisplayLayout, mActivityClipData, mLoggerSessionId); ArrayList<Target> targets = mPolicy.getTargets(mInsets); for (Target t : targets) { assertTrue(mPolicy.getTargetAtLocation(t.hitRegion.left, t.hitRegion.top) == t); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedBackgroundPanelOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedBackgroundPanelOrganizerTest.java index 99c610765c04..7b9553c5ef9b 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedBackgroundPanelOrganizerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedBackgroundPanelOrganizerTest.java @@ -19,6 +19,9 @@ package com.android.wm.shell.onehanded; import static android.view.Display.DEFAULT_DISPLAY; import static android.window.DisplayAreaOrganizer.FEATURE_ONE_HANDED_BACKGROUND_PANEL; +import static com.android.wm.shell.onehanded.OneHandedState.STATE_ACTIVE; +import static com.android.wm.shell.onehanded.OneHandedState.STATE_NONE; + import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.anyInt; @@ -109,4 +112,20 @@ public class OneHandedBackgroundPanelOrganizerTest extends OneHandedTestCase { assertThat(mSpiedBackgroundPanelOrganizer.mBackgroundSurface).isNull(); } + + @Test + public void testStateNone_onConfigurationChanged() { + mSpiedBackgroundPanelOrganizer.onStateChanged(STATE_NONE); + mSpiedBackgroundPanelOrganizer.onConfigurationChanged(); + + verify(mSpiedBackgroundPanelOrganizer, never()).showBackgroundPanelLayer(); + } + + @Test + public void testStateActivate_onConfigurationChanged() { + mSpiedBackgroundPanelOrganizer.onStateChanged(STATE_ACTIVE); + mSpiedBackgroundPanelOrganizer.onConfigurationChanged(); + + verify(mSpiedBackgroundPanelOrganizer).showBackgroundPanelLayer(); + } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java index ab6f76996398..b0a39d67d00c 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java @@ -65,9 +65,10 @@ public class SplitTestUtils { TestStageCoordinator(Context context, int displayId, SyncTransactionQueue syncQueue, RootTaskDisplayAreaOrganizer rootTDAOrganizer, ShellTaskOrganizer taskOrganizer, MainStage mainStage, SideStage sideStage, DisplayImeController imeController, - SplitLayout splitLayout, Transitions transitions, TransactionPool transactionPool) { + SplitLayout splitLayout, Transitions transitions, TransactionPool transactionPool, + SplitscreenEventLogger logger) { super(context, displayId, syncQueue, rootTDAOrganizer, taskOrganizer, mainStage, - sideStage, imeController, splitLayout, transitions, transactionPool); + sideStage, imeController, splitLayout, transitions, transactionPool, logger); // Prepare default TaskDisplayArea for testing. mDisplayAreaInfo = new DisplayAreaInfo( diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java index b6da8681d850..cb759dc454af 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java @@ -82,6 +82,7 @@ public class SplitTransitionTests extends ShellTestCase { @Mock private TransactionPool mTransactionPool; @Mock private Transitions mTransitions; @Mock private SurfaceSession mSurfaceSession; + @Mock private SplitscreenEventLogger mLogger; private SplitLayout mSplitLayout; private MainStage mMainStage; private SideStage mSideStage; @@ -107,7 +108,8 @@ public class SplitTransitionTests extends ShellTestCase { mSideStage.onTaskAppeared(new TestRunningTaskInfoBuilder().build(), createMockSurface()); mStageCoordinator = new SplitTestUtils.TestStageCoordinator(mContext, DEFAULT_DISPLAY, mSyncQueue, mRootTDAOrganizer, mTaskOrganizer, mMainStage, mSideStage, - mDisplayImeController, mSplitLayout, mTransitions, mTransactionPool); + mDisplayImeController, mSplitLayout, mTransitions, mTransactionPool, + mLogger); mSplitScreenTransitions = mStageCoordinator.getSplitTransitions(); doAnswer((Answer<IBinder>) invocation -> mock(IBinder.class)) .when(mTransitions).startTransition(anyInt(), any(), any()); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java index 06b08686bf4c..a4b76fb943e6 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java @@ -59,6 +59,7 @@ public class StageCoordinatorTests extends ShellTestCase { @Mock private DisplayImeController mDisplayImeController; @Mock private Transitions mTransitions; @Mock private TransactionPool mTransactionPool; + @Mock private SplitscreenEventLogger mLogger; private StageCoordinator mStageCoordinator; @Before @@ -66,7 +67,8 @@ public class StageCoordinatorTests extends ShellTestCase { MockitoAnnotations.initMocks(this); mStageCoordinator = new SplitTestUtils.TestStageCoordinator(mContext, DEFAULT_DISPLAY, mSyncQueue, mRootTDAOrganizer, mTaskOrganizer, mMainStage, mSideStage, - mDisplayImeController, null /* splitLayout */, mTransitions, mTransactionPool); + mDisplayImeController, null /* splitLayout */, mTransitions, mTransactionPool, + mLogger); } @Test diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java index 376245609c19..54eacee8a9c3 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java @@ -27,6 +27,7 @@ import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_UNSPECI 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_FLAG_KEYGUARD_GOING_AWAY; import static android.view.WindowManager.TRANSIT_OPEN; import static android.view.WindowManager.TRANSIT_TO_BACK; import static android.view.WindowManager.TRANSIT_TO_FRONT; @@ -89,6 +90,9 @@ import java.util.ArrayList; /** * Tests for the shell transitions. + * + * Build/Install/Run: + * atest WMShellUnitTests:ShellTransitionTests */ @SmallTest @RunWith(AndroidJUnit4.class) @@ -305,6 +309,54 @@ public class ShellTransitionTests { } @Test + public void testTransitionFilterChecksTypeSet() { + TransitionFilter filter = new TransitionFilter(); + filter.mTypeSet = new int[]{TRANSIT_OPEN, TRANSIT_TO_FRONT}; + + final TransitionInfo openOnly = new TransitionInfoBuilder(TRANSIT_OPEN) + .addChange(TRANSIT_OPEN).build(); + assertTrue(filter.matches(openOnly)); + + final TransitionInfo toFrontOnly = new TransitionInfoBuilder(TRANSIT_TO_FRONT) + .addChange(TRANSIT_TO_FRONT).build(); + assertTrue(filter.matches(toFrontOnly)); + + final TransitionInfo closeOnly = new TransitionInfoBuilder(TRANSIT_CLOSE) + .addChange(TRANSIT_CLOSE).build(); + assertFalse(filter.matches(closeOnly)); + } + + @Test + public void testTransitionFilterChecksFlags() { + TransitionFilter filter = new TransitionFilter(); + filter.mFlags = TRANSIT_FLAG_KEYGUARD_GOING_AWAY; + + final TransitionInfo withFlag = new TransitionInfoBuilder(TRANSIT_TO_BACK, + TRANSIT_FLAG_KEYGUARD_GOING_AWAY) + .addChange(TRANSIT_TO_BACK).build(); + assertTrue(filter.matches(withFlag)); + + final TransitionInfo withoutFlag = new TransitionInfoBuilder(TRANSIT_OPEN) + .addChange(TRANSIT_OPEN).build(); + assertFalse(filter.matches(withoutFlag)); + } + + @Test + public void testTransitionFilterChecksNotFlags() { + TransitionFilter filter = new TransitionFilter(); + filter.mNotFlags = TRANSIT_FLAG_KEYGUARD_GOING_AWAY; + + final TransitionInfo withFlag = new TransitionInfoBuilder(TRANSIT_TO_BACK, + TRANSIT_FLAG_KEYGUARD_GOING_AWAY) + .addChange(TRANSIT_TO_BACK).build(); + assertFalse(filter.matches(withFlag)); + + final TransitionInfo withoutFlag = new TransitionInfoBuilder(TRANSIT_OPEN) + .addChange(TRANSIT_OPEN).build(); + assertTrue(filter.matches(withoutFlag)); + } + + @Test public void testRegisteredRemoteTransition() { Transitions transitions = createTestTransitions(); transitions.replaceDefaultHandlerForTest(mDefaultHandler); @@ -534,7 +586,12 @@ public class ShellTransitionTests { final TransitionInfo mInfo; TransitionInfoBuilder(@WindowManager.TransitionType int type) { - mInfo = new TransitionInfo(type, 0 /* flags */); + this(type, 0 /* flags */); + } + + TransitionInfoBuilder(@WindowManager.TransitionType int type, + @WindowManager.TransitionFlags int flags) { + mInfo = new TransitionInfo(type, flags); mInfo.setRootLeash(createMockSurface(true /* valid */), 0, 0); } diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp index b8fa55a18dac..109b5352fe30 100644 --- a/libs/hwui/Properties.cpp +++ b/libs/hwui/Properties.cpp @@ -84,7 +84,7 @@ float Properties::defaultSdrWhitePoint = 200.f; bool Properties::useHintManager = true; int Properties::targetCpuTimePercentage = 70; -bool Properties::enableWebViewOverlays = false; +bool Properties::enableWebViewOverlays = true; StretchEffectBehavior Properties::stretchEffectBehavior = StretchEffectBehavior::ShaderHWUI; @@ -139,7 +139,7 @@ bool Properties::load() { targetCpuTimePercentage = base::GetIntProperty(PROPERTY_TARGET_CPU_TIME_PERCENTAGE, 70); if (targetCpuTimePercentage <= 0 || targetCpuTimePercentage > 100) targetCpuTimePercentage = 70; - enableWebViewOverlays = base::GetBoolProperty(PROPERTY_WEBVIEW_OVERLAYS_ENABLED, false); + enableWebViewOverlays = base::GetBoolProperty(PROPERTY_WEBVIEW_OVERLAYS_ENABLED, true); return (prevDebugLayersUpdates != debugLayersUpdates) || (prevDebugOverdraw != debugOverdraw); } diff --git a/libs/hwui/WebViewFunctorManager.cpp b/libs/hwui/WebViewFunctorManager.cpp index df4101109a18..5aad821ad59f 100644 --- a/libs/hwui/WebViewFunctorManager.cpp +++ b/libs/hwui/WebViewFunctorManager.cpp @@ -100,6 +100,9 @@ WebViewFunctor::~WebViewFunctor() { destroyContext(); ATRACE_NAME("WebViewFunctor::onDestroy"); + if (mSurfaceControl) { + removeOverlays(); + } mCallbacks.onDestroyed(mFunctor, mData); } diff --git a/libs/hwui/apex/android_matrix.cpp b/libs/hwui/apex/android_matrix.cpp index 693b22b62663..04ac3cf0ebc8 100644 --- a/libs/hwui/apex/android_matrix.cpp +++ b/libs/hwui/apex/android_matrix.cpp @@ -35,3 +35,10 @@ bool AMatrix_getContents(JNIEnv* env, jobject matrixObj, float values[9]) { } return false; } + +jobject AMatrix_newInstance(JNIEnv* env, float values[9]) { + jobject matrixObj = android::android_graphics_Matrix_newInstance(env); + SkMatrix* m = android::android_graphics_Matrix_getSkMatrix(env, matrixObj); + m->set9(values); + return matrixObj; +} diff --git a/libs/hwui/apex/include/android/graphics/matrix.h b/libs/hwui/apex/include/android/graphics/matrix.h index 987ad13f7635..5705ba485ba3 100644 --- a/libs/hwui/apex/include/android/graphics/matrix.h +++ b/libs/hwui/apex/include/android/graphics/matrix.h @@ -34,6 +34,16 @@ __BEGIN_DECLS */ ANDROID_API bool AMatrix_getContents(JNIEnv* env, jobject matrixObj, float values[9]); +/** + * Returns a new Matrix jobject that contains the values passed in as initial values. + * @param values The 9 values of the 3x3 matrix in the following order. + * values[0] = scaleX values[1] = skewX values[2] = transX + * values[3] = skewY values[4] = scaleY values[5] = transY + * values[6] = persp0 values[7] = persp1 values[8] = persp2 + * @return The matrix jobject + */ +ANDROID_API jobject AMatrix_newInstance(JNIEnv* env, float values[9]); + __END_DECLS #endif // ANDROID_GRAPHICS_MATRIX_H diff --git a/libs/hwui/jni/android_graphics_Matrix.cpp b/libs/hwui/jni/android_graphics_Matrix.cpp index 7338ef24cb58..cf6702e45fff 100644 --- a/libs/hwui/jni/android_graphics_Matrix.cpp +++ b/libs/hwui/jni/android_graphics_Matrix.cpp @@ -378,13 +378,17 @@ static const JNINativeMethod methods[] = { {"nEquals", "(JJ)Z", (void*) SkMatrixGlue::equals} }; +static jclass sClazz; static jfieldID sNativeInstanceField; +static jmethodID sCtor; int register_android_graphics_Matrix(JNIEnv* env) { int result = RegisterMethodsOrDie(env, "android/graphics/Matrix", methods, NELEM(methods)); jclass clazz = FindClassOrDie(env, "android/graphics/Matrix"); + sClazz = MakeGlobalRefOrDie(env, clazz); sNativeInstanceField = GetFieldIDOrDie(env, clazz, "native_instance", "J"); + sCtor = GetMethodIDOrDie(env, clazz, "<init>", "()V"); return result; } @@ -393,4 +397,7 @@ SkMatrix* android_graphics_Matrix_getSkMatrix(JNIEnv* env, jobject matrixObj) { return reinterpret_cast<SkMatrix*>(env->GetLongField(matrixObj, sNativeInstanceField)); } +jobject android_graphics_Matrix_newInstance(JNIEnv* env) { + return env->NewObject(sClazz, sCtor); +} } diff --git a/libs/hwui/jni/android_graphics_Matrix.h b/libs/hwui/jni/android_graphics_Matrix.h index fe90d2ef945d..79de48b46954 100644 --- a/libs/hwui/jni/android_graphics_Matrix.h +++ b/libs/hwui/jni/android_graphics_Matrix.h @@ -25,6 +25,9 @@ namespace android { /* Gets the underlying SkMatrix from a Matrix object. */ SkMatrix* android_graphics_Matrix_getSkMatrix(JNIEnv* env, jobject matrixObj); +/* Creates a new Matrix java object. */ +jobject android_graphics_Matrix_newInstance(JNIEnv* env); + } // namespace android #endif // _ANDROID_GRAPHICS_MATRIX_H_ diff --git a/libs/hwui/libhwui.map.txt b/libs/hwui/libhwui.map.txt index 73de0d12a60b..77b8a44d85a1 100644 --- a/libs/hwui/libhwui.map.txt +++ b/libs/hwui/libhwui.map.txt @@ -28,6 +28,7 @@ LIBHWUI { register_android_graphics_GraphicsStatsService; zygote_preload_graphics; AMatrix_getContents; + AMatrix_newInstance; APaint_createPaint; APaint_destroyPaint; APaint_setBlendMode; diff --git a/media/java/android/media/AudioAttributes.java b/media/java/android/media/AudioAttributes.java index a031b4cfc911..c22ab9463736 100644 --- a/media/java/android/media/AudioAttributes.java +++ b/media/java/android/media/AudioAttributes.java @@ -447,13 +447,26 @@ public final class AudioAttributes implements Parcelable { */ public static final int FLAG_CAPTURE_PRIVATE = 0x1 << 13; + /** + * @hide + * Flag indicating the audio content has been processed to provide a virtual multichannel + * audio experience + */ + public static final int FLAG_CONTENT_SPATIALIZED = 0x1 << 14; + + /** + * @hide + * Flag indicating the audio content is to never be spatialized + */ + public static final int FLAG_NEVER_SPATIALIZE = 0x1 << 15; // Note that even though FLAG_MUTE_HAPTIC is stored as a flag bit, it is not here since // it is known as a boolean value outside of AudioAttributes. private static final int FLAG_ALL = FLAG_AUDIBILITY_ENFORCED | FLAG_SECURE | FLAG_SCO | FLAG_BEACON | FLAG_HW_AV_SYNC | FLAG_HW_HOTWORD | FLAG_BYPASS_INTERRUPTION_POLICY | FLAG_BYPASS_MUTE | FLAG_LOW_LATENCY | FLAG_DEEP_BUFFER | FLAG_NO_MEDIA_PROJECTION - | FLAG_NO_SYSTEM_CAPTURE | FLAG_CAPTURE_PRIVATE; + | FLAG_NO_SYSTEM_CAPTURE | FLAG_CAPTURE_PRIVATE | FLAG_CONTENT_SPATIALIZED + | FLAG_NEVER_SPATIALIZE; private final static int FLAG_ALL_PUBLIC = FLAG_AUDIBILITY_ENFORCED | FLAG_HW_AV_SYNC | FLAG_LOW_LATENCY; /* mask of flags that can be set by SDK and System APIs through the Builder */ @@ -615,6 +628,49 @@ public final class AudioAttributes implements Parcelable { } /** + * Return true if the audio content associated with these attributes has already been + * spatialized, that is it has already been processed to offer a binaural or transaural + * immersive audio experience. + * @return {@code true} if the content has been processed + */ + public boolean isContentSpatialized() { + return (mFlags & FLAG_CONTENT_SPATIALIZED) != 0; + } + + /** @hide */ + @IntDef(flag = false, value = { + SPATIALIZATION_BEHAVIOR_AUTO, + SPATIALIZATION_BEHAVIOR_NEVER, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface SpatializationBehavior {}; + + /** + * Constant indicating the audio content associated with these attributes will follow the + * default platform behavior with regards to which content will be spatialized or not. + * @see #getSpatializationBehavior() + * @see Spatializer + */ + public static final int SPATIALIZATION_BEHAVIOR_AUTO = 0; + + /** + * Constant indicating the audio content associated with these attributes should never + * be virtualized. + * @see #getSpatializationBehavior() + * @see Spatializer + */ + public static final int SPATIALIZATION_BEHAVIOR_NEVER = 1; + + /** + * Return the behavior affecting whether spatialization will be used. + * @return the spatialization behavior + */ + public @SpatializationBehavior int getSpatializationBehavior() { + return ((mFlags & FLAG_NEVER_SPATIALIZE) != 0) + ? SPATIALIZATION_BEHAVIOR_NEVER : SPATIALIZATION_BEHAVIOR_AUTO; + } + + /** * Return the capture policy. * @return the capture policy set by {@link Builder#setAllowedCapturePolicy(int)} or * the default if it was not called. @@ -657,6 +713,8 @@ public final class AudioAttributes implements Parcelable { private int mSource = MediaRecorder.AudioSource.AUDIO_SOURCE_INVALID; private int mFlags = 0x0; private boolean mMuteHapticChannels = true; + private boolean mIsContentSpatialized = false; + private int mSpatializationBehavior = SPATIALIZATION_BEHAVIOR_AUTO; private HashSet<String> mTags = new HashSet<String>(); private Bundle mBundle; private int mPrivacySensitive = PRIVACY_SENSITIVE_DEFAULT; @@ -687,6 +745,8 @@ public final class AudioAttributes implements Parcelable { mFlags = aa.getAllFlags(); mTags = (HashSet<String>) aa.mTags.clone(); mMuteHapticChannels = aa.areHapticChannelsMuted(); + mIsContentSpatialized = aa.isContentSpatialized(); + mSpatializationBehavior = aa.getSpatializationBehavior(); } /** @@ -719,6 +779,12 @@ public final class AudioAttributes implements Parcelable { if (mMuteHapticChannels) { aa.mFlags |= FLAG_MUTE_HAPTIC; } + if (mIsContentSpatialized) { + aa.mFlags |= FLAG_CONTENT_SPATIALIZED; + } + if (mSpatializationBehavior == SPATIALIZATION_BEHAVIOR_NEVER) { + aa.mFlags |= FLAG_NEVER_SPATIALIZE; + } if (mPrivacySensitive == PRIVACY_SENSITIVE_DEFAULT) { // capturing for camcorder or communication is private by default to @@ -906,6 +972,35 @@ public final class AudioAttributes implements Parcelable { } /** + * Specifies whether the content has already been processed for spatialization. + * If it has, setting this to true will prevent issues such as double-processing. + * @param isSpatialized + * @return the same Builder instance + */ + public @NonNull Builder setIsContentSpatialized(boolean isSpatialized) { + mIsContentSpatialized = isSpatialized; + return this; + } + + /** + * Sets the behavior affecting whether spatialization will be used. + * @param sb the spatialization behavior + * @return the same Builder instance + * + */ + public @NonNull Builder setSpatializationBehavior(@SpatializationBehavior int sb) { + switch (sb) { + case SPATIALIZATION_BEHAVIOR_NEVER: + case SPATIALIZATION_BEHAVIOR_AUTO: + break; + default: + throw new IllegalArgumentException("Invalid spatialization behavior " + sb); + } + mSpatializationBehavior = sb; + return this; + } + + /** * @hide * Replaces flags. * @param flags any combination of {@link AudioAttributes#FLAG_ALL}. @@ -990,6 +1085,8 @@ public final class AudioAttributes implements Parcelable { mContentType = attributes.mContentType; mFlags = attributes.getAllFlags(); mMuteHapticChannels = attributes.areHapticChannelsMuted(); + mIsContentSpatialized = attributes.isContentSpatialized(); + mSpatializationBehavior = attributes.getSpatializationBehavior(); mTags = attributes.mTags; mBundle = attributes.mBundle; mSource = attributes.mSource; diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java index 3b9c05bbe64f..a31ad613bf48 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -805,7 +805,7 @@ public class AudioManager { } @UnsupportedAppUsage - private static IAudioService getService() + static IAudioService getService() { if (sService != null) { return sService; @@ -2439,6 +2439,149 @@ public class AudioManager { } //==================================================================== + // Immersive audio + + /** + * @hide + * Returns the level of support for immersive audio from the {@link Spatializer} if + * available. + * @return the level of immersive audio support through spatialization + * @see Spatializer#getImmersiveAudioLevel() + */ + @Spatializer.ImmersiveAudioLevel int getSpatializerImmersiveAudioLevel() { + final IAudioService service = getService(); + try { + return service.getSpatializerImmersiveAudioLevel(); + } catch (RemoteException e) { + return Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_NONE; + } + } + + /** + * Return a handle to the optional platform's {@link Spatializer} + * @return {@code null} if spatialization is not supported, the {@code Spatializer} instance + * otherwise. + */ + public @Nullable Spatializer getSpatializer() { + if (getSpatializerImmersiveAudioLevel() == Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_NONE) { + return null; + } + return new Spatializer(this); + } + + /** + * @hide + * @see Spatializer#isEnabled() + * @return {@code true} if spatialization is enabled + */ + boolean isSpatializerEnabled() { + final IAudioService service = getService(); + try { + return service.isSpatializerEnabled(); + } catch (RemoteException e) { + Log.e(TAG, "Error querying isSpatializerEnabled, returning false", e); + return false; + } + } + + /** + * @hide + * @see Spatializer#setEnabled(boolean) + * Enable/disable the spatialization wherever supported. + * @param enabled {@code true} to enable + */ + void setSpatializerFeatureEnabled(boolean enabled) { + final IAudioService service = getService(); + try { + service.setSpatializerFeatureEnabled(enabled); + } catch (RemoteException e) { + Log.e(TAG, "Error calling setSpatializerFeatureEnabled", e); + } + } + + /** + * @hide + * @see Spatializer#setEnabledForDevice(boolean, AudioDeviceAttributes) + * @see Spatializer#setEnabled(boolean) + * @param enabled enable/disable for a specific device. + * @param device the device concerned with spatializer functionality. + */ + void setSpatializerEnabledForDevice(boolean enabled, + @NonNull AudioDeviceAttributes device) { + final IAudioService service = getService(); + try { + service.setSpatializerEnabledForDevice(enabled, device); + } catch (RemoteException e) { + Log.e(TAG, "Error calling setSpatializerEnabledForDevice", e); + } + } + + /** + * @hide + * @see Spatializer#canBeSpatialized(AudioAttributes, AudioFormat) + * @param attributes the {@code AudioAttributes} of the content as used for playback + * @param format the {@code AudioFormat} of the content as used for playback + * @return true if the device is capable of spatializing the combination of audio + * format and attributes. + */ + boolean canBeSpatialized( + @NonNull AudioAttributes attributes, @NonNull AudioFormat format) { + final IAudioService service = getService(); + try { + return service.canBeSpatialized(attributes, format); + } catch (RemoteException e) { + Log.e(TAG, "Error querying canBeSpatialized for attr:" + attributes + + " format:" + format + " returning false", e); + return false; + } + } + + /** + * @hide + * @see Spatializer#getCompatibleAudioDevices() + * @return a non-null list of the spatialization-compatible audio devices + */ + @NonNull List<AudioDeviceAttributes> getSpatializerCompatibleAudioDevices() { + final IAudioService service = getService(); + try { + return service.getSpatializerCompatibleAudioDevices(); + } catch (RemoteException e) { + Log.e(TAG, "Error querying getSpatializerCompatibleAudioDevices(), " + + " returning empty list", e); + return new ArrayList<AudioDeviceAttributes>(0); + } + } + + /** + * @hide + * @see Spatializer#addCompatibleAudioDevice(AudioDeviceAttributes) + * @param ada the audio device compatible with spatialization + */ + void addSpatializerCompatibleAudioDevice(@NonNull AudioDeviceAttributes ada) { + final IAudioService service = getService(); + try { + service.addSpatializerCompatibleAudioDevice(ada); + } catch (RemoteException e) { + Log.e(TAG, "Error calling addSpatializerCompatibleAudioDevice()", e); + } + } + + /** + * @hide + * @see Spatializer#removeCompatibleAudioDevice(AudioDeviceAttributes) + * @param ada the audio device incompatible with spatialization + */ + void removeSpatializerCompatibleAudioDevice(@NonNull AudioDeviceAttributes ada) { + final IAudioService service = getService(); + try { + service.removeSpatializerCompatibleAudioDevice(ada); + } catch (RemoteException e) { + Log.e(TAG, "Error calling removeSpatializerCompatibleAudioDevice()", e); + } + } + + + //==================================================================== // Bluetooth SCO control /** * Sticky broadcast intent action indicating that the Bluetooth SCO audio diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl index 0b35ebed966a..d2c70e4a6616 100755 --- a/media/java/android/media/IAudioService.aidl +++ b/media/java/android/media/IAudioService.aidl @@ -20,6 +20,7 @@ import android.bluetooth.BluetoothDevice; import android.content.ComponentName; import android.media.AudioAttributes; import android.media.AudioDeviceAttributes; +import android.media.AudioFormat; import android.media.AudioFocusInfo; import android.media.AudioPlaybackConfiguration; import android.media.AudioRecordingConfiguration; @@ -34,6 +35,7 @@ import android.media.IPlaybackConfigDispatcher; import android.media.IRecordingConfigDispatcher; import android.media.IRingtonePlayer; import android.media.IStrategyPreferredDevicesDispatcher; +import android.media.ISpatializerCallback; import android.media.IVolumeController; import android.media.IVolumeController; import android.media.PlayerBase; @@ -392,4 +394,24 @@ interface IAudioService { void registerModeDispatcher(IAudioModeDispatcher dispatcher); oneway void unregisterModeDispatcher(IAudioModeDispatcher dispatcher); + + int getSpatializerImmersiveAudioLevel(); + + boolean isSpatializerEnabled(); + + void setSpatializerFeatureEnabled(boolean enabled); + + void setSpatializerEnabledForDevice(boolean enabled, in AudioDeviceAttributes device); + + boolean canBeSpatialized(in AudioAttributes aa, in AudioFormat af); + + void registerSpatializerCallback(in ISpatializerCallback callback); + + void unregisterSpatializerCallback(in ISpatializerCallback callback); + + List<AudioDeviceAttributes> getSpatializerCompatibleAudioDevices(); + + void addSpatializerCompatibleAudioDevice(in AudioDeviceAttributes ada); + + void removeSpatializerCompatibleAudioDevice(in AudioDeviceAttributes ada); } diff --git a/media/java/android/media/ISpatializerCallback.aidl b/media/java/android/media/ISpatializerCallback.aidl new file mode 100644 index 000000000000..50c5a6b8338f --- /dev/null +++ b/media/java/android/media/ISpatializerCallback.aidl @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media; + +/** + * AIDL for the AudioService to signal Spatializer state changes. + * + * {@hide} + */ +oneway interface ISpatializerCallback { + + void dispatchSpatializerStateChanged(boolean enabled); + +} diff --git a/media/java/android/media/Spatializer.java b/media/java/android/media/Spatializer.java new file mode 100644 index 000000000000..a2267cc51c83 --- /dev/null +++ b/media/java/android/media/Spatializer.java @@ -0,0 +1,331 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media; + +import android.annotation.CallbackExecutor; +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.RequiresPermission; +import android.annotation.SystemApi; +import android.media.permission.ClearCallingIdentityContext; +import android.media.permission.SafeCloseable; +import android.os.RemoteException; + +import com.android.internal.annotations.GuardedBy; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.concurrent.Executor; + +/** + * Spatializer provides access to querying capabilities and behavior of sound spatialization + * on the device. + * Sound spatialization simulates sounds originating around the listener as if they were coming + * from virtual speakers placed around the listener.<br> + * Support for spatialization is optional, use {@link AudioManager#getSpatializer()} to obtain an + * instance of this class if the feature is supported. + * + */ +public class Spatializer { + + private final @NonNull AudioManager mAm; + + private final Object mStateListenerLock = new Object(); + + /** + * List of listeners for state listener and their associated Executor. + * List is lazy-initialized on first registration + */ + @GuardedBy("mStateListenerLock") + private @Nullable ArrayList<StateListenerInfo> mStateListeners; + + @GuardedBy("mStateListenerLock") + private SpatializerInfoDispatcherStub mInfoDispatcherStub; + + /** + * @hide + * Constructor with AudioManager acting as proxy to AudioService + * @param am a non-null AudioManager + */ + protected Spatializer(@NonNull AudioManager am) { + mAm = Objects.requireNonNull(am); + } + + /** + * Returns whether spatialization is enabled or not. + * A false value can originate from a number of sources, examples are the user electing to + * disable the feature, or the use of an audio device that is not compatible with multichannel + * audio spatialization (for instance playing audio over a monophonic speaker). + * @return {@code true} if spatialization is enabled + */ + public boolean isEnabled() { + return mAm.isSpatializerEnabled(); + } + + /** @hide */ + @IntDef(flag = false, value = { + SPATIALIZER_IMMERSIVE_LEVEL_NONE, + SPATIALIZER_IMMERSIVE_LEVEL_MULTICHANNEL, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface ImmersiveAudioLevel {}; + + /** + * @hide + * Constant indicating there are no spatialization capabilities supported on this device. + * @see AudioManager#getImmersiveAudioLevel() + */ + public static final int SPATIALIZER_IMMERSIVE_LEVEL_NONE = 0; + + /** + * @hide + * Constant indicating the {@link Spatializer} on this device supports multichannel + * spatialization. + * @see AudioManager#getImmersiveAudioLevel() + */ + public static final int SPATIALIZER_IMMERSIVE_LEVEL_MULTICHANNEL = 1; + + + /** + * @hide + * @param enabled + * @param device + */ + //TODO make as API if needed for UX, remove otherwise + //@SystemApi(client = SystemApi.Client.PRIVILEGED_APPS) + //@RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) + public void setEnabledForDevice(boolean enabled, + @NonNull AudioDeviceAttributes device) { + Objects.requireNonNull(device); + mAm.setSpatializerEnabledForDevice(enabled, device); + } + + /** + * @hide + * Enables / disables the spatializer effect + * @param enabled {@code true} for enabling the effect + */ + //TODO make as API if needed for UX, remove otherwise + //@SystemApi(client = SystemApi.Client.PRIVILEGED_APPS) + @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) + public void setEnabled(boolean enabled) { + mAm.setSpatializerFeatureEnabled(enabled); + } + + /** + * An interface to be notified of changes to the state of the spatializer. + */ + public interface OnSpatializerEnabledChangedListener { + /** + * Called when the enabled state of the Spatializer changes + * @param enabled {@code true} if the Spatializer effect is enabled on the device, + * {@code false} otherwise + */ + void onSpatializerEnabledChanged(boolean enabled); + } + + /** + * Returns whether audio of the given {@link AudioFormat}, played with the given + * {@link AudioAttributes} can be spatialized. + * Note that the result reflects the capabilities of the device and may change when + * audio accessories are connected/disconnected (e.g. wired headphones plugged in or not). + * The result is independent from whether spatialization processing is enabled or not. + * @param attributes the {@code AudioAttributes} of the content as used for playback + * @param format the {@code AudioFormat} of the content as used for playback + * @return true if the device is capable of spatializing the combination of audio format and + * attributes. + */ + public boolean canBeSpatialized( + @NonNull AudioAttributes attributes, @NonNull AudioFormat format) { + return mAm.canBeSpatialized( + Objects.requireNonNull(attributes), Objects.requireNonNull(format)); + } + + /** + * Adds a listener to be notified of changes to the enabled state of the + * {@code Spatializer}. + * @see #isEnabled() + * @param executor the {@code Executor} handling the callback + * @param listener the listener to receive enabled state updates + */ + public void addOnSpatializerEnabledChangedListener( + @NonNull @CallbackExecutor Executor executor, + @NonNull OnSpatializerEnabledChangedListener listener) { + Objects.requireNonNull(executor); + Objects.requireNonNull(listener); + synchronized (mStateListenerLock) { + if (hasSpatializerStateListener(listener)) { + throw new IllegalArgumentException( + "Called addOnSpatializerEnabledChangedListener() " + + "on a previously registered listener"); + } + // lazy initialization of the list of strategy-preferred device listener + if (mStateListeners == null) { + mStateListeners = new ArrayList<>(); + } + mStateListeners.add(new StateListenerInfo(listener, executor)); + if (mStateListeners.size() == 1) { + // register binder for callbacks + if (mInfoDispatcherStub == null) { + mInfoDispatcherStub = + new SpatializerInfoDispatcherStub(); + } + try { + mAm.getService().registerSpatializerCallback( + mInfoDispatcherStub); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + } + } + + /** + * Removes a previously added listener for changes to the enabled state of the + * {@code Spatializer}. + * @see #isEnabled() + * @param listener the listener to receive enabled state updates + */ + public void removeOnSpatializerEnabledChangedListener( + @NonNull OnSpatializerEnabledChangedListener listener) { + Objects.requireNonNull(listener); + synchronized (mStateListenerLock) { + if (!removeStateListener(listener)) { + throw new IllegalArgumentException( + "Called removeOnSpatializerEnabledChangedListener() " + + "on an unregistered listener"); + } + if (mStateListeners.size() == 0) { + // unregister binder for callbacks + try { + mAm.getService().unregisterSpatializerCallback(mInfoDispatcherStub); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } finally { + mInfoDispatcherStub = null; + mStateListeners = null; + } + } + } + } + + /** + * @hide + * Returns the list of playback devices that are compatible with the playback of multichannel + * audio through virtualization + * @return a list of devices. An empty list indicates virtualization is not supported. + */ + @SystemApi(client = SystemApi.Client.PRIVILEGED_APPS) + @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) + public @NonNull List<AudioDeviceAttributes> getCompatibleAudioDevices() { + return mAm.getSpatializerCompatibleAudioDevices(); + } + + /** + * @hide + * Adds a playback device to the list of devices compatible with the playback of multichannel + * audio through spatialization. + * @see #getCompatibleAudioDevices() + * @param ada the audio device compatible with spatialization + */ + @SystemApi(client = SystemApi.Client.PRIVILEGED_APPS) + @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) + public void addCompatibleAudioDevice(@NonNull AudioDeviceAttributes ada) { + mAm.addSpatializerCompatibleAudioDevice(Objects.requireNonNull(ada)); + } + + /** + * @hide + * Remove a playback device from the list of devices compatible with the playback of + * multichannel audio through spatialization. + * @see #getCompatibleAudioDevices() + * @param ada the audio device incompatible with spatialization + */ + @SystemApi(client = SystemApi.Client.PRIVILEGED_APPS) + @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) + public void removeCompatibleAudioDevice(@NonNull AudioDeviceAttributes ada) { + mAm.removeSpatializerCompatibleAudioDevice(Objects.requireNonNull(ada)); + } + + private final class SpatializerInfoDispatcherStub + extends ISpatializerCallback.Stub { + @Override + public void dispatchSpatializerStateChanged(boolean enabled) { + // make a shallow copy of listeners so callback is not executed under lock + final ArrayList<StateListenerInfo> stateListeners; + synchronized (mStateListenerLock) { + if (mStateListeners == null || mStateListeners.size() == 0) { + return; + } + stateListeners = (ArrayList<StateListenerInfo>) mStateListeners.clone(); + } + try (SafeCloseable ignored = ClearCallingIdentityContext.create()) { + for (StateListenerInfo info : stateListeners) { + info.mExecutor.execute(() -> + info.mListener.onSpatializerEnabledChanged(enabled)); + } + } + } + } + + private static class StateListenerInfo { + final @NonNull OnSpatializerEnabledChangedListener mListener; + final @NonNull Executor mExecutor; + + StateListenerInfo(@NonNull OnSpatializerEnabledChangedListener listener, + @NonNull Executor exe) { + mListener = listener; + mExecutor = exe; + } + } + + @GuardedBy("mStateListenerLock") + private boolean hasSpatializerStateListener(OnSpatializerEnabledChangedListener listener) { + return getStateListenerInfo(listener) != null; + } + + @GuardedBy("mStateListenerLock") + private @Nullable StateListenerInfo getStateListenerInfo( + OnSpatializerEnabledChangedListener listener) { + if (mStateListeners == null) { + return null; + } + for (StateListenerInfo info : mStateListeners) { + if (info.mListener == listener) { + return info; + } + } + return null; + } + + @GuardedBy("mStateListenerLock") + /** + * @return true if the listener was removed from the list + */ + private boolean removeStateListener(OnSpatializerEnabledChangedListener listener) { + final StateListenerInfo infoToRemove = getStateListenerInfo(listener); + if (infoToRemove != null) { + mStateListeners.remove(infoToRemove); + return true; + } + return false; + } +} diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java index c1a0a9a92cc2..b4cafd8548f4 100644 --- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java +++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java @@ -93,9 +93,9 @@ public class CompanionDeviceActivity extends Activity { final DeviceFilterPair selectedDevice = getService().mDevicesFound.get(0); setTitle(Html.fromHtml(getString( R.string.confirmation_title, - getCallingAppName(), - profileName, - selectedDevice.getDisplayName()), 0)); + Html.escapeHtml(getCallingAppName()), + Html.escapeHtml(selectedDevice.getDisplayName())), 0)); + mPairButton = findViewById(R.id.button_pair); mPairButton.setOnClickListener(v -> onDeviceConfirmed(getService().mSelectedDevice)); getService().mSelectedDevice = selectedDevice; @@ -108,8 +108,8 @@ public class CompanionDeviceActivity extends Activity { mPairButton = findViewById(R.id.button_pair); mPairButton.setVisibility(View.GONE); setTitle(Html.fromHtml(getString(R.string.chooser_title, - profileName, - getCallingAppName()), 0)); + Html.escapeHtml(profileName), + Html.escapeHtml(getCallingAppName())), 0)); mDeviceListView = findViewById(R.id.device_list); mDevicesAdapter = new DevicesAdapter(); mDeviceListView.setAdapter(mDevicesAdapter); diff --git a/packages/PackageInstaller/AndroidManifest.xml b/packages/PackageInstaller/AndroidManifest.xml index 48cdf1647d86..197b7b2b0eaf 100644 --- a/packages/PackageInstaller/AndroidManifest.xml +++ b/packages/PackageInstaller/AndroidManifest.xml @@ -31,7 +31,7 @@ android:directBootAware="true"> <receiver android:name=".TemporaryFileManager" - android:exported="true"> + android:exported="false"> <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED" /> </intent-filter> @@ -76,7 +76,7 @@ <receiver android:name=".InstallEventReceiver" android:permission="android.permission.INSTALL_PACKAGES" - android:exported="true"> + android:exported="false"> <intent-filter android:priority="1"> <action android:name="com.android.packageinstaller.ACTION_INSTALL_COMMIT" /> </intent-filter> @@ -106,14 +106,14 @@ <receiver android:name=".UninstallEventReceiver" android:permission="android.permission.INSTALL_PACKAGES" - android:exported="true"> + android:exported="false"> <intent-filter android:priority="1"> <action android:name="com.android.packageinstaller.ACTION_UNINSTALL_COMMIT" /> </intent-filter> </receiver> <receiver android:name=".PackageInstalledReceiver" - android:exported="true"> + android:exported="false"> <intent-filter android:priority="1"> <action android:name="android.intent.action.PACKAGE_ADDED" /> <data android:scheme="package" /> diff --git a/packages/PackageInstaller/TEST_MAPPING b/packages/PackageInstaller/TEST_MAPPING index 5d7b9bb36f75..cef9014ec229 100644 --- a/packages/PackageInstaller/TEST_MAPPING +++ b/packages/PackageInstaller/TEST_MAPPING @@ -19,6 +19,9 @@ }, { "name": "CtsPackageUninstallTestCases" + }, + { + "name": "PackageInstallerTests" } ] -}
\ No newline at end of file +} diff --git a/packages/PackageInstaller/res/values-te/strings.xml b/packages/PackageInstaller/res/values-te/strings.xml index 11e01c7ce51d..806734ffa997 100644 --- a/packages/PackageInstaller/res/values-te/strings.xml +++ b/packages/PackageInstaller/res/values-te/strings.xml @@ -45,14 +45,14 @@ <string name="out_of_space_dlg_title" msgid="4156690013884649502">"ఖాళీ లేదు"</string> <string name="out_of_space_dlg_text" msgid="8727714096031856231">"<xliff:g id="APP_NAME">%1$s</xliff:g>ని ఇన్స్టాల్ చేయడం సాధ్యపడలేదు. కొంత స్థలాన్ని ఖాళీ చేసి మళ్లీ ప్రయత్నించండి."</string> <string name="app_not_found_dlg_title" msgid="5107924008597470285">"యాప్ కనుగొనబడలేదు"</string> - <string name="app_not_found_dlg_text" msgid="5219983779377811611">"ఇన్స్టాల్ చేసిన యాప్ల జాబితాలో యాప్ కనుగొనబడలేదు."</string> + <string name="app_not_found_dlg_text" msgid="5219983779377811611">"ఇన్స్టాల్ చేసిన యాప్ల లిస్ట్లో యాప్ కనుగొనబడలేదు."</string> <string name="user_is_not_allowed_dlg_title" msgid="6915293433252210232">"అనుమతించబడలేదు"</string> <string name="user_is_not_allowed_dlg_text" msgid="3468447791330611681">"ప్రస్తుత వినియోగదారు ఈ అన్ఇన్స్టాలేషన్ చేసేందుకు అనుమతించబడరు."</string> <string name="generic_error_dlg_title" msgid="5863195085927067752">"లోపం"</string> <string name="generic_error_dlg_text" msgid="5287861443265795232">"యాప్ను అన్ఇన్స్టాల్ చేయడం సాధ్యపడలేదు."</string> <string name="uninstall_application_title" msgid="4045420072401428123">"యాప్ను అన్ఇన్స్టాల్ చేయి"</string> <string name="uninstall_update_title" msgid="824411791011583031">"అప్డేట్ అన్ఇన్స్టాల్ చేయి"</string> - <string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> అనేది క్రింది యాప్లో ఒక భాగం:"</string> + <string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> అనేది కింది యాప్లో ఒక భాగం:"</string> <string name="uninstall_application_text" msgid="3816830743706143980">"మీరు ఈ యాప్ను అన్ఇన్స్టాల్ చేయాలనుకుంటున్నారా?"</string> <string name="uninstall_application_text_all_users" msgid="575491774380227119">"మీరు ఈ యాప్ను "<b>"అందరు"</b>" వినియోగదారులకు అన్ఇన్స్టాల్ చేయాలనుకుంటున్నారా? అప్లికేషన్, దాని డేటా పరికరంలోని "<b>"అందరు"</b>" వినియోగదారుల నుండి తీసివేయబడుతుంది."</string> <string name="uninstall_application_text_user" msgid="498072714173920526">"మీరు వినియోగదారు <xliff:g id="USERNAME">%1$s</xliff:g> కోసం ఈ యాప్ను అన్ఇన్స్టాల్ చేయాలనుకుంటున్నారా?"</string> diff --git a/packages/PrintSpooler/res/values-ta/strings.xml b/packages/PrintSpooler/res/values-ta/strings.xml index eaf05b104046..7ffac67c5a8e 100644 --- a/packages/PrintSpooler/res/values-ta/strings.xml +++ b/packages/PrintSpooler/res/values-ta/strings.xml @@ -63,7 +63,7 @@ <string name="printer_info_desc" msgid="7181988788991581654">"இந்தப் பிரிண்டர் பற்றிய கூடுதல் தகவல்"</string> <string name="notification_channel_progress" msgid="872788690775721436">"இயக்கத்திலுள்ள அச்சுப் பணிகள்"</string> <string name="notification_channel_failure" msgid="9042250774797916414">"தோல்வியடைந்த அச்சுப் பணிகள்"</string> - <string name="could_not_create_file" msgid="3425025039427448443">"கோப்பை உருவாக்க முடியவில்லை"</string> + <string name="could_not_create_file" msgid="3425025039427448443">"ஃபைலை உருவாக்க முடியவில்லை"</string> <string name="print_services_disabled_toast" msgid="9089060734685174685">"சில அச்சுப் பொறிகள் முடக்கப்பட்டன"</string> <string name="print_searching_for_printers" msgid="6550424555079932867">"பிரிண்டர்களைத் தேடுகிறது"</string> <string name="print_no_print_services" msgid="8561247706423327966">"அச்சுப் பொறிகள் இல்லை"</string> diff --git a/packages/SettingsLib/ActionBarShadow/Android.bp b/packages/SettingsLib/ActionBarShadow/Android.bp index 800ab671cedb..4a07d49fcde5 100644 --- a/packages/SettingsLib/ActionBarShadow/Android.bp +++ b/packages/SettingsLib/ActionBarShadow/Android.bp @@ -19,5 +19,5 @@ android_library { ], sdk_version: "system_current", - min_sdk_version: "21", + min_sdk_version: "28", } diff --git a/packages/SettingsLib/ActionBarShadow/lint-baseline.xml b/packages/SettingsLib/ActionBarShadow/lint-baseline.xml deleted file mode 100644 index 4d5de5f46894..000000000000 --- a/packages/SettingsLib/ActionBarShadow/lint-baseline.xml +++ /dev/null @@ -1,37 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<issues format="5" by="lint 4.1.0" client="cli" variant="all" version="4.1.0"> - - <issue - id="NewApi" - message="Call requires API level 23 (current min is 21): `android.view.View#setOnScrollChangeListener`" - errorLine1=" mScrollView.setOnScrollChangeListener(mScrollChangeWatcher);" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SettingsLib/ActionBarShadow/src/com/android/settingslib/widget/ActionBarShadowController.java" - line="81" - column="25"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 23 (current min is 21): `android.view.View#setOnScrollChangeListener`" - errorLine1=" mScrollView.setOnScrollChangeListener(null);" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SettingsLib/ActionBarShadow/src/com/android/settingslib/widget/ActionBarShadowController.java" - line="88" - column="21"/> - </issue> - - <issue - id="NewApi" - message="Class requires API level 23 (current min is 21): `android.view.View.OnScrollChangeListener`" - errorLine1=" final class ScrollChangeWatcher implements View.OnScrollChangeListener {" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SettingsLib/ActionBarShadow/src/com/android/settingslib/widget/ActionBarShadowController.java" - line="95" - column="48"/> - </issue> - -</issues> diff --git a/packages/SettingsLib/ActionButtonsPreference/lint-baseline.xml b/packages/SettingsLib/ActionButtonsPreference/lint-baseline.xml deleted file mode 100644 index 95b7e3b8033d..000000000000 --- a/packages/SettingsLib/ActionButtonsPreference/lint-baseline.xml +++ /dev/null @@ -1,81 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<issues format="5" by="lint 4.1.0" client="cli" variant="all" version="4.1.0"> - - <issue - id="NewApi" - message="`android:Widget.DeviceDefault.Button.Borderless.Colored` requires API level 28 (current min is 21)" - errorLine1=" <style name="SettingsActionButton" parent="android:Widget.DeviceDefault.Button.Borderless.Colored">" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SettingsLib/ActionButtonsPreference/res/values/styles.xml" - line="19" - column="40"/> - </issue> - - <issue - id="NewApi" - message="`android:drawableTint` requires API level 23 (current min is 21)" - errorLine1=" <item name="android:drawableTint">@*android:color/btn_colored_borderless_text_material</item>" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SettingsLib/ActionButtonsPreference/res/values/styles.xml" - line="21" - column="15"/> - </issue> - - <issue - id="NewApi" - message="`?android:attr/dialogCornerRadius` requires API level 28 (current min is 21)" - errorLine1=" android:topLeftRadius="?android:attr/dialogCornerRadius"" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SettingsLib/ActionButtonsPreference/res/drawable/half_rounded_left_bk.xml" - line="23" - column="9"/> - </issue> - - <issue - id="NewApi" - message="`?android:attr/dialogCornerRadius` requires API level 28 (current min is 21)" - errorLine1=" android:bottomLeftRadius="?android:attr/dialogCornerRadius"" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SettingsLib/ActionButtonsPreference/res/drawable/half_rounded_left_bk.xml" - line="25" - column="9"/> - </issue> - - <issue - id="NewApi" - message="`?android:attr/dialogCornerRadius` requires API level 28 (current min is 21)" - errorLine1=" android:topRightRadius="?android:attr/dialogCornerRadius"" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SettingsLib/ActionButtonsPreference/res/drawable/half_rounded_right_bk.xml" - line="24" - column="9"/> - </issue> - - <issue - id="NewApi" - message="`?android:attr/dialogCornerRadius` requires API level 28 (current min is 21)" - errorLine1=" android:bottomRightRadius="?android:attr/dialogCornerRadius"" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SettingsLib/ActionButtonsPreference/res/drawable/half_rounded_right_bk.xml" - line="26" - column="9"/> - </issue> - - <issue - id="NewApi" - message="`?android:attr/dialogCornerRadius` requires API level 28 (current min is 21)" - errorLine1=" android:bottomRightRadius="?android:attr/dialogCornerRadius"" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SettingsLib/ActionButtonsPreference/res/drawable/rounded_bk.xml" - line="23" - column="9"/> - </issue> - -</issues> diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/half_rounded_left_bk.xml b/packages/SettingsLib/ActionButtonsPreference/res/drawable/half_rounded_left_bk.xml index 16a85d694bf8..8a25726ecd17 100644 --- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/half_rounded_left_bk.xml +++ b/packages/SettingsLib/ActionButtonsPreference/res/drawable/half_rounded_left_bk.xml @@ -17,6 +17,8 @@ <shape xmlns:android="http://schemas.android.com/apk/res/android" xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" + xmlns:tools="http://schemas.android.com/tools" + tools:targetApi="28" android:shape="rectangle"> <solid android:color="?androidprv:attr/colorSurface" /> <corners diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/half_rounded_right_bk.xml b/packages/SettingsLib/ActionButtonsPreference/res/drawable/half_rounded_right_bk.xml index 1b9f68fac850..7e626e50255a 100644 --- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/half_rounded_right_bk.xml +++ b/packages/SettingsLib/ActionButtonsPreference/res/drawable/half_rounded_right_bk.xml @@ -1,22 +1,24 @@ <?xml version="1.0" encoding="utf-8"?> <!-- - ~ Copyright (C) 2021 The Android Open Source Project - ~ - ~ Licensed under the Apache License, Version 2.0 (the "License"); - ~ you may not use this file except in compliance with the License. - ~ You may obtain a copy of the License at - ~ - ~ http://www.apache.org/licenses/LICENSE-2.0 - ~ - ~ Unless required by applicable law or agreed to in writing, software - ~ distributed under the License is distributed on an "AS IS" BASIS, - ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ~ See the License for the specific language governing permissions and - ~ limitations under the License. + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. --> <shape xmlns:android="http://schemas.android.com/apk/res/android" xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" + xmlns:tools="http://schemas.android.com/tools" + tools:targetApi="28" android:shape="rectangle"> <solid android:color="?androidprv:attr/colorSurface" /> <corners diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/rounded_bk.xml b/packages/SettingsLib/ActionButtonsPreference/res/drawable/rounded_bk.xml index a884ef11e835..9f4980beb76f 100644 --- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/rounded_bk.xml +++ b/packages/SettingsLib/ActionButtonsPreference/res/drawable/rounded_bk.xml @@ -1,22 +1,24 @@ <?xml version="1.0" encoding="utf-8"?> <!-- - ~ Copyright (C) 2021 The Android Open Source Project - ~ - ~ Licensed under the Apache License, Version 2.0 (the "License"); - ~ you may not use this file except in compliance with the License. - ~ You may obtain a copy of the License at - ~ - ~ http://www.apache.org/licenses/LICENSE-2.0 - ~ - ~ Unless required by applicable law or agreed to in writing, software - ~ distributed under the License is distributed on an "AS IS" BASIS, - ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ~ See the License for the specific language governing permissions and - ~ limitations under the License. + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. --> <shape xmlns:android="http://schemas.android.com/apk/res/android" xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" + xmlns:tools="http://schemas.android.com/tools" + tools:targetApi="28" android:shape="rectangle"> <solid android:color="?androidprv:attr/colorSurface" /> <corners diff --git a/packages/SettingsLib/ActionButtonsPreference/res/values/styles.xml b/packages/SettingsLib/ActionButtonsPreference/res/values/styles.xml index 42c7d76a80e9..8a449cfbb7c8 100644 --- a/packages/SettingsLib/ActionButtonsPreference/res/values/styles.xml +++ b/packages/SettingsLib/ActionButtonsPreference/res/values/styles.xml @@ -15,7 +15,9 @@ limitations under the License. --> -<resources> +<resources + xmlns:tools="http://schemas.android.com/tools" + tools:targetApi="28"> <style name="SettingsLibActionButton" parent="android:Widget.DeviceDefault.Button.Borderless.Colored"> <item name="android:drawablePadding">4dp</item> <item name="android:drawableTint">@*android:color/btn_colored_borderless_text_material</item> diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-v31/styles.xml b/packages/SettingsLib/BannerMessagePreference/res/values-v31/styles.xml index e74ac44ec8a4..fede44feb090 100644 --- a/packages/SettingsLib/BannerMessagePreference/res/values-v31/styles.xml +++ b/packages/SettingsLib/BannerMessagePreference/res/values-v31/styles.xml @@ -33,18 +33,18 @@ <style name="Banner.Title.SettingsLib" parent="@android:style/TextAppearance.Material.Subhead"> <item name="android:textSize">20sp</item> - <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item> + <item name="android:fontFamily">@string/settingslib_config_headlineFontFamily</item> <item name="android:textColor">?android:attr/textColorPrimary</item> </style> <style name="Banner.Subtitle.SettingsLib" - parent="@*android:style/TextAppearance.DeviceDefault.Body1"> + parent="@android:style/TextAppearance.DeviceDefault"> <item name="android:textColor">?android:attr/textColorSecondary</item> <item name="android:textSize">14sp</item> </style> <style name="Banner.Summary.SettingsLib" - parent="@*android:style/TextAppearance.DeviceDefault.Body1"> + parent="@android:style/TextAppearance.DeviceDefault"> <item name="android:textColor">?android:attr/textColorSecondary</item> <item name="android:textSize">14sp</item> </style> @@ -58,4 +58,4 @@ parent="android:Widget.DeviceDefault.Button.Borderless.Colored"> <item name="android:textColor">?android:attr/colorAccent</item> </style> -</resources>
\ No newline at end of file +</resources> diff --git a/packages/SettingsLib/BannerMessagePreference/res/values/styles.xml b/packages/SettingsLib/BannerMessagePreference/res/values/styles.xml index df47c642e402..4c6ed58f4a58 100644 --- a/packages/SettingsLib/BannerMessagePreference/res/values/styles.xml +++ b/packages/SettingsLib/BannerMessagePreference/res/values/styles.xml @@ -17,14 +17,13 @@ <resources> <style name="Banner.Text.Title" - parent="@android:style/TextAppearance.Material.Subhead"> + parent="@android:style/TextAppearance.DeviceDefault.WindowTitle"> <item name="android:textSize">16sp</item> - <item name="android:fontFamily">@*android:string/config_headlineFontFamilyMedium</item> <item name="android:textColor">?android:attr/textColorPrimary</item> </style> <style name="Banner.Text.Summary" - parent="@*android:style/TextAppearance.DeviceDefault.Body1"> + parent="@android:style/TextAppearance.DeviceDefault"> <item name="android:textColor">?android:attr/textColorSecondary</item> <item name="android:textSize">14sp</item> </style> diff --git a/packages/SettingsLib/BarChartPreference/Android.bp b/packages/SettingsLib/BarChartPreference/Android.bp index ae2606661697..4f6537334770 100644 --- a/packages/SettingsLib/BarChartPreference/Android.bp +++ b/packages/SettingsLib/BarChartPreference/Android.bp @@ -14,7 +14,7 @@ android_library { resource_dirs: ["res"], static_libs: [ - "androidx.preference_preference", + "androidx.preference_preference", ], sdk_version: "system_current", diff --git a/packages/SettingsLib/BarChartPreference/lint-baseline.xml b/packages/SettingsLib/BarChartPreference/lint-baseline.xml deleted file mode 100644 index f1043bb62fe1..000000000000 --- a/packages/SettingsLib/BarChartPreference/lint-baseline.xml +++ /dev/null @@ -1,15 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<issues format="5" by="lint 4.1.0" client="cli" variant="all" version="4.1.0"> - - <issue - id="NewApi" - message="`@android:style/Widget.DeviceDefault.Button.Borderless.Colored` requires API level 28 (current min is 21)" - errorLine1=" parent="@android:style/Widget.DeviceDefault.Button.Borderless.Colored">" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SettingsLib/BarChartPreference/res/values/styles.xml" - line="35" - column="12"/> - </issue> - -</issues> diff --git a/packages/SettingsLib/BarChartPreference/res/values/styles.xml b/packages/SettingsLib/BarChartPreference/res/values/styles.xml index 7a3fb7d9386e..1c4420717188 100644 --- a/packages/SettingsLib/BarChartPreference/res/values/styles.xml +++ b/packages/SettingsLib/BarChartPreference/res/values/styles.xml @@ -15,7 +15,9 @@ limitations under the License. --> -<resources> +<resources + xmlns:tools="http://schemas.android.com/tools" + tools:targetApi="28"> <style name="SettingsBarChart"> <item name="android:layout_marginStart">10dp</item> @@ -85,9 +87,9 @@ </style> <style name="BarChart.Text" - parent="@android:style/TextAppearance.Material.Subhead"> - <item name="android:fontFamily">@*android:string/config_headlineFontFamilyMedium</item> + parent="@android:style/TextAppearance.DeviceDefault.WindowTitle"> <item name="android:textColor">?android:attr/textColorPrimary</item> + <item name="android:textSize">16sp</item> </style> <style name="BarChart.Text.HeaderTitle"> @@ -99,7 +101,7 @@ </style> <style name="BarChart.Text.Summary" - parent="@*android:style/TextAppearance.DeviceDefault.Body1"> + parent="@android:style/TextAppearance.DeviceDefault"> <item name="android:textColor">?android:attr/textColorSecondary</item> <item name="android:textSize">12sp</item> </style> diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/Android.bp b/packages/SettingsLib/CollapsingToolbarBaseActivity/Android.bp index 2f911c4e6546..238e65ec9a3c 100644 --- a/packages/SettingsLib/CollapsingToolbarBaseActivity/Android.bp +++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/Android.bp @@ -19,6 +19,7 @@ android_library { "com.google.android.material_material", "SettingsLibSettingsTransition", "SettingsLibUtils", + "SettingsLibSettingsTheme", ], sdk_version: "system_current", min_sdk_version: "29", diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v31/collapsing_toolbar_base_layout.xml b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v31/collapsing_toolbar_base_layout.xml index 59506564400b..907863e19972 100644 --- a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v31/collapsing_toolbar_base_layout.xml +++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v31/collapsing_toolbar_base_layout.xml @@ -16,7 +16,6 @@ --> <androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/content_parent" android:layout_width="match_parent" @@ -40,7 +39,7 @@ android:clipToPadding="false" app:forceApplySystemWindowInsetTop="true" app:extraMultilineHeightEnabled="true" - app:contentScrim="?androidprv:attr/colorSurfaceHeader" + app:contentScrim="@color/settingslib_colorSurfaceHeader" app:maxLines="3" app:layout_scrollFlags="scroll|exitUntilCollapsed|snap" app:scrimAnimationDuration="50" diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-night/themes.xml b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-night-v31/themes.xml index 878275a08752..c20beaf9bf93 100644 --- a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-night/themes.xml +++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-night-v31/themes.xml @@ -18,7 +18,7 @@ <style name="Theme.CollapsingToolbar.Settings" parent="@style/Theme.MaterialComponents.DayNight"> <item name="elevationOverlayEnabled">true</item> <item name="elevationOverlayColor">?attr/colorPrimary</item> - <item name="colorPrimary">@*android:color/primary_dark_device_default_settings</item> - <item name="colorAccent">@*android:color/accent_device_default_dark</item> + <item name="colorPrimary">@color/settingslib_primary_dark_device_default_settings</item> + <item name="colorAccent">@color/settingslib_accent_device_default_dark</item> </style> </resources>
\ No newline at end of file diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values/dimens.xml b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-v31/dimens.xml index 15c1abbf97ba..15c1abbf97ba 100644 --- a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values/dimens.xml +++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-v31/dimens.xml diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values/styles.xml b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-v31/styles.xml index 63d397c69353..d0b6c4d54bb1 100644 --- a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values/styles.xml +++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-v31/styles.xml @@ -16,11 +16,13 @@ --> <resources> <style name="CollapsingToolbarTitle.Collapsed" parent="@android:style/TextAppearance.DeviceDefault.Widget.ActionBar.Title"> - <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item> + <item name="android:fontFamily">@string/settingslib_config_headlineFontFamily</item> <item name="android:textSize">20dp</item> + <item name="android:textColor">@color/settingslib_text_color_primary_device_default</item> </style> <style name="CollapsingToolbarTitle.Expanded" parent="CollapsingToolbarTitle.Collapsed"> <item name="android:textSize">36dp</item> + <item name="android:textColor">@color/settingslib_text_color_primary_device_default</item> </style> </resources>
\ No newline at end of file diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values/themes.xml b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-v31/themes.xml index 2e7a6a9181fe..9ecc297c6d36 100644 --- a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values/themes.xml +++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-v31/themes.xml @@ -18,7 +18,7 @@ <style name="Theme.CollapsingToolbar.Settings" parent="@style/Theme.MaterialComponents.DayNight"> <item name="elevationOverlayEnabled">true</item> <item name="elevationOverlayColor">?attr/colorPrimary</item> - <item name="colorPrimary">@*android:color/primary_device_default_settings_light</item> - <item name="colorAccent">@*android:color/accent_device_default_light</item> + <item name="colorPrimary">@color/settingslib_primary_device_default_settings_light</item> + <item name="colorAccent">@color/settingslib_accent_device_default_light</item> </style> </resources>
\ No newline at end of file diff --git a/packages/SettingsLib/FooterPreference/res/values/styles.xml b/packages/SettingsLib/FooterPreference/res/values/styles.xml index 08dd35991f69..5a3bada3e594 100644 --- a/packages/SettingsLib/FooterPreference/res/values/styles.xml +++ b/packages/SettingsLib/FooterPreference/res/values/styles.xml @@ -17,9 +17,8 @@ <resources> <style name="TextAppearance.Footer.Title.SettingsLib" - parent="@android:style/TextAppearance.DeviceDefault.Medium"> + parent="@android:style/TextAppearance.DeviceDefault.WindowTitle"> <item name="android:textSize">14sp</item> - <item name="android:fontFamily">@*android:string/config_headlineFontFamilyMedium</item> <item name="android:textColor">?android:attr/colorAccent</item> </style> </resources>
\ No newline at end of file diff --git a/packages/SettingsLib/HelpUtils/lint-baseline.xml b/packages/SettingsLib/HelpUtils/lint-baseline.xml deleted file mode 100644 index 940f027295a7..000000000000 --- a/packages/SettingsLib/HelpUtils/lint-baseline.xml +++ /dev/null @@ -1,15 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<issues format="5" by="lint 4.1.0" client="cli" variant="all" version="4.1.0"> - - <issue - id="NewApi" - message="Call requires API level 28 (current min is 21): `android.content.pm.PackageInfo#getLongVersionCode`" - errorLine1=" sCachedVersionCode = Long.toString(info.getLongVersionCode());" - errorLine2=" ~~~~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SettingsLib/HelpUtils/src/com/android/settingslib/HelpUtils.java" - line="239" - column="57"/> - </issue> - -</issues> diff --git a/packages/SettingsLib/HelpUtils/src/com/android/settingslib/HelpUtils.java b/packages/SettingsLib/HelpUtils/src/com/android/settingslib/HelpUtils.java index 541a2468db45..70c8658a7f55 100644 --- a/packages/SettingsLib/HelpUtils/src/com/android/settingslib/HelpUtils.java +++ b/packages/SettingsLib/HelpUtils/src/com/android/settingslib/HelpUtils.java @@ -25,6 +25,7 @@ import android.content.pm.PackageInfo; import android.content.pm.PackageManager.NameNotFoundException; import android.content.res.Resources; import android.net.Uri; +import android.os.Build; import android.provider.Settings.Global; import android.text.TextUtils; import android.util.Log; @@ -32,6 +33,7 @@ import android.view.Menu; import android.view.MenuItem; import android.view.MenuItem.OnMenuItemClickListener; +import androidx.annotation.RequiresApi; import androidx.annotation.VisibleForTesting; import com.android.settingslib.widget.R; @@ -73,6 +75,15 @@ public class HelpUtils { private HelpUtils() { } + /** + * Prepares the help menu item by doing the following. + * - If the helpUrlString is empty or null, the help menu item is made invisible. + * - Otherwise, this makes the help menu item visible and sets the intent for the help menu + * item to view the URL. + * + * @return returns whether the help menu item has been made visible. + */ + @RequiresApi(Build.VERSION_CODES.P) public static boolean prepareHelpMenuItem(Activity activity, Menu menu, String helpUri, String backupContext) { // menu contains help item, skip it @@ -84,6 +95,15 @@ public class HelpUtils { return prepareHelpMenuItem(activity, helpItem, helpUri, backupContext); } + /** + * Prepares the help menu item by doing the following. + * - If the helpUrlString is empty or null, the help menu item is made invisible. + * - Otherwise, this makes the help menu item visible and sets the intent for the help menu + * item to view the URL. + * + * @return returns whether the help menu item has been made visible. + */ + @RequiresApi(Build.VERSION_CODES.P) public static boolean prepareHelpMenuItem(Activity activity, Menu menu, int helpUriResource, String backupContext) { // menu contains help item, skip it @@ -105,6 +125,7 @@ public class HelpUtils { * @return returns whether the help menu item has been made visible. */ @VisibleForTesting + @RequiresApi(Build.VERSION_CODES.P) static boolean prepareHelpMenuItem(final Activity activity, MenuItem helpMenuItem, String helpUriString, String backupContext) { if (Global.getInt(activity.getContentResolver(), Global.DEVICE_PROVISIONED, 0) == 0) { @@ -152,6 +173,10 @@ public class HelpUtils { } } + /** + * Get the help intent from helpUriString. + */ + @RequiresApi(Build.VERSION_CODES.P) public static Intent getHelpIntent(Context context, String helpUriString, String backupContext) { if (Global.getInt(context.getContentResolver(), Global.DEVICE_PROVISIONED, 0) == 0) { @@ -223,7 +248,8 @@ public class HelpUtils { * * @return the uri with added query parameters */ - private static Uri uriWithAddedParameters(Context context, Uri baseUri) { + @RequiresApi(Build.VERSION_CODES.P) + public static Uri uriWithAddedParameters(Context context, Uri baseUri) { Uri.Builder builder = baseUri.buildUpon(); // Add in the preferred language diff --git a/packages/SettingsLib/LayoutPreference/res/values/styles.xml b/packages/SettingsLib/LayoutPreference/res/values/styles.xml index 4a99e845a5fc..2ffe6d91651b 100644 --- a/packages/SettingsLib/LayoutPreference/res/values/styles.xml +++ b/packages/SettingsLib/LayoutPreference/res/values/styles.xml @@ -24,14 +24,13 @@ </style> <style name="TextAppearance.EntityHeaderTitle" - parent="@android:style/TextAppearance.Material.Subhead"> - <item name="android:fontFamily">@*android:string/config_headlineFontFamilyMedium</item> + parent="@android:style/TextAppearance.DeviceDefault.WindowTitle"> <item name="android:textColor">?android:attr/textColorPrimary</item> <item name="android:textSize">20sp</item> </style> <style name="TextAppearance.EntityHeaderSummary" - parent="@*android:style/TextAppearance.DeviceDefault.Body1"> + parent="@android:style/TextAppearance.DeviceDefault"> <item name="android:textAlignment">viewStart</item> <item name="android:textColor">?android:attr/textColorSecondary</item> <item name="android:singleLine">true</item> diff --git a/packages/SettingsLib/MainSwitchPreference/res/drawable/settingslib_switch_bar_bg_disabled.xml b/packages/SettingsLib/MainSwitchPreference/res/drawable-v31/settingslib_switch_bar_bg_disabled.xml index 088e82bb4260..088e82bb4260 100644 --- a/packages/SettingsLib/MainSwitchPreference/res/drawable/settingslib_switch_bar_bg_disabled.xml +++ b/packages/SettingsLib/MainSwitchPreference/res/drawable-v31/settingslib_switch_bar_bg_disabled.xml diff --git a/packages/SettingsLib/MainSwitchPreference/res/drawable/settingslib_switch_bar_bg_off.xml b/packages/SettingsLib/MainSwitchPreference/res/drawable-v31/settingslib_switch_bar_bg_off.xml index 088e82bb4260..088e82bb4260 100644 --- a/packages/SettingsLib/MainSwitchPreference/res/drawable/settingslib_switch_bar_bg_off.xml +++ b/packages/SettingsLib/MainSwitchPreference/res/drawable-v31/settingslib_switch_bar_bg_off.xml diff --git a/packages/SettingsLib/MainSwitchPreference/res/drawable/settingslib_switch_bar_bg_on.xml b/packages/SettingsLib/MainSwitchPreference/res/drawable-v31/settingslib_switch_bar_bg_on.xml index 250188b892f4..250188b892f4 100644 --- a/packages/SettingsLib/MainSwitchPreference/res/drawable/settingslib_switch_bar_bg_on.xml +++ b/packages/SettingsLib/MainSwitchPreference/res/drawable-v31/settingslib_switch_bar_bg_on.xml diff --git a/packages/SettingsLib/MainSwitchPreference/res/layout-v31/settingslib_main_switch_bar.xml b/packages/SettingsLib/MainSwitchPreference/res/layout-v31/settingslib_main_switch_bar.xml index 6e5911cbf0a0..30748e6244cb 100644 --- a/packages/SettingsLib/MainSwitchPreference/res/layout-v31/settingslib_main_switch_bar.xml +++ b/packages/SettingsLib/MainSwitchPreference/res/layout-v31/settingslib_main_switch_bar.xml @@ -50,7 +50,7 @@ android:tint="?android:attr/colorAccent" android:layout_gravity="center_vertical" android:layout_marginEnd="@dimen/settingslib_restricted_icon_margin_end" - android:src="@*android:drawable/ic_info" + android:src="@android:drawable/ic_info" android:visibility="gone" /> <Switch diff --git a/packages/SettingsLib/MainSwitchPreference/res/layout/settingslib_main_switch_bar.xml b/packages/SettingsLib/MainSwitchPreference/res/layout/settingslib_main_switch_bar.xml index 306145a3e689..d0c2d0b5937d 100644 --- a/packages/SettingsLib/MainSwitchPreference/res/layout/settingslib_main_switch_bar.xml +++ b/packages/SettingsLib/MainSwitchPreference/res/layout/settingslib_main_switch_bar.xml @@ -28,7 +28,7 @@ android:layout_gravity="center_vertical" android:maxLines="2" android:ellipsize="end" - android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Widget.ActionBar.Title" + android:textAppearance="@android:style/TextAppearance.DeviceDefault.Widget.ActionBar.Title" android:textSize="16sp" android:textColor="?android:attr/textColorPrimaryInverse" android:layout_marginStart="@dimen/settingslib_switchbar_subsettings_margin_start" @@ -42,7 +42,7 @@ android:theme="@android:style/Theme.Material" android:layout_gravity="center_vertical" android:layout_marginEnd="@dimen/settingslib_restricted_icon_margin_end" - android:src="@*android:drawable/ic_info" + android:src="@android:drawable/ic_info" android:visibility="gone"/> <Switch diff --git a/packages/SettingsLib/MainSwitchPreference/res/values-v31/dimens.xml b/packages/SettingsLib/MainSwitchPreference/res/values-v31/dimens.xml new file mode 100644 index 000000000000..2272a375fb83 --- /dev/null +++ b/packages/SettingsLib/MainSwitchPreference/res/values-v31/dimens.xml @@ -0,0 +1,37 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources> + + <!-- Size of layout margin --> + <dimen name="settingslib_switchbar_margin">16dp</dimen> + + <!-- Size of layout margin left --> + <dimen name="settingslib_switchbar_padding_left">24dp</dimen> + + <!-- Size of layout margin right --> + <dimen name="settingslib_switchbar_padding_right">16dp</dimen> + + <!-- Minimum width of switch --> + <dimen name="settingslib_min_switch_width">52dp</dimen> + + <!-- Minimum width of switch bar --> + <dimen name="settingslib_min_switch_bar_height">72dp</dimen> + + <!-- Radius of switch bar --> + <dimen name="settingslib_switch_bar_radius">28dp</dimen> +</resources> diff --git a/packages/SettingsLib/MainSwitchPreference/res/values-v31/styles.xml b/packages/SettingsLib/MainSwitchPreference/res/values-v31/styles.xml new file mode 100644 index 000000000000..a50fc7cc0028 --- /dev/null +++ b/packages/SettingsLib/MainSwitchPreference/res/values-v31/styles.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources> + + <style name="MainSwitchText.Settingslib" parent="@android:style/TextAppearance.DeviceDefault.Widget.ActionBar.Title"> + <item name="android:textSize">20sp</item> + <item name="android:fontFamily">@string/settingslib_config_headlineFontFamily</item> + <item name="android:textColor">@android:color/black</item> + </style> +</resources> diff --git a/packages/SettingsLib/MainSwitchPreference/res/values/dimens.xml b/packages/SettingsLib/MainSwitchPreference/res/values/dimens.xml index 16b8af6a2dab..6362882e2332 100644 --- a/packages/SettingsLib/MainSwitchPreference/res/values/dimens.xml +++ b/packages/SettingsLib/MainSwitchPreference/res/values/dimens.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <!-- - Copyright (C) 2020 The Android Open Source Project + Copyright (C) 2021 The Android Open Source Project Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -17,30 +17,12 @@ <resources> - <!-- Size of layout margin --> - <dimen name="settingslib_switchbar_margin">16dp</dimen> - - <!-- Size of layout margin left --> - <dimen name="settingslib_switchbar_padding_left">24dp</dimen> - - <!-- Size of layout margin right --> - <dimen name="settingslib_switchbar_padding_right">16dp</dimen> - - <!-- Minimum width of switch --> - <dimen name="settingslib_min_switch_width">52dp</dimen> - - <!-- Minimum width of switch bar --> - <dimen name="settingslib_min_switch_bar_height">72dp</dimen> - <!-- Restricted icon size in switch bar --> - <dimen name="settingslib_restricted_icon_size">@*android:dimen/config_restrictedIconSize</dimen> + <dimen name="settingslib_restricted_icon_size">@android:dimen/config_restrictedIconSize</dimen> <!-- Restricted icon in switch bar --> <dimen name="settingslib_restricted_icon_margin_end">16dp</dimen> - <!-- Radius of switch bar --> - <dimen name="settingslib_switch_bar_radius">28dp</dimen> - <!-- Size of title margin --> <dimen name="settingslib_switch_title_margin">16dp</dimen> diff --git a/packages/SettingsLib/MainSwitchPreference/res/values/styles.xml b/packages/SettingsLib/MainSwitchPreference/res/values/styles.xml index 3924e301a2d3..870812ae6caf 100644 --- a/packages/SettingsLib/MainSwitchPreference/res/values/styles.xml +++ b/packages/SettingsLib/MainSwitchPreference/res/values/styles.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <!-- - Copyright (C) 2020 The Android Open Source Project + Copyright (C) 2021 The Android Open Source Project Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -17,13 +17,6 @@ <resources> - <style name="MainSwitchText.Settingslib" parent="@android:style/TextAppearance.DeviceDefault.Widget.ActionBar.Title"> - <item name="android:textSize">20sp</item> - <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item> - <item name="android:textColor">@android:color/black</item> - </style> - - <style name="SwitchBar.Switch.Settingslib" parent="@android:style/Widget.Material.CompoundButton.Switch"> <item name="android:trackTint">@color/settingslib_switchbar_switch_track_tint</item> <item name="android:thumbTint">@color/settingslib_switchbar_switch_thumb_tint</item> diff --git a/packages/SettingsLib/ProgressBar/lint-baseline.xml b/packages/SettingsLib/ProgressBar/lint-baseline.xml deleted file mode 100644 index 03d0f3f43986..000000000000 --- a/packages/SettingsLib/ProgressBar/lint-baseline.xml +++ /dev/null @@ -1,15 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<issues format="5" by="lint 4.1.0" client="cli" variant="all" version="4.1.0"> - - <issue - id="NewApi" - message="`?android:attr/colorSecondary` requires API level 25 (current min is 21)" - errorLine1=" android:background="?android:attr/colorSecondary" />" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SettingsLib/ProgressBar/res/layout/progress_header.xml" - line="27" - column="9"/> - </issue> - -</issues> diff --git a/packages/SettingsLib/ProgressBar/res/layout/progress_header.xml b/packages/SettingsLib/ProgressBar/res/layout/progress_header.xml index 268858bebf0c..52ff42cbfaad 100644 --- a/packages/SettingsLib/ProgressBar/res/layout/progress_header.xml +++ b/packages/SettingsLib/ProgressBar/res/layout/progress_header.xml @@ -17,6 +17,8 @@ <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + tools:targetApi="25" android:layout_width="match_parent" android:layout_height="3dp"> <View diff --git a/packages/SettingsLib/RestrictedLockUtils/lint-baseline.xml b/packages/SettingsLib/RestrictedLockUtils/lint-baseline.xml deleted file mode 100644 index 173c7353ae8c..000000000000 --- a/packages/SettingsLib/RestrictedLockUtils/lint-baseline.xml +++ /dev/null @@ -1,26 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<issues format="5" by="lint 4.1.0" client="cli" variant="all" version="4.1.0"> - - <issue - id="NewApi" - message="Call requires API level 23 (current min is 21): `android.content.Context#getSystemService`" - errorLine1=" ComponentName adminComponent = userContext.getSystemService(" - errorLine2=" ~~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SettingsLib/RestrictedLockUtils/src/com/android/settingslib/RestrictedLockUtils.java" - line="59" - column="52"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 23 (current min is 21): `android.content.Context#getSystemService`" - errorLine1=" UserManager um = context.getSystemService(UserManager.class);" - errorLine2=" ~~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SettingsLib/RestrictedLockUtils/src/com/android/settingslib/RestrictedLockUtils.java" - line="101" - column="34"/> - </issue> - -</issues> diff --git a/packages/SettingsLib/RestrictedLockUtils/src/com/android/settingslib/RestrictedLockUtils.java b/packages/SettingsLib/RestrictedLockUtils/src/com/android/settingslib/RestrictedLockUtils.java index a77e34b4af1e..80f02b4ac0ee 100644 --- a/packages/SettingsLib/RestrictedLockUtils/src/com/android/settingslib/RestrictedLockUtils.java +++ b/packages/SettingsLib/RestrictedLockUtils/src/com/android/settingslib/RestrictedLockUtils.java @@ -21,11 +21,13 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; +import android.os.Build; import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; import androidx.annotation.Nullable; +import androidx.annotation.RequiresApi; import java.util.Objects; @@ -34,10 +36,18 @@ import java.util.Objects; * support message dialog. */ public class RestrictedLockUtils { + /** + * Get EnforcedAdmin from DevicePolicyManager + */ + @RequiresApi(Build.VERSION_CODES.M) public static EnforcedAdmin getProfileOrDeviceOwner(Context context, UserHandle user) { return getProfileOrDeviceOwner(context, null, user); } + /** + * Get EnforcedAdmin from DevicePolicyManager + */ + @RequiresApi(Build.VERSION_CODES.M) public static EnforcedAdmin getProfileOrDeviceOwner( Context context, String enforcedRestriction, UserHandle user) { if (user == null) { @@ -73,6 +83,7 @@ public class RestrictedLockUtils { /** * Send the intent to trigger the {@code android.settings.ShowAdminSupportDetailsDialog}. */ + @RequiresApi(Build.VERSION_CODES.M) public static void sendShowAdminSupportDetailsIntent(Context context, EnforcedAdmin admin) { final Intent intent = getShowAdminSupportDetailsIntent(context, admin); int targetUserId = UserHandle.myUserId(); @@ -97,6 +108,10 @@ public class RestrictedLockUtils { return intent; } + /** + * Check if current user is profile or not + */ + @RequiresApi(Build.VERSION_CODES.M) public static boolean isCurrentUserOrProfile(Context context, int userId) { UserManager um = context.getSystemService(UserManager.class); return um.getUserProfiles().contains(UserHandle.of(userId)); diff --git a/packages/SettingsLib/SearchWidget/res/values-te/strings.xml b/packages/SettingsLib/SearchWidget/res/values-te/strings.xml index c5ece7454b22..dbad586f48d1 100644 --- a/packages/SettingsLib/SearchWidget/res/values-te/strings.xml +++ b/packages/SettingsLib/SearchWidget/res/values-te/strings.xml @@ -17,5 +17,5 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="search_menu" msgid="1914043873178389845">"సెట్టింగ్లను వెతకండి"</string> + <string name="search_menu" msgid="1914043873178389845">"సెట్టింగ్లను సెర్చ్ చేయండి"</string> </resources> diff --git a/packages/SettingsLib/SettingsSpinner/lint-baseline.xml b/packages/SettingsLib/SettingsSpinner/lint-baseline.xml deleted file mode 100644 index ae1ed38e9288..000000000000 --- a/packages/SettingsLib/SettingsSpinner/lint-baseline.xml +++ /dev/null @@ -1,15 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<issues format="5" by="lint 4.1.0" client="cli" variant="all" version="4.1.0"> - - <issue - id="NewApi" - message="Call requires API level 23 (current min is 21): `new android.widget.Spinner`" - errorLine1=" super(context, attrs, defStyleAttr, defStyleRes, mode, null);" - errorLine2=" ~~~~~"> - <location - file="frameworks/base/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/settingsspinner/SettingsSpinner.java" - line="122" - column="9"/> - </issue> - -</issues> diff --git a/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/settingsspinner/SettingsSpinner.java b/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/settingsspinner/SettingsSpinner.java index 0be80a9fd466..14286fa76d8a 100644 --- a/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/settingsspinner/SettingsSpinner.java +++ b/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/settingsspinner/SettingsSpinner.java @@ -17,9 +17,12 @@ package com.android.settingslib.widget.settingsspinner; import android.content.Context; +import android.os.Build; import android.util.AttributeSet; import android.widget.Spinner; +import androidx.annotation.RequiresApi; + import com.android.settingslib.widget.R; /** @@ -117,6 +120,7 @@ public class SettingsSpinner extends Spinner { * @see Spinner#MODE_DIALOG * @see Spinner#MODE_DROPDOWN */ + @RequiresApi(Build.VERSION_CODES.M) public SettingsSpinner(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes, int mode) { super(context, attrs, defStyleAttr, defStyleRes, mode, null); diff --git a/packages/SettingsLib/SettingsTheme/res/color-v31/settingslib_surface_light.xml b/packages/SettingsLib/SettingsTheme/res/color-v31/settingslib_surface_light.xml new file mode 100644 index 000000000000..037b80abc6f9 --- /dev/null +++ b/packages/SettingsLib/SettingsTheme/res/color-v31/settingslib_surface_light.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:color="@android:color/system_neutral1_500" android:lStar="98" /> +</selector>
\ No newline at end of file diff --git a/packages/SettingsLib/SettingsTheme/res/values-night-v31/colors.xml b/packages/SettingsLib/SettingsTheme/res/values-night-v31/colors.xml index 8c7c7ed5b120..c20690342c19 100644 --- a/packages/SettingsLib/SettingsTheme/res/values-night-v31/colors.xml +++ b/packages/SettingsLib/SettingsTheme/res/values-night-v31/colors.xml @@ -36,4 +36,11 @@ <color name="settingslib_dialog_colorError">#f28b82</color> <!-- Red 300 --> <color name="settingslib_colorSurfaceVariant">@android:color/system_neutral1_700</color> + + <color name="settingslib_colorSurfaceHeader">@android:color/system_neutral1_700</color> + + <!-- copy from accent_primary_variant_dark_device_default--> + <color name="settingslib_accent_primary_variant">@android:color/system_accent1_300</color> + + <color name="settingslib_text_color_primary_device_default">@android:color/system_neutral1_50</color> </resources>
\ No newline at end of file diff --git a/packages/SettingsLib/SettingsTheme/res/values-v31/colors.xml b/packages/SettingsLib/SettingsTheme/res/values-v31/colors.xml index 77f1bcd17371..04010985fe74 100644 --- a/packages/SettingsLib/SettingsTheme/res/values-v31/colors.xml +++ b/packages/SettingsLib/SettingsTheme/res/values-v31/colors.xml @@ -37,9 +37,32 @@ <!-- Dialog accent color --> <color name="settingslib_dialog_accent">@android:color/system_accent1_600</color> <!-- Dialog background color --> - <color name="settingslib_dialog_background">@*android:color/surface_light</color> + <color name="settingslib_dialog_background">@color/settingslib_surface_light</color> <!-- Dialog error color. --> <color name="settingslib_dialog_colorError">#d93025</color> <!-- Red 600 --> <color name="settingslib_colorSurfaceVariant">@android:color/system_neutral2_100</color> + + <color name="settingslib_colorSurfaceHeader">@android:color/system_neutral1_100</color> + + <color name="settingslib_accent_device_default_dark">@android:color/system_accent1_100</color> + + <color name="settingslib_accent_device_default_light">@android:color/system_accent1_600</color> + + <color name="settingslib_primary_dark_device_default_settings">@android:color/system_neutral1_900</color> + + <color name="settingslib_primary_device_default_settings_light">@android:color/system_neutral1_50</color> + + <color name="settingslib_accent_primary_device_default">@android:color/system_accent1_100</color> + + <!-- copy from accent_primary_variant_light_device_default--> + <color name="settingslib_accent_primary_variant">@android:color/system_accent1_600</color> + + <color name="settingslib_accent_secondary_device_default">@android:color/system_accent2_100</color> + + <color name="settingslib_background_device_default_dark">@android:color/system_neutral1_900</color> + + <color name="settingslib_background_device_default_light">@android:color/system_neutral1_50</color> + + <color name="settingslib_text_color_primary_device_default">@android:color/system_neutral1_900</color> </resources> diff --git a/packages/SettingsLib/SettingsTheme/res/values-v31/dimens.xml b/packages/SettingsLib/SettingsTheme/res/values-v31/dimens.xml index ddcc83eee4bf..1c33f1a57ea5 100644 --- a/packages/SettingsLib/SettingsTheme/res/values-v31/dimens.xml +++ b/packages/SettingsLib/SettingsTheme/res/values-v31/dimens.xml @@ -19,4 +19,5 @@ <dimen name="app_preference_padding_start">20dp</dimen> <dimen name="app_icon_min_width">52dp</dimen> <dimen name="settingslib_preferred_minimum_touch_target">48dp</dimen> + <dimen name="settingslib_dialogCornerRadius">28dp</dimen> </resources> diff --git a/packages/SettingsLib/SettingsTheme/res/values-v31/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-v31/strings.xml new file mode 100644 index 000000000000..6d072a936b15 --- /dev/null +++ b/packages/SettingsLib/SettingsTheme/res/values-v31/strings.xml @@ -0,0 +1,37 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <!-- Name of a font family to use for headlines in SettingsLib. --> + <string name="settingslib_config_headlineFontFamily" translatable="false"> + @*android:string/config_headlineFontFamily + </string> + + <!-- Name of a font family to use for headlines-medium in SettingsLib. --> + <string name="settingslib_config_headlineFontFamilyMedium" translatable="false"> + @*android:string/config_headlineFontFamilyMedium + </string> + + <!-- Name of a font family to use for body in SettingsLib. --> + <string name="settingslib_config_bodyFontFamily" translatable="false"> + @*android:string/config_bodyFontFamily + </string> + + <!-- Name of a font family to use for body-medium in SettingsLib. --> + <string name="settingslib_config_bodyFontFamilyMedium" translatable="false"> + @*android:string/config_bodyFontFamilyMedium + </string> +</resources> diff --git a/packages/SettingsLib/SettingsTheme/res/values-v31/styles.xml b/packages/SettingsLib/SettingsTheme/res/values-v31/styles.xml index 46f1e030af23..58006369988e 100644 --- a/packages/SettingsLib/SettingsTheme/res/values-v31/styles.xml +++ b/packages/SettingsLib/SettingsTheme/res/values-v31/styles.xml @@ -16,12 +16,16 @@ --> <resources> <style name="TextAppearance.PreferenceTitle.SettingsLib" - parent="@*android:style/TextAppearance.DeviceDefault.ListItem"> + parent="@android:style/TextAppearance.Material.Subhead"> + <item name="android:fontFamily">@string/settingslib_config_headlineFontFamily</item> <item name="android:textSize">20sp</item> </style> <style name="TextAppearance.CategoryTitle.SettingsLib" - parent="@*android:style/TextAppearance.DeviceDefault.Body2" /> + parent="@android:style/TextAppearance.DeviceDefault.Medium"> + <item name="android:textColor">?android:attr/textColorPrimary</item> + <item name="android:textSize">14sp</item> + </style> <style name="Switch.SettingsLib" parent="@android:style/Widget.Material.CompoundButton.Switch"> <item name="android:switchMinWidth">52dp</item> diff --git a/packages/SettingsLib/SettingsTheme/res/values-v31/themes.xml b/packages/SettingsLib/SettingsTheme/res/values-v31/themes.xml index 8034710b4341..6bf288b74d5a 100644 --- a/packages/SettingsLib/SettingsTheme/res/values-v31/themes.xml +++ b/packages/SettingsLib/SettingsTheme/res/values-v31/themes.xml @@ -50,6 +50,6 @@ <item name="android:clipToPadding">true</item> <item name="android:clipChildren">true</item> - <item name="dialogCornerRadius">@*android:dimen/config_dialogCornerRadius</item> + <item name="dialogCornerRadius">@dimen/settingslib_dialogCornerRadius</item> </style> </resources>
\ No newline at end of file diff --git a/packages/SettingsLib/SettingsTheme/res/values/dimens.xml b/packages/SettingsLib/SettingsTheme/res/values/dimens.xml index 25f9514c29b7..18af1f9c15d0 100644 --- a/packages/SettingsLib/SettingsTheme/res/values/dimens.xml +++ b/packages/SettingsLib/SettingsTheme/res/values/dimens.xml @@ -20,4 +20,5 @@ <dimen name="app_preference_padding_start">?android:attr/listPreferredItemPaddingStart</dimen> <dimen name="app_icon_min_width">56dp</dimen> <dimen name="two_target_min_width">72dp</dimen> + <dimen name="settingslib_dialogCornerRadius">8dp</dimen> </resources> diff --git a/packages/SettingsLib/SettingsTheme/res/values/themes.xml b/packages/SettingsLib/SettingsTheme/res/values/themes.xml index 6f2517746ddc..2d881d1a8a7b 100644 --- a/packages/SettingsLib/SettingsTheme/res/values/themes.xml +++ b/packages/SettingsLib/SettingsTheme/res/values/themes.xml @@ -35,7 +35,7 @@ <!-- TODO(b/189308264): fix the crash in Android R if set the attributes: <item name="colorAccent">@*android:color/accent_device_default_light</item> <item name="android:colorBackground">@color/settingslib_dialog_background</item> - <item name="dialogCornerRadius">@*android:dimen/config_dialogCornerRadius</item> + <item name="dialogCornerRadius">@dimen/settingslib_dialogCornerRadius</item> --> <item name="android:windowSoftInputMode">adjustResize</item> <item name="android:clipToPadding">true</item> diff --git a/packages/SettingsLib/TopIntroPreference/res/values/styles.xml b/packages/SettingsLib/TopIntroPreference/res/values/styles.xml index 65869b5580b5..b6ca41fb6b6d 100644 --- a/packages/SettingsLib/TopIntroPreference/res/values/styles.xml +++ b/packages/SettingsLib/TopIntroPreference/res/values/styles.xml @@ -16,8 +16,7 @@ --> <resources> <style name="TextAppearance.TopIntroText" - parent="@*android:style/TextAppearance.DeviceDefault"> - <item name="android:fontFamily">@*android:string/config_bodyFontFamily</item> + parent="@android:style/TextAppearance.DeviceDefault"> <item name="android:textSize">14sp</item> <item name="android:textColor">?android:attr/textColorSecondary</item> </style> diff --git a/packages/SettingsLib/TwoTargetPreference/res/layout-v31/preference_two_target_divider.xml b/packages/SettingsLib/TwoTargetPreference/res/layout-v31/preference_two_target_divider.xml index f21e51cf847a..46414217d76e 100644 --- a/packages/SettingsLib/TwoTargetPreference/res/layout-v31/preference_two_target_divider.xml +++ b/packages/SettingsLib/TwoTargetPreference/res/layout-v31/preference_two_target_divider.xml @@ -22,6 +22,8 @@ android:layout_height="match_parent" android:gravity="start|center_vertical" android:orientation="horizontal" + android:paddingStart="?android:attr/listPreferredItemPaddingEnd" + android:paddingLeft="?android:attr/listPreferredItemPaddingEnd" android:paddingTop="16dp" android:paddingBottom="16dp"> <View diff --git a/packages/SettingsLib/Utils/src/com/android/settingslib/utils/BuildCompatUtils.java b/packages/SettingsLib/Utils/src/com/android/settingslib/utils/BuildCompatUtils.java index 44f6f5439af3..9dcb5bc8c7fa 100644 --- a/packages/SettingsLib/Utils/src/com/android/settingslib/utils/BuildCompatUtils.java +++ b/packages/SettingsLib/Utils/src/com/android/settingslib/utils/BuildCompatUtils.java @@ -52,9 +52,9 @@ public final class BuildCompatUtils { } return (VERSION.CODENAME.equals("REL") && VERSION.SDK_INT >= 31) - || (VERSION.CODENAME.length() == 1 - && VERSION.CODENAME.compareTo("S") >= 0 - && VERSION.CODENAME.compareTo("Z") <= 0); + || (VERSION.CODENAME.length() >= 1 + && VERSION.CODENAME.toUpperCase().charAt(0) >= 'S' + && VERSION.CODENAME.toUpperCase().charAt(0) <= 'Z'); } private BuildCompatUtils() {} diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml index b503fdbbc57b..693a6d2076e8 100644 --- a/packages/SettingsLib/res/values-fr-rCA/strings.xml +++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml @@ -155,7 +155,7 @@ <string name="running_process_item_user_label" msgid="3988506293099805796">"Utilisateur : <xliff:g id="USER_NAME">%1$s</xliff:g>"</string> <string name="launch_defaults_some" msgid="3631650616557252926">"Certaines préférences par défaut définies"</string> <string name="launch_defaults_none" msgid="8049374306261262709">"Aucune préférence par défaut définie"</string> - <string name="tts_settings" msgid="8130616705989351312">"Synthèse vocale"</string> + <string name="tts_settings" msgid="8130616705989351312">"Paramètres de synthèse vocale"</string> <string name="tts_settings_title" msgid="7602210956640483039">"Synthèse vocale"</string> <string name="tts_default_rate_title" msgid="3964187817364304022">"Cadence"</string> <string name="tts_default_rate_summary" msgid="3781937042151716987">"Vitesse à laquelle le texte est énoncé"</string> diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml index 864f177fcec9..631cb3b9b628 100644 --- a/packages/SettingsLib/res/values-mr/strings.xml +++ b/packages/SettingsLib/res/values-mr/strings.xml @@ -307,7 +307,7 @@ <string name="adb_keys_warning_message" msgid="2968555274488101220">"तुम्ही पूर्वी ऑथोराइझ केलेल्या सर्व संगणकांवरुन USB डीबग करण्यासाठी अॅक्सेस रीव्होक करायचा?"</string> <string name="dev_settings_warning_title" msgid="8251234890169074553">"विकास सेटिंग्जला अनुमती द्यायची?"</string> <string name="dev_settings_warning_message" msgid="37741686486073668">"या सेटिंग्जचा हेतू फक्त विकास वापरासाठी आहे. त्यामुळे तुमचे डिव्हाइस आणि त्यावरील ॲप्लिकेशन ब्रेक होऊ शकतात किंवा नेहमीपेक्षा वेगळे वर्तन करू शकतात."</string> - <string name="verify_apps_over_usb_title" msgid="6031809675604442636">"USB वर अॅप्स पडताळून पाहा"</string> + <string name="verify_apps_over_usb_title" msgid="6031809675604442636">"USB वर अॅप्स पडताळून पहा"</string> <string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"हानिकारक वर्तनासाठी ADB/ADT द्वारे इंस्टॉल अॅप्स तपासा."</string> <string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"नावांशिवाय ब्लूटूथ डिव्हाइस (फक्त MAC पत्ते) दाखवले जातील"</string> <string name="bluetooth_disable_absolute_volume_summary" msgid="2006309932135547681">"रिमोट डिव्हाइसमध्ये सहन न होणारा मोठा आवाज किंवा नियंत्रणाचा अभाव यासारखी आवाजाची समस्या असल्यास ब्लूटूथ संपूर्ण आवाज वैशिष्ट्य बंद करते."</string> diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml index 4a3b32a3a90f..839853629569 100644 --- a/packages/SettingsLib/res/values-ta/strings.xml +++ b/packages/SettingsLib/res/values-ta/strings.xml @@ -84,7 +84,7 @@ <string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"செயலில் உள்ளது"</string> <string name="bluetooth_profile_a2dp" msgid="4632426382762851724">"மீடியா ஆடியோ"</string> <string name="bluetooth_profile_headset" msgid="5395952236133499331">"ஃபோன் அழைப்புகள்"</string> - <string name="bluetooth_profile_opp" msgid="6692618568149493430">"கோப்பு இடமாற்றம்"</string> + <string name="bluetooth_profile_opp" msgid="6692618568149493430">"ஃபைல் இடமாற்றம்"</string> <string name="bluetooth_profile_hid" msgid="2969922922664315866">"உள்ளீட்டுச் சாதனம்"</string> <string name="bluetooth_profile_pan" msgid="1006235139308318188">"இணைய அணுகல்"</string> <string name="bluetooth_profile_pbap" msgid="7064307749579335765">"தொடர்புப் பகிர்தல்"</string> @@ -101,7 +101,7 @@ <string name="bluetooth_opp_profile_summary_connected" msgid="2393521801478157362">"ஃபைலைப் பரிமாற்றும் சேவையகத்துடன் இணைக்கப்பட்டது"</string> <string name="bluetooth_map_profile_summary_connected" msgid="4141725591784669181">"வரைபடத்துடன் இணைக்கப்பட்டது"</string> <string name="bluetooth_sap_profile_summary_connected" msgid="1280297388033001037">"SAP உடன் இணைக்கப்பட்டது"</string> - <string name="bluetooth_opp_profile_summary_not_connected" msgid="3959741824627764954">"கோப்பு இடமாற்றும் சேவையகத்துடன் இணைக்கப்படவில்லை"</string> + <string name="bluetooth_opp_profile_summary_not_connected" msgid="3959741824627764954">"ஃபைல் இடமாற்றும் சேவையகத்துடன் இணைக்கப்படவில்லை"</string> <string name="bluetooth_hid_profile_summary_connected" msgid="3923653977051684833">"உள்ளீட்டுச் சாதனத்துடன் இணைக்கப்பட்டது"</string> <string name="bluetooth_pan_user_profile_summary_connected" msgid="380469653827505727">"சாதனத்துடன் இணைந்தது"</string> <string name="bluetooth_pan_nap_profile_summary_connected" msgid="3744773111299503493">"சாதனத்துடன் உள்ளூர் இண்டர்நெட்டைப் பகிர்தல்"</string> @@ -110,7 +110,7 @@ <string name="bluetooth_sap_profile_summary_use_for" msgid="6204902866176714046">"சிம் அணுகலுக்குப் பயன்படுத்தும்"</string> <string name="bluetooth_a2dp_profile_summary_use_for" msgid="7324694226276491807">"மீடியாவின் ஆடியோவிற்குப் பயன்படுத்து"</string> <string name="bluetooth_headset_profile_summary_use_for" msgid="808970643123744170">"மொபைல் ஆடியோவைப் பயன்படுத்து"</string> - <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"கோப்பு பரிமாற்றத்திற்காகப் பயன்படுத்து"</string> + <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"ஃபைல் பரிமாற்றத்திற்காகப் பயன்படுத்து"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"உள்ளீட்டுக்குப் பயன்படுத்து"</string> <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"செவித்துணை கருவிகளுக்குப் பயன்படுத்தவும்"</string> <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"இணை"</string> @@ -412,11 +412,11 @@ <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView செயல்படுத்தல்"</string> <string name="select_webview_provider_dialog_title" msgid="2444261109877277714">"WebView செயல்படுத்தலை அமை"</string> <string name="select_webview_provider_toast_text" msgid="8512254949169359848">"இனி இந்தத் தேர்வைப் பயன்படுத்த முடியாது. மீண்டும் முயலவும்."</string> - <string name="convert_to_file_encryption" msgid="2828976934129751818">"கோப்பு முறைமையாக்கத்திற்கு மாற்று"</string> + <string name="convert_to_file_encryption" msgid="2828976934129751818">"ஃபைல் முறைமையாக்கத்திற்கு மாற்று"</string> <string name="convert_to_file_encryption_enabled" msgid="840757431284311754">"மாற்று…"</string> - <string name="convert_to_file_encryption_done" msgid="8965831011811180627">"ஏற்கனவே கோப்பு என்க்ரிப்ட் செய்யப்பட்டது"</string> - <string name="title_convert_fbe" msgid="5780013350366495149">"கோப்பு சார்ந்த முறைமையாக்கத்திற்கு மாற்றுதல்"</string> - <string name="convert_to_fbe_warning" msgid="34294381569282109">"தரவுப் பகிர்வை, கோப்பு சார்ந்த முறைமையாக்கத்திற்கு மாற்றவும்.\n !!எச்சரிக்கை!! இது எல்லா தரவையும் அழிக்கும்.\n இது ஆல்பா நிலை அம்சமாக இருப்பதால் சரியாகச் செயல்படாமல் போகக்கூடும்.\n தொடர, \'அழித்து, மாற்று…\' என்பதை அழுத்தவும்."</string> + <string name="convert_to_file_encryption_done" msgid="8965831011811180627">"ஏற்கனவே ஃபைல் என்க்ரிப்ட் செய்யப்பட்டது"</string> + <string name="title_convert_fbe" msgid="5780013350366495149">"ஃபைல் சார்ந்த முறைமையாக்கத்திற்கு மாற்றுதல்"</string> + <string name="convert_to_fbe_warning" msgid="34294381569282109">"தரவுப் பகிர்வை, ஃபைல் சார்ந்த முறைமையாக்கத்திற்கு மாற்றவும்.\n !!எச்சரிக்கை!! இது எல்லா தரவையும் அழிக்கும்.\n இது ஆல்பா நிலை அம்சமாக இருப்பதால் சரியாகச் செயல்படாமல் போகக்கூடும்.\n தொடர, \'அழித்து, மாற்று…\' என்பதை அழுத்தவும்."</string> <string name="button_convert_fbe" msgid="1159861795137727671">"அழித்து மாற்று…"</string> <string name="picture_color_mode" msgid="1013807330552931903">"படத்தின் வண்ணப் பயன்முறை"</string> <string name="picture_color_mode_desc" msgid="151780973768136200">"sRGBஐப் பயன்படுத்தும்"</string> diff --git a/packages/SettingsLib/res/values-te/arrays.xml b/packages/SettingsLib/res/values-te/arrays.xml index 99efbde3a90a..e0ba52006c66 100644 --- a/packages/SettingsLib/res/values-te/arrays.xml +++ b/packages/SettingsLib/res/values-te/arrays.xml @@ -25,7 +25,7 @@ <item msgid="3288373008277313483">"స్కాన్ చేస్తోంది…"</item> <item msgid="6050951078202663628">"కనెక్ట్ చేస్తోంది..."</item> <item msgid="8356618438494652335">"ప్రామాణీకరిస్తోంది…"</item> - <item msgid="2837871868181677206">"IP చిరునామాను పొందుతోంది…"</item> + <item msgid="2837871868181677206">"IP అడ్రస్ను పొందుతోంది…"</item> <item msgid="4613015005934755724">"కనెక్ట్ చేయబడింది"</item> <item msgid="3763530049995655072">"తాత్కాలికంగా రద్దు చేయబడింది"</item> <item msgid="7852381437933824454">"డిస్కనెక్ట్ చేస్తోంది..."</item> @@ -39,7 +39,7 @@ <item msgid="1818677602615822316">"స్కాన్ చేస్తోంది…"</item> <item msgid="8339720953594087771">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g>కి కనెక్ట్ చేస్తోంది…"</item> <item msgid="3028983857109369308">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g>తో ప్రామాణీకరిస్తోంది…"</item> - <item msgid="4287401332778341890">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> నుండి IP చిరునామాను పొందుతోంది…"</item> + <item msgid="4287401332778341890">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> నుండి IP అడ్రస్ను పొందుతోంది…"</item> <item msgid="1043944043827424501">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g>కి కనెక్ట్ చేయబడింది"</item> <item msgid="7445993821842009653">"తాత్కాలికంగా రద్దు చేయబడింది"</item> <item msgid="1175040558087735707">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> నుండి డిస్కనెక్ట్ చేస్తోంది…"</item> @@ -138,15 +138,15 @@ <item msgid="1333279807604675720">"స్టీరియో"</item> </string-array> <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_titles"> - <item msgid="1241278021345116816">"ఆడియో నాణ్యత (990kbps/909kbps) కోసం అనుకూలీకరించబడింది"</item> - <item msgid="3523665555859696539">"సమతుల్య ఆడియో మరియు కనెక్షన్ నాణ్యత (660kbps/606kbps)"</item> - <item msgid="886408010459747589">"కనెక్షన్ నాణ్యత (330kbps/303kbps) కోసం అనుకూలీకరించబడింది"</item> + <item msgid="1241278021345116816">"ఆడియో క్వాలిటీ (990kbps/909kbps) కోసం అనుకూలీకరించబడింది"</item> + <item msgid="3523665555859696539">"సమతుల్య ఆడియో మరియు కనెక్షన్ క్వాలిటీ (660kbps/606kbps)"</item> + <item msgid="886408010459747589">"కనెక్షన్ క్వాలిటీ (330kbps/303kbps) కోసం అనుకూలీకరించబడింది"</item> <item msgid="3808414041654351577">"ఉత్తమ కృషి (అనుకూల బిట్ రేట్)"</item> </string-array> <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_summaries"> - <item msgid="804499336721569838">"ఆడియో నాణ్యత కోసం అనుకూలీకరించబడింది"</item> - <item msgid="7451422070435297462">"సమతుల్య ఆడియో మరియు కనెక్షన్ నాణ్యత"</item> - <item msgid="6173114545795428901">"కనెక్షన్ నాణ్యత కోసం అనుకూలీకరించబడింది"</item> + <item msgid="804499336721569838">"ఆడియో క్వాలిటీ కోసం అనుకూలీకరించబడింది"</item> + <item msgid="7451422070435297462">"సమతుల్య ఆడియో మరియు కనెక్షన్ క్వాలిటీ"</item> + <item msgid="6173114545795428901">"కనెక్షన్ క్వాలిటీ కోసం అనుకూలీకరించబడింది"</item> <item msgid="4349908264188040530">"ఉత్తమ కృషి (అనుకూల బిట్ రేట్)"</item> </string-array> <string-array name="bluetooth_audio_active_device_summaries"> diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml index 9a1e4f4297c4..1c03e225e0d8 100644 --- a/packages/SettingsLib/res/values-te/strings.xml +++ b/packages/SettingsLib/res/values-te/strings.xml @@ -26,7 +26,7 @@ <string name="wifi_disconnected" msgid="7054450256284661757">"డిస్కనెక్ట్ అయ్యింది"</string> <string name="wifi_disabled_generic" msgid="2651916945380294607">"డిజేబుల్ చేయబడింది"</string> <string name="wifi_disabled_network_failure" msgid="2660396183242399585">"IP కాన్ఫిగరేషన్ వైఫల్యం"</string> - <string name="wifi_disabled_by_recommendation_provider" msgid="1302938248432705534">"తక్కువ నాణ్యతా నెట్వర్క్ కారణంగా కనెక్ట్ చేయబడలేదు"</string> + <string name="wifi_disabled_by_recommendation_provider" msgid="1302938248432705534">"తక్కువ క్వాలిటీ నెట్వర్క్ కారణంగా కనెక్ట్ చేయబడలేదు"</string> <string name="wifi_disabled_wifi_failure" msgid="8819554899148331100">"WiFi కనెక్షన్ వైఫల్యం"</string> <string name="wifi_disabled_password_failure" msgid="6892387079613226738">"ప్రామాణీకరణ సమస్య"</string> <string name="wifi_cant_connect" msgid="5718417542623056783">"కనెక్ట్ చేయడం సాధ్యపడదు"</string> @@ -88,9 +88,9 @@ <string name="bluetooth_profile_hid" msgid="2969922922664315866">"ఇన్పుట్ పరికరం"</string> <string name="bluetooth_profile_pan" msgid="1006235139308318188">"ఇంటర్నెట్ యాక్సెస్"</string> <string name="bluetooth_profile_pbap" msgid="7064307749579335765">"కాంటాక్ట్ షేరింగ్"</string> - <string name="bluetooth_profile_pbap_summary" msgid="2955819694801952056">"పరిచయ భాగస్వామ్యం కోసం ఉపయోగించు"</string> + <string name="bluetooth_profile_pbap_summary" msgid="2955819694801952056">"కాంటాక్ట్ షేరింగ్ కోసం ఉపయోగించండి"</string> <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"ఇంటర్నెట్ కనెక్షన్ భాగస్వామ్యం"</string> - <string name="bluetooth_profile_map" msgid="8907204701162107271">"వచన సందేశాలు"</string> + <string name="bluetooth_profile_map" msgid="8907204701162107271">"వచన మెసేజ్లు"</string> <string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM యాక్సెస్"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD ఆడియో: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD ఆడియో"</string> @@ -116,7 +116,7 @@ <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"జత చేయి"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"జత చేయి"</string> <string name="bluetooth_pairing_decline" msgid="6483118841204885890">"రద్దు చేయి"</string> - <string name="bluetooth_pairing_will_share_phonebook" msgid="3064334458659165176">"జత చేయడం వలన కనెక్ట్ చేయబడినప్పుడు మీ పరిచయాలకు మరియు కాల్ చరిత్రకు ప్రాప్యతను మంజూరు చేస్తుంది."</string> + <string name="bluetooth_pairing_will_share_phonebook" msgid="3064334458659165176">"జత చేయడం వలన కనెక్ట్ చేయబడినప్పుడు మీ పరిచయాలకు మరియు కాల్ చరిత్రకు యాక్సెస్ను మంజూరు చేస్తుంది."</string> <string name="bluetooth_pairing_error_message" msgid="6626399020672335565">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>తో జత చేయడం సాధ్యపడలేదు."</string> <string name="bluetooth_pairing_pin_error_message" msgid="264422127613704940">"పిన్ లేదా పాస్కీ చెల్లని కారణంగా <xliff:g id="DEVICE_NAME">%1$s</xliff:g>తో పెయిర్ చేయడం సాధ్యపడలేదు."</string> <string name="bluetooth_pairing_device_down_error_message" msgid="2554424863101358857">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>తో కమ్యూనికేట్ చేయడం సాధ్యపడదు."</string> @@ -172,7 +172,7 @@ <string name="tts_engine_security_warning" msgid="3372432853837988146">"ఈ ప్రసంగ సమన్వయ ఇంజిన్ చదివి వినిపించబడే మొత్తం వచనాన్ని అలాగే పాస్వర్డలు మరియు క్రెడిట్ కార్డు నంబర్ల వంటి వ్యక్తిగత డేటాను సేకరించగలదు. ఇది <xliff:g id="TTS_PLUGIN_ENGINE_NAME">%s</xliff:g> ఇంజిన్లో అందించబడుతుంది. ఈ ప్రసంగ సమన్వయ ఇంజిన్ యొక్క వినియోగాన్ని ప్రారంభించాలా?"</string> <string name="tts_engine_network_required" msgid="8722087649733906851">"వచనం నుండి ప్రసంగం అవుట్పుట్ కోసం ఈ భాషకు పని చేస్తున్న నెట్వర్క్ కనెక్షన్ కావాలి."</string> <string name="tts_default_sample_string" msgid="6388016028292967973">"ఇది ప్రసంగ సమన్వయానికి ఉదాహరణ"</string> - <string name="tts_status_title" msgid="8190784181389278640">"డిఫాల్ట్ భాష స్థితి"</string> + <string name="tts_status_title" msgid="8190784181389278640">"ఆటోమేటిక్ భాష స్టేటస్"</string> <string name="tts_status_ok" msgid="8583076006537547379">"<xliff:g id="LOCALE">%1$s</xliff:g>కి పూర్తి మద్దతు ఉంది"</string> <string name="tts_status_requires_network" msgid="8327617638884678896">"<xliff:g id="LOCALE">%1$s</xliff:g>కి నెట్వర్క్ కనెక్షన్ అవసరం"</string> <string name="tts_status_not_supported" msgid="2702997696245523743">"<xliff:g id="LOCALE">%1$s</xliff:g>కు మద్దతు లేదు"</string> @@ -182,7 +182,7 @@ <string name="tts_engine_preference_section_title" msgid="3861562305498624904">"ప్రాధాన్య ఇంజిన్"</string> <string name="tts_general_section_title" msgid="8919671529502364567">"సాధారణం"</string> <string name="tts_reset_speech_pitch_title" msgid="7149398585468413246">"ప్రసంగ స్వర స్థాయిని రీసెట్ చేయండి"</string> - <string name="tts_reset_speech_pitch_summary" msgid="6822904157021406449">"వచనాన్ని చదివి వినిపించే స్వర స్థాయిని డిఫాల్ట్కి రీసెట్ చేస్తుంది."</string> + <string name="tts_reset_speech_pitch_summary" msgid="6822904157021406449">"టెక్స్ట్ను చదివి వినిపించే స్వర స్థాయిని ఆటోమేటిక్కు రీసెట్ చేస్తుంది."</string> <string-array name="tts_rate_entries"> <item msgid="9004239613505400644">"చాలా నెమ్మది"</item> <item msgid="1815382991399815061">"నెమ్మది"</item> @@ -199,7 +199,7 @@ <string name="category_work" msgid="4014193632325996115">"ఆఫీస్"</string> <string name="development_settings_title" msgid="140296922921597393">"డెవలపర్ ఆప్షన్లు"</string> <string name="development_settings_enable" msgid="4285094651288242183">"డెవలపర్ ఎంపికలను ప్రారంభించండి"</string> - <string name="development_settings_summary" msgid="8718917813868735095">"అనువర్తన అభివృద్ధి కోసం ఎంపికలను సెట్ చేయండి"</string> + <string name="development_settings_summary" msgid="8718917813868735095">"యాప్ అభివృద్ధి కోసం ఎంపికలను సెట్ చేయండి"</string> <string name="development_settings_not_available" msgid="355070198089140951">"ఈ వినియోగదారు కోసం డెవలపర్ ఎంపికలు అందుబాటులో లేవు"</string> <string name="vpn_settings_not_available" msgid="2894137119965668920">"VPN సెట్టింగ్లు ఈ వినియోగదారుకి అందుబాటులో లేవు"</string> <string name="tethering_settings_not_available" msgid="266821736434699780">"టీథరింగ్ సెట్టింగ్లు ఈ వినియోగదారుకి అందుబాటులో లేవు"</string> @@ -227,12 +227,12 @@ <string name="adb_pairing_device_dialog_pairing_code_label" msgid="3639239786669722731">"Wi‑Fi పెయిరింగ్ కోడ్"</string> <string name="adb_pairing_device_dialog_failed_title" msgid="3426758947882091735">"పెయిరింగ్ విఫలమైంది"</string> <string name="adb_pairing_device_dialog_failed_msg" msgid="6611097519661997148">"పరికరం అదే నెట్వర్క్కు కనెక్ట్ చేయబడి ఉందని నిర్ధారించుకోండి."</string> - <string name="adb_wireless_qrcode_summary" msgid="8051414549011801917">"QR కోడ్ను స్కాన్ చేయడం ద్వారా Wi-Fiని ఉపయోగించి పరికరాన్ని పెయిర్ చెయ్యండి"</string> + <string name="adb_wireless_qrcode_summary" msgid="8051414549011801917">"QR కోడ్ను స్కాన్ చేయడం ద్వారా Wi-Fiని ఉపయోగించి పరికరాన్ని పెయిర్ చేయండి"</string> <string name="adb_wireless_verifying_qrcode_text" msgid="6123192424916029207">"పరికరం పెయిర్ చేయబడుతోంది…"</string> <string name="adb_qrcode_pairing_device_failed_msg" msgid="6936292092592914132">"పరికరాన్ని పెయిర్ చేయడం విఫలమైంది. QR కోడ్ తప్పుగా ఉండడం గాని, లేదా పరికరం అదే నెట్వర్క్కు కనెక్ట్ అయి లేకపోవడం గాని జరిగింది."</string> <string name="adb_wireless_ip_addr_preference_title" msgid="8335132107715311730">"IP అడ్రస్ & పోర్ట్"</string> <string name="adb_wireless_qrcode_pairing_title" msgid="1906409667944674707">"QR కోడ్ను స్కాన్ చేయండి"</string> - <string name="adb_wireless_qrcode_pairing_description" msgid="6014121407143607851">"QR కోడ్ను స్కాన్ చేయడం ద్వారా Wi-Fiని ఉపయోగించి పరికరాన్ని పెయిర్ చెయ్యండి"</string> + <string name="adb_wireless_qrcode_pairing_description" msgid="6014121407143607851">"QR కోడ్ను స్కాన్ చేయడం ద్వారా Wi-Fiని ఉపయోగించి పరికరాన్ని పెయిర్ చేయండి"</string> <string name="adb_wireless_no_network_msg" msgid="2365795244718494658">"దయచేసి Wi-Fi నెట్వర్క్కు కనెక్ట్ చేయండి"</string> <string name="keywords_adb_wireless" msgid="6507505581882171240">"adb, డీబగ్, dev"</string> <string name="bugreport_in_power" msgid="8664089072534638709">"బగ్ రిపోర్ట్ షార్ట్కట్"</string> @@ -271,8 +271,8 @@ <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="4898693684282596143">"బ్లూటూత్ ఆడియో కోడెక్ని సక్రియం చేయండి\nఎంపిక: ఒక్కో నమూనాలో బిట్లు"</string> <string name="bluetooth_select_a2dp_codec_channel_mode" msgid="364277285688014427">"బ్లూటూత్ ఆడియో ఛానెల్ మోడ్"</string> <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="2076949781460359589">"బ్లూటూత్ ఆడియో కోడెక్ని సక్రియం చేయండి\nఎంపిక: ఛానెల్ మోడ్"</string> - <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3233402355917446304">"బ్లూటూత్ ఆడియో LDAC కోడెక్: ప్లేబ్యాక్ నాణ్యత"</string> - <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="7274396574659784285">"బ్లూటూత్ ఆడియో LDAC యాక్టివ్ చేయండి\nకోడెక్ ఎంపిక: ప్లేబ్యాక్ నాణ్యత"</string> + <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3233402355917446304">"బ్లూటూత్ ఆడియో LDAC కోడెక్: ప్లేబ్యాక్ క్వాలిటీ"</string> + <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="7274396574659784285">"బ్లూటూత్ ఆడియో LDAC యాక్టివ్ చేయండి\nకోడెక్ ఎంపిక: ప్లేబ్యాక్ క్వాలిటీ"</string> <string name="bluetooth_select_a2dp_codec_streaming_label" msgid="2040810756832027227">"ప్రసారం చేస్తోంది: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string> <string name="select_private_dns_configuration_title" msgid="7887550926056143018">"ప్రైవేట్ DNS"</string> <string name="select_private_dns_configuration_dialog_title" msgid="3731422918335951912">"ప్రైవేట్ DNS మోడ్ను ఎంచుకోండి"</string> @@ -304,7 +304,7 @@ <string name="adb_warning_message" msgid="8145270656419669221">"USB డీబగ్గింగ్ అనేది అభివృద్ధి ప్రయోజనాల కోసం మాత్రమే ఉద్దేశించబడింది. మీ కంప్యూటర్ మరియు మీ పరికరం మధ్య డేటాను కాపీ చేయడానికి, నోటిఫికేషన్ లేకుండా మీ పరికరంలో యాప్లను ఇన్స్టాల్ చేయడానికి మరియు లాగ్ డేటాను చదవడానికి దీన్ని ఉపయోగించండి."</string> <string name="adbwifi_warning_title" msgid="727104571653031865">"వైర్లెస్ డీబగ్గింగ్ను అనుమతించాలా?"</string> <string name="adbwifi_warning_message" msgid="8005936574322702388">"వైర్లెస్ డీబగ్గింగ్ అనేది అభివృద్ధి ప్రయోజనాల కోసం మాత్రమే ఉద్దేశించబడింది. మీ కంప్యూటర్, పరికరాల మధ్య డేటాను కాపీ చేయడానికి, నోటిఫికేషన్ లేకుండా మీ పరికరంలో యాప్లను ఇన్స్టాల్ చేయడానికి, లాగ్ డేటాను చదవడానికి దీన్ని ఉపయోగించండి."</string> - <string name="adb_keys_warning_message" msgid="2968555274488101220">"మీరు గతంలో ప్రామాణీకరించిన అన్ని కంప్యూటర్ల నుండి USB డీబగ్గింగ్కు ప్రాప్యతను ఉపసంహరించాలా?"</string> + <string name="adb_keys_warning_message" msgid="2968555274488101220">"మీరు గతంలో ప్రామాణీకరించిన అన్ని కంప్యూటర్ల నుండి USB డీబగ్గింగ్కు యాక్సెస్ను ఉపసంహరించాలా?"</string> <string name="dev_settings_warning_title" msgid="8251234890169074553">"అభివృద్ధి సెట్టింగ్లను అనుమతించాలా?"</string> <string name="dev_settings_warning_message" msgid="37741686486073668">"ఈ సెట్టింగ్లు అభివృద్ధి వినియోగం కోసం మాత్రమే ఉద్దేశించబడినవి. వీటి వలన మీ పరికరం మరియు దీనిలోని యాప్లు విచ్ఛిన్నం కావచ్చు లేదా తప్పుగా ప్రవర్తించవచ్చు."</string> <string name="verify_apps_over_usb_title" msgid="6031809675604442636">"USB ద్వారా యాప్లను వెరిఫై చేయి"</string> @@ -314,7 +314,7 @@ <string name="bluetooth_enable_gabeldorsche_summary" msgid="2054730331770712629">"బ్లూటూత్ Gabeldorsche ఫీచర్ స్ట్యాక్ను ఎనేబుల్ చేస్తుంది."</string> <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"మెరుగైన కనెక్టివిటీ ఫీచర్ను ఎనేబుల్ చేస్తుంది."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"స్థానిక టెర్మినల్"</string> - <string name="enable_terminal_summary" msgid="2481074834856064500">"స్థానిక షెల్ ప్రాప్యతను అందించే టెర్మినల్ యాప్ను ప్రారంభించు"</string> + <string name="enable_terminal_summary" msgid="2481074834856064500">"స్థానిక షెల్ యాక్సెస్ను అందించే టెర్మినల్ యాప్ను ప్రారంభించు"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP చెకింగ్"</string> <string name="hdcp_checking_dialog_title" msgid="7691060297616217781">"HDCP తనిఖీ ప్రవర్తనను సెట్ చేయండి"</string> <string name="debug_debugging_category" msgid="535341063709248842">"డీబగ్గింగ్"</string> @@ -383,12 +383,12 @@ <string name="local_backup_password_title" msgid="4631017948933578709">"డెస్క్టాప్ బ్యాకప్ పాస్వర్డ్"</string> <string name="local_backup_password_summary_none" msgid="7646898032616361714">"డెస్క్టాప్ పూర్తి బ్యాకప్లు ప్రస్తుతం రక్షించబడలేదు"</string> <string name="local_backup_password_summary_change" msgid="1707357670383995567">"డెస్క్టాప్ పూర్తి బ్యాకప్ల కోసం పాస్వర్డ్ను మార్చడానికి లేదా తీసివేయడానికి నొక్కండి"</string> - <string name="local_backup_password_toast_success" msgid="4891666204428091604">"కొత్త బ్యాకప్ పాస్వర్డ్ను సెట్ చేసారు"</string> + <string name="local_backup_password_toast_success" msgid="4891666204428091604">"కొత్త బ్యాకప్ పాస్వర్డ్ను సెట్ చేశారు"</string> <string name="local_backup_password_toast_confirmation_mismatch" msgid="2994718182129097733">"కొత్త పాస్వర్డ్ మరియు నిర్ధారణ సరిపోలడం లేదు"</string> <string name="local_backup_password_toast_validation_failure" msgid="714669442363647122">"బ్యాకప్ పాస్వర్డ్ను సెట్ చేయడంలో వైఫల్యం"</string> <string name="loading_injected_setting_summary" msgid="8394446285689070348">"లోడ్ చేస్తోంది…"</string> <string-array name="color_mode_names"> - <item msgid="3836559907767149216">"సచేతనం (డిఫాల్ట్)"</item> + <item msgid="3836559907767149216">"వైబ్రంట్ (ఆటోమేటిక్)"</item> <item msgid="9112200311983078311">"సహజం"</item> <item msgid="6564241960833766170">"స్టాండర్డ్"</item> </string-array> @@ -408,7 +408,7 @@ <string name="transcode_notification" msgid="5560515979793436168">"ట్రాన్స్కోడింగ్ నోటిఫికేషన్లను చూపండి"</string> <string name="transcode_disable_cache" msgid="3160069309377467045">"ట్రాన్స్కోడింగ్ కాష్ను డిజేబుల్ చేయండి"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"అమలులో ఉన్న సర్వీస్లు"</string> - <string name="runningservices_settings_summary" msgid="1046080643262665743">"ప్రస్తుతం అమలులో ఉన్న సర్వీస్లను వీక్షించండి, కంట్రోల్ చేయండి"</string> + <string name="runningservices_settings_summary" msgid="1046080643262665743">"ప్రస్తుతం అమలులో ఉన్న సర్వీస్లను చూడండి, కంట్రోల్ చేయండి"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"వెబ్ వీక్షణ అమలు"</string> <string name="select_webview_provider_dialog_title" msgid="2444261109877277714">"వెబ్ వీక్షణ అమలుని సెట్ చేయండి"</string> <string name="select_webview_provider_toast_text" msgid="8512254949169359848">"ఈ ఎంపిక ఇప్పుడు లేదు. మళ్లీ ప్రయత్నించండి."</string> @@ -529,7 +529,7 @@ <string name="help_label" msgid="3528360748637781274">"సహాయం & ఫీడ్బ్యాక్"</string> <string name="storage_category" msgid="2287342585424631813">"స్టోరేజ్"</string> <string name="shared_data_title" msgid="1017034836800864953">"షేర్ చేసిన డేటా"</string> - <string name="shared_data_summary" msgid="5516326713822885652">"షేర్ చేసిన డేటాను చూసి, సవరించండి"</string> + <string name="shared_data_summary" msgid="5516326713822885652">"షేర్ చేసిన డేటాను చూసి, ఎడిట్ చేయండి"</string> <string name="shared_data_no_blobs_text" msgid="3108114670341737434">"ఈ యూజర్ కోసం షేర్ చేసిన డేటా ఏదీ లేదు."</string> <string name="shared_data_query_failure_text" msgid="3489828881998773687">"షేర్ చేసిన డేటా పొందడంలో ఎర్రర్ ఏర్పడింది. మళ్లీ ట్రై చేయండి."</string> <string name="blob_id_text" msgid="8680078988996308061">"షేర్ చేసిన డేటా ID: <xliff:g id="BLOB_ID">%d</xliff:g>"</string> @@ -542,7 +542,7 @@ <string name="delete_blob_text" msgid="2819192607255625697">"షేర్ చేసిన డేటాను తొలగించు"</string> <string name="delete_blob_confirmation_text" msgid="7807446938920827280">"మీరు ఖచ్చితంగా ఈ షేర్ చేసిన డేటాను తొలగించాలనుకుంటున్నారా?"</string> <string name="user_add_user_item_summary" msgid="5748424612724703400">"వినియోగదారులు వారి స్వంత యాప్లను మరియు కంటెంట్ను కలిగి ఉన్నారు"</string> - <string name="user_add_profile_item_summary" msgid="5418602404308968028">"మీరు మీ ఖాతా నుండి యాప్లకు మరియు కంటెంట్కు ప్రాప్యతను పరిమితం చేయవచ్చు"</string> + <string name="user_add_profile_item_summary" msgid="5418602404308968028">"మీరు మీ ఖాతా నుండి యాప్లకు మరియు కంటెంట్కు యాక్సెస్ను పరిమితం చేయవచ్చు"</string> <string name="user_add_user_item_title" msgid="2394272381086965029">"యూజర్"</string> <string name="user_add_profile_item_title" msgid="3111051717414643029">"పరిమితం చేయబడిన ప్రొఫైల్"</string> <string name="user_add_user_title" msgid="5457079143694924885">"కొత్త వినియోగదారుని జోడించాలా?"</string> diff --git a/packages/SettingsLib/src/com/android/settingslib/location/RecentLocationAccesses.java b/packages/SettingsLib/src/com/android/settingslib/applications/RecentAppOpsAccess.java index 877dd2dfa26e..2e7cfcb13d8c 100644 --- a/packages/SettingsLib/src/com/android/settingslib/location/RecentLocationAccesses.java +++ b/packages/SettingsLib/src/com/android/settingslib/applications/RecentAppOpsAccess.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 The Android Open Source Project + * Copyright (C) 2021 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,8 @@ * limitations under the License. */ -package com.android.settingslib.location; +package com.android.settingslib.applications; + import android.app.AppOpsManager; import android.content.Context; @@ -39,14 +40,24 @@ import java.util.Comparator; import java.util.List; /** - * Retrieves the information of applications which accessed location recently. + * Retrieval of app ops information for the specified ops. */ -public class RecentLocationAccesses { - private static final String TAG = RecentLocationAccesses.class.getSimpleName(); +public class RecentAppOpsAccess { @VisibleForTesting - static final String ANDROID_SYSTEM_PACKAGE_NAME = "android"; + static final int[] LOCATION_OPS = new int[]{ + AppOpsManager.OP_FINE_LOCATION, + AppOpsManager.OP_COARSE_LOCATION, + }; + private static final int[] MICROPHONE_OPS = new int[]{ + AppOpsManager.OP_RECORD_AUDIO, + }; + - // Keep last 24 hours of location app information. + private static final String TAG = RecentAppOpsAccess.class.getSimpleName(); + @VisibleForTesting + public static final String ANDROID_SYSTEM_PACKAGE_NAME = "android"; + + // Keep last 24 hours of access app information. private static final long RECENT_TIME_INTERVAL_MILLIS = DateUtils.DAY_IN_MILLIS; /** The flags for querying ops that are trusted for showing in the UI. */ @@ -54,47 +65,55 @@ public class RecentLocationAccesses { | AppOpsManager.OP_FLAG_UNTRUSTED_PROXY | AppOpsManager.OP_FLAG_TRUSTED_PROXIED; - @VisibleForTesting - static final int[] LOCATION_OPS = new int[]{ - AppOpsManager.OP_FINE_LOCATION, - AppOpsManager.OP_COARSE_LOCATION, - }; - private final PackageManager mPackageManager; private final Context mContext; + private final int[] mOps; private final IconDrawableFactory mDrawableFactory; private final Clock mClock; - public RecentLocationAccesses(Context context) { - this(context, Clock.systemDefaultZone()); + public RecentAppOpsAccess(Context context, int[] ops) { + this(context, Clock.systemDefaultZone(), ops); } @VisibleForTesting - RecentLocationAccesses(Context context, Clock clock) { + RecentAppOpsAccess(Context context, Clock clock, int[] ops) { mContext = context; mPackageManager = context.getPackageManager(); + mOps = ops; mDrawableFactory = IconDrawableFactory.newInstance(context); mClock = clock; } /** - * Fills a list of applications which queried location recently within specified time. - * Apps are sorted by recency. Apps with more recent location accesses are in the front. + * Creates an instance of {@link RecentAppOpsAccess} for location (coarse and fine) access. + */ + public static RecentAppOpsAccess createForLocation(Context context) { + return new RecentAppOpsAccess(context, LOCATION_OPS); + } + + /** + * Creates an instance of {@link RecentAppOpsAccess} for microphone access. + */ + public static RecentAppOpsAccess createForMicrophone(Context context) { + return new RecentAppOpsAccess(context, MICROPHONE_OPS); + } + + /** + * Fills a list of applications which queried for access recently within specified time. + * Apps are sorted by recency. Apps with more recent accesses are in the front. */ @VisibleForTesting - List<Access> getAppList(boolean showSystemApps) { - // Retrieve a location usage list from AppOps - PackageManager pm = mContext.getPackageManager(); - AppOpsManager aoManager = - (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE); - List<AppOpsManager.PackageOps> appOps = aoManager.getPackagesForOps(LOCATION_OPS); + public List<Access> getAppList(boolean showSystemApps) { + // Retrieve a access usage list from AppOps + AppOpsManager aoManager = mContext.getSystemService(AppOpsManager.class); + List<AppOpsManager.PackageOps> appOps = aoManager.getPackagesForOps(mOps); final int appOpsCount = appOps != null ? appOps.size() : 0; // Process the AppOps list and generate a preference list. ArrayList<Access> accesses = new ArrayList<>(appOpsCount); final long now = mClock.millis(); - final UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE); + final UserManager um = mContext.getSystemService(UserManager.class); final List<UserHandle> profiles = um.getUserProfiles(); for (int i = 0; i < appOpsCount; ++i) { @@ -111,9 +130,10 @@ public class RecentLocationAccesses { // Don't show apps that do not have user sensitive location permissions boolean showApp = true; if (!showSystemApps) { - for (int op : LOCATION_OPS) { + for (int op : mOps) { final String permission = AppOpsManager.opToPermission(op); - final int permissionFlags = pm.getPermissionFlags(permission, packageName, + final int permissionFlags = mPackageManager.getPermissionFlags(permission, + packageName, user); if (PermissionChecker.checkPermissionForPreflight(mContext, permission, PermissionChecker.PID_UNKNOWN, uid, packageName) @@ -144,12 +164,11 @@ public class RecentLocationAccesses { return accesses; } - /** - * Gets a list of apps that accessed location recently, sorting by recency. + * Gets a list of apps that accessed the app op recently, sorting by recency. * * @param showSystemApps whether includes system apps in the list. - * @return the list of apps that recently accessed location. + * @return the list of apps that recently accessed the app op. */ public List<Access> getAppListSorted(boolean showSystemApps) { List<Access> accesses = getAppList(showSystemApps); @@ -174,18 +193,18 @@ public class RecentLocationAccesses { AppOpsManager.PackageOps ops) { String packageName = ops.getPackageName(); List<AppOpsManager.OpEntry> entries = ops.getOps(); - long locationAccessFinishTime = 0L; - // Earliest time for a location access to end and still be shown in list. - long recentLocationCutoffTime = now - RECENT_TIME_INTERVAL_MILLIS; + long accessFinishTime = 0L; + // Earliest time for a access to end and still be shown in list. + long recentAccessCutoffTime = now - RECENT_TIME_INTERVAL_MILLIS; // Compute the most recent access time from all op entries. for (AppOpsManager.OpEntry entry : entries) { long lastAccessTime = entry.getLastAccessTime(TRUSTED_STATE_FLAGS); - if (lastAccessTime > locationAccessFinishTime) { - locationAccessFinishTime = lastAccessTime; + if (lastAccessTime > accessFinishTime) { + accessFinishTime = lastAccessTime; } } // Bail out if the entry is out of date. - if (locationAccessFinishTime < recentLocationCutoffTime) { + if (accessFinishTime < recentAccessCutoffTime) { return null; } @@ -213,13 +232,16 @@ public class RecentLocationAccesses { badgedAppLabel = null; } access = new Access(packageName, userHandle, icon, appLabel, badgedAppLabel, - locationAccessFinishTime); + accessFinishTime); } catch (NameNotFoundException e) { Log.w(TAG, "package name not found for " + packageName + ", userId " + userId); } return access; } + /** + * Information about when an app last accessed a particular app op. + */ public static class Access { public final String packageName; public final UserHandle userHandle; diff --git a/packages/SettingsLib/src/com/android/settingslib/enterprise/BiometricActionDisabledByAdminController.java b/packages/SettingsLib/src/com/android/settingslib/enterprise/BiometricActionDisabledByAdminController.java index e0339dacf5ac..d205eaab6656 100644 --- a/packages/SettingsLib/src/com/android/settingslib/enterprise/BiometricActionDisabledByAdminController.java +++ b/packages/SettingsLib/src/com/android/settingslib/enterprise/BiometricActionDisabledByAdminController.java @@ -65,7 +65,6 @@ public class BiometricActionDisabledByAdminController extends BaseActionDisabled Log.d(TAG, "Positive button clicked, component: " + enforcedAdmin.component); final Intent intent = new Intent(ACTION_LEARN_MORE) .putExtra(EXTRA_SETTING_KEY, EXTRA_SETTING_VALUE) - .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) .setPackage(enforcedAdmin.component.getPackageName()); context.startActivity(intent); }; diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java index 72fa25fc7a3a..bf0dc7bce5f9 100644 --- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java +++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java @@ -157,7 +157,6 @@ public class WifiStatusTracker { private Network mDefaultNetwork = null; private NetworkCapabilities mDefaultNetworkCapabilities = null; private final Runnable mCallback; - private final boolean mSupportMergedUi; private WifiInfo mWifiInfo; public boolean enabled; @@ -181,7 +180,6 @@ public class WifiStatusTracker { mNetworkScoreManager = networkScoreManager; mConnectivityManager = connectivityManager; mCallback = callback; - mSupportMergedUi = false; } public void setListening(boolean listening) { @@ -223,10 +221,8 @@ public class WifiStatusTracker { } else { ssid = getValidSsid(mWifiInfo); } - if (mSupportMergedUi) { - isCarrierMerged = mWifiInfo.isCarrierMerged(); - subId = mWifiInfo.getSubscriptionId(); - } + isCarrierMerged = mWifiInfo.isCarrierMerged(); + subId = mWifiInfo.getSubscriptionId(); updateRssi(mWifiInfo.getRssi()); maybeRequestNetworkScore(); } @@ -255,10 +251,8 @@ public class WifiStatusTracker { } else { ssid = getValidSsid(mWifiInfo); } - if (mSupportMergedUi) { - isCarrierMerged = mWifiInfo.isCarrierMerged(); - subId = mWifiInfo.getSubscriptionId(); - } + isCarrierMerged = mWifiInfo.isCarrierMerged(); + subId = mWifiInfo.getSubscriptionId(); updateRssi(mWifiInfo.getRssi()); maybeRequestNetworkScore(); } diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/RecentLocationAccessesTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/RecentAppOpsAccessesTest.java index 16d73a39d551..cb62a735434d 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/RecentLocationAccessesTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/RecentAppOpsAccessesTest.java @@ -1,15 +1,34 @@ -package com.android.settingslib.location; +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settingslib.applications; import static com.google.common.truth.Truth.assertThat; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.isA; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; +import android.Manifest; import android.app.AppOpsManager; import android.app.AppOpsManager.OpEntry; import android.app.AppOpsManager.PackageOps; import android.content.Context; +import android.content.PermissionChecker; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; @@ -19,13 +38,14 @@ import android.os.UserManager; import android.util.LongSparseArray; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Config; +import org.robolectric.shadows.ShadowPermissionChecker; import java.time.Clock; import java.util.ArrayList; @@ -34,7 +54,8 @@ import java.util.List; import java.util.concurrent.TimeUnit; @RunWith(RobolectricTestRunner.class) -public class RecentLocationAccessesTest { +@Config(shadows = {ShadowPermissionChecker.class}) +public class RecentAppOpsAccessesTest { private static final int TEST_UID = 1234; private static final long NOW = 1_000_000_000; // Approximately 9/8/2001 @@ -54,7 +75,7 @@ public class RecentLocationAccessesTest { private Clock mClock; private Context mContext; private int mTestUserId; - private RecentLocationAccesses mRecentLocationAccesses; + private RecentAppOpsAccess mRecentAppOpsAccess; @Before public void setUp() throws NameNotFoundException { @@ -69,24 +90,37 @@ public class RecentLocationAccessesTest { .thenReturn("testApplicationLabel"); when(mPackageManager.getUserBadgedLabel(isA(CharSequence.class), isA(UserHandle.class))) .thenReturn("testUserBadgedLabel"); + when(mPackageManager.getPermissionFlags(any(), any(), any())) + .thenReturn(PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED + | PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED); + for (String testPackageName : TEST_PACKAGE_NAMES) { + ShadowPermissionChecker.setResult( + testPackageName, + Manifest.permission.ACCESS_COARSE_LOCATION, + PermissionChecker.PERMISSION_GRANTED); + ShadowPermissionChecker.setResult( + testPackageName, + Manifest.permission.ACCESS_FINE_LOCATION, + PermissionChecker.PERMISSION_GRANTED); + } mTestUserId = UserHandle.getUserId(TEST_UID); when(mUserManager.getUserProfiles()) .thenReturn(Collections.singletonList(new UserHandle(mTestUserId))); long[] testRequestTime = {ONE_MIN_AGO, TWENTY_THREE_HOURS_AGO, TWO_DAYS_AGO}; List<PackageOps> appOps = createTestPackageOpsList(TEST_PACKAGE_NAMES, testRequestTime); - when(mAppOpsManager.getPackagesForOps(RecentLocationAccesses.LOCATION_OPS)).thenReturn( + when(mAppOpsManager.getPackagesForOps(RecentAppOpsAccess.LOCATION_OPS)).thenReturn( appOps); mockTestApplicationInfos(mTestUserId, TEST_PACKAGE_NAMES); when(mClock.millis()).thenReturn(NOW); - mRecentLocationAccesses = new RecentLocationAccesses(mContext, mClock); + mRecentAppOpsAccess = new RecentAppOpsAccess(mContext, mClock, + RecentAppOpsAccess.LOCATION_OPS); } @Test - @Ignore public void testGetAppList_shouldFilterRecentAccesses() { - List<RecentLocationAccesses.Access> requests = mRecentLocationAccesses.getAppList(false); + List<RecentAppOpsAccess.Access> requests = mRecentAppOpsAccess.getAppList(false); // Only two of the apps have requested location within 15 min. assertThat(requests).hasSize(2); // Make sure apps are ordered by recency @@ -97,12 +131,11 @@ public class RecentLocationAccessesTest { } @Test - @Ignore public void testGetAppList_shouldNotShowAndroidOS() throws NameNotFoundException { // Add android OS to the list of apps. PackageOps androidSystemPackageOps = createPackageOps( - RecentLocationAccesses.ANDROID_SYSTEM_PACKAGE_NAME, + RecentAppOpsAccess.ANDROID_SYSTEM_PACKAGE_NAME, Process.SYSTEM_UID, AppOpsManager.OP_FINE_LOCATION, ONE_MIN_AGO); @@ -110,12 +143,12 @@ public class RecentLocationAccessesTest { {ONE_MIN_AGO, TWENTY_THREE_HOURS_AGO, TWO_DAYS_AGO, ONE_MIN_AGO}; List<PackageOps> appOps = createTestPackageOpsList(TEST_PACKAGE_NAMES, testRequestTime); appOps.add(androidSystemPackageOps); - when(mAppOpsManager.getPackagesForOps(RecentLocationAccesses.LOCATION_OPS)).thenReturn( + when(mAppOpsManager.getPackagesForOps(RecentAppOpsAccess.LOCATION_OPS)).thenReturn( appOps); mockTestApplicationInfos( - Process.SYSTEM_UID, RecentLocationAccesses.ANDROID_SYSTEM_PACKAGE_NAME); + Process.SYSTEM_UID, RecentAppOpsAccess.ANDROID_SYSTEM_PACKAGE_NAME); - List<RecentLocationAccesses.Access> requests = mRecentLocationAccesses.getAppList(true); + List<RecentAppOpsAccess.Access> requests = mRecentAppOpsAccess.getAppList(true); // Android OS shouldn't show up in the list of apps. assertThat(requests).hasSize(2); // Make sure apps are ordered by recency @@ -159,7 +192,7 @@ public class RecentLocationAccessesTest { // Slot for background access timestamp. final LongSparseArray<AppOpsManager.NoteOpEvent> accessEvents = new LongSparseArray<>(); accessEvents.put(AppOpsManager.makeKey(AppOpsManager.UID_STATE_BACKGROUND, - AppOpsManager.OP_FLAG_SELF), new AppOpsManager.NoteOpEvent(time, -1, null)); + AppOpsManager.OP_FLAG_SELF), new AppOpsManager.NoteOpEvent(time, -1, null)); return new OpEntry(op, AppOpsManager.MODE_ALLOWED, Collections.singletonMap(null, new AppOpsManager.AttributedOpEntry(op, false, accessEvents, null))); diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java index 01ae1e915c8c..db33c3f2c9cf 100644 --- a/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java +++ b/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java @@ -78,5 +78,6 @@ public class GlobalSettings { Settings.Global.POWER_BUTTON_LONG_PRESS, Settings.Global.AUTOMATIC_POWER_SAVE_MODE, Settings.Global.ADVANCED_BATTERY_USAGE_AMOUNT, + Settings.Global.POWER_BUTTON_LONG_PRESS_DURATION_MS }; } diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java index 60226084c70d..96f127b6a611 100644 --- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java +++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java @@ -101,6 +101,7 @@ public class SecureSettings { Settings.Secure.CAMERA_DOUBLE_TAP_POWER_GESTURE_DISABLED, Settings.Secure.SYSTEM_NAVIGATION_KEYS_ENABLED, Settings.Secure.QS_TILES, + Settings.Secure.QS_AUTO_ADDED_TILES, Settings.Secure.CONTROLS_ENABLED, Settings.Secure.POWER_MENU_LOCKED_SHOW_CONTENT, Settings.Secure.DOZE_ENABLED, @@ -118,7 +119,6 @@ public class SecureSettings { Settings.Secure.VR_DISPLAY_MODE, Settings.Secure.NOTIFICATION_BADGING, Settings.Secure.NOTIFICATION_DISMISS_RTL, - Settings.Secure.QS_AUTO_ADDED_TILES, Settings.Secure.SCREENSAVER_ENABLED, Settings.Secure.SCREENSAVER_COMPONENTS, Settings.Secure.SCREENSAVER_ACTIVATE_ON_DOCK, diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java index 3c7d7a8e890c..d0448ef63793 100644 --- a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java +++ b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java @@ -20,6 +20,7 @@ import static android.media.AudioFormat.SURROUND_SOUND_ENCODING; import static android.provider.settings.validators.SettingsValidators.ANY_INTEGER_VALIDATOR; import static android.provider.settings.validators.SettingsValidators.ANY_STRING_VALIDATOR; import static android.provider.settings.validators.SettingsValidators.BOOLEAN_VALIDATOR; +import static android.provider.settings.validators.SettingsValidators.NONE_NEGATIVE_LONG_VALIDATOR; import static android.provider.settings.validators.SettingsValidators.PACKAGE_NAME_VALIDATOR; import static android.provider.settings.validators.SettingsValidators.PERCENTAGE_INTEGER_VALIDATOR; import static android.view.Display.HdrCapabilities.HDR_TYPES; @@ -142,6 +143,7 @@ public class GlobalSettingsValidators { VALIDATORS.put(Global.DEVICE_CONFIG_SYNC_DISABLED, BOOLEAN_VALIDATOR); VALIDATORS.put(Global.AUTOMATIC_POWER_SAVE_MODE, ANY_INTEGER_VALIDATOR); VALIDATORS.put(Global.ADVANCED_BATTERY_USAGE_AMOUNT, PERCENTAGE_INTEGER_VALIDATOR); + VALIDATORS.put(Global.POWER_BUTTON_LONG_PRESS_DURATION_MS, NONE_NEGATIVE_LONG_VALIDATOR); } } diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java index c57786888e8d..6cfcb51239a3 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java @@ -72,8 +72,9 @@ public class SettingsHelper { * {@hide} */ private static final ArraySet<String> sBroadcastOnRestore; + private static final ArraySet<String> sBroadcastOnRestoreSystemUI; static { - sBroadcastOnRestore = new ArraySet<String>(4); + sBroadcastOnRestore = new ArraySet<String>(9); sBroadcastOnRestore.add(Settings.Secure.ENABLED_NOTIFICATION_LISTENERS); sBroadcastOnRestore.add(Settings.Secure.ENABLED_VR_LISTENERS); sBroadcastOnRestore.add(Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES); @@ -83,6 +84,9 @@ public class SettingsHelper { sBroadcastOnRestore.add(Settings.Secure.DARK_THEME_CUSTOM_END_TIME); sBroadcastOnRestore.add(Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED); sBroadcastOnRestore.add(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS); + sBroadcastOnRestoreSystemUI = new ArraySet<String>(2); + sBroadcastOnRestoreSystemUI.add(Settings.Secure.QS_TILES); + sBroadcastOnRestoreSystemUI.add(Settings.Secure.QS_AUTO_ADDED_TILES); } private interface SettingsLookup { @@ -133,6 +137,7 @@ public class SettingsHelper { // Will we need a post-restore broadcast for this element? String oldValue = null; boolean sendBroadcast = false; + boolean sendBroadcastSystemUI = false; final SettingsLookup table; if (destination.equals(Settings.Secure.CONTENT_URI)) { @@ -143,10 +148,12 @@ public class SettingsHelper { table = sGlobalLookup; } - if (sBroadcastOnRestore.contains(name)) { + sendBroadcast = sBroadcastOnRestore.contains(name); + sendBroadcastSystemUI = sBroadcastOnRestoreSystemUI.contains(name); + + if (sendBroadcast || sendBroadcastSystemUI) { // TODO: http://b/22388012 oldValue = table.lookup(cr, name, UserHandle.USER_SYSTEM); - sendBroadcast = true; } try { @@ -193,18 +200,28 @@ public class SettingsHelper { } catch (Exception e) { // If we fail to apply the setting, by definition nothing happened sendBroadcast = false; + sendBroadcastSystemUI = false; } finally { // If this was an element of interest, send the "we just restored it" // broadcast with the historical value now that the new value has // been committed and observers kicked off. - if (sendBroadcast) { + if (sendBroadcast || sendBroadcastSystemUI) { Intent intent = new Intent(Intent.ACTION_SETTING_RESTORED) - .setPackage("android").addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY) + .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY) .putExtra(Intent.EXTRA_SETTING_NAME, name) .putExtra(Intent.EXTRA_SETTING_NEW_VALUE, value) .putExtra(Intent.EXTRA_SETTING_PREVIOUS_VALUE, oldValue) .putExtra(Intent.EXTRA_SETTING_RESTORED_FROM_SDK_INT, restoredFromSdkInt); - context.sendBroadcastAsUser(intent, UserHandle.SYSTEM, null); + + if (sendBroadcast) { + intent.setPackage("android"); + context.sendBroadcastAsUser(intent, UserHandle.SYSTEM, null); + } + if (sendBroadcastSystemUI) { + intent.setPackage( + context.getString(com.android.internal.R.string.config_systemUi)); + context.sendBroadcastAsUser(intent, UserHandle.SYSTEM, null); + } } } } diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java index 073b4d00653d..90cec3fb6913 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java @@ -1195,6 +1195,9 @@ class SettingsProtoDumpUtil { dumpSetting(s, p, Settings.Global.POWER_MANAGER_CONSTANTS, GlobalSettingsProto.POWER_MANAGER_CONSTANTS); + dumpSetting(s, p, + Settings.Global.POWER_BUTTON_LONG_PRESS_DURATION_MS, + GlobalSettingsProto.POWER_BUTTON_LONG_PRESS_DURATION_MS); final long prepaidSetupToken = p.start(GlobalSettingsProto.PREPAID_SETUP); dumpSetting(s, p, diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt index a50efd731cf6..3cbe435026b5 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt @@ -5,7 +5,6 @@ import android.animation.AnimatorListenerAdapter import android.animation.ValueAnimator import android.app.ActivityManager import android.app.ActivityTaskManager -import android.app.AppGlobals import android.app.PendingIntent import android.app.TaskInfo import android.content.Context @@ -44,6 +43,7 @@ class ActivityLaunchAnimator( context: Context ) { companion object { + private const val DEBUG = false const val ANIMATION_DURATION = 500L private const val ANIMATION_DURATION_FADE_OUT_CONTENT = 150L private const val ANIMATION_DURATION_FADE_IN_WINDOW = 183L @@ -75,8 +75,6 @@ class ActivityLaunchAnimator( } } - private val packageManager = AppGlobals.getPackageManager() - /** The interpolator used for the width, height, Y position and corner radius. */ private val animationInterpolator = AnimationUtils.loadInterpolator(context, R.interpolator.launch_animation_interpolator_y) @@ -100,6 +98,10 @@ class ActivityLaunchAnimator( * If possible, you should pass the [packageName] of the intent that will be started so that * trampoline activity launches will also be animated. * + * If the device is currently locked, the user will have to unlock it before the intent is + * started unless [showOverLockscreen] is true. In that case, the activity will be started + * directly over the lockscreen. + * * This method will throw any exception thrown by [intentStarter]. */ @JvmOverloads @@ -107,21 +109,22 @@ class ActivityLaunchAnimator( controller: Controller?, animate: Boolean = true, packageName: String? = null, + showOverLockscreen: Boolean = false, intentStarter: (RemoteAnimationAdapter?) -> Int ) { if (controller == null || !animate) { - Log.d(TAG, "Starting intent with no animation") + Log.i(TAG, "Starting intent with no animation") intentStarter(null) controller?.callOnIntentStartedOnMainThread(willAnimate = false) return } - Log.d(TAG, "Starting intent with a launch animation") val runner = Runner(controller) - val isOnKeyguard = callback.isOnKeyguard() + val hideKeyguardWithAnimation = callback.isOnKeyguard() && !showOverLockscreen - // Pass the RemoteAnimationAdapter to the intent starter only if we are not on the keyguard. - val animationAdapter = if (!isOnKeyguard) { + // Pass the RemoteAnimationAdapter to the intent starter only if we are not hiding the + // keyguard with the animation + val animationAdapter = if (!hideKeyguardWithAnimation) { RemoteAnimationAdapter( runner, ANIMATION_DURATION, @@ -149,9 +152,11 @@ class ActivityLaunchAnimator( val willAnimate = launchResult == ActivityManager.START_TASK_TO_FRONT || launchResult == ActivityManager.START_SUCCESS || - (launchResult == ActivityManager.START_DELIVERED_TO_TOP && isOnKeyguard) + (launchResult == ActivityManager.START_DELIVERED_TO_TOP && + hideKeyguardWithAnimation) - Log.d(TAG, "launchResult=$launchResult willAnimate=$willAnimate isOnKeyguard=$isOnKeyguard") + Log.i(TAG, "launchResult=$launchResult willAnimate=$willAnimate " + + "hideKeyguardWithAnimation=$hideKeyguardWithAnimation") controller.callOnIntentStartedOnMainThread(willAnimate) // If we expect an animation, post a timeout to cancel it in case the remote animation is @@ -160,7 +165,7 @@ class ActivityLaunchAnimator( runner.postTimeout() // Hide the keyguard using the launch animation instead of the default unlock animation. - if (isOnKeyguard) { + if (hideKeyguardWithAnimation) { callback.hideKeyguardWithAnimation(runner) } } @@ -424,13 +429,16 @@ class ActivityLaunchAnimator( nonApps: Array<out RemoteAnimationTarget>?, iCallback: IRemoteAnimationFinishedCallback? ) { - Log.d(TAG, "Remote animation started") + if (DEBUG) { + Log.d(TAG, "Remote animation started") + } + val window = apps?.firstOrNull { it.mode == RemoteAnimationTarget.MODE_OPENING } if (window == null) { - Log.d(TAG, "Aborting the animation as no window is opening") + Log.i(TAG, "Aborting the animation as no window is opening") removeTimeout() iCallback?.invoke() controller.onLaunchAnimationCancelled() @@ -500,7 +508,10 @@ class ActivityLaunchAnimator( val launchContainerOverlay = launchContainer.overlay animator.addListener(object : AnimatorListenerAdapter() { override fun onAnimationStart(animation: Animator?, isReverse: Boolean) { - Log.d(TAG, "Animation started") + if (DEBUG) { + Log.d(TAG, "Animation started") + } + callback.setBlursDisabledForAppLaunch(true) controller.onLaunchAnimationStart(isExpandingFullyAbove) @@ -511,7 +522,10 @@ class ActivityLaunchAnimator( } override fun onAnimationEnd(animation: Animator?) { - Log.d(TAG, "Animation ended") + if (DEBUG) { + Log.d(TAG, "Animation ended") + } + callback.setBlursDisabledForAppLaunch(false) iCallback?.invoke() controller.onLaunchAnimationEnd(isExpandingFullyAbove) @@ -686,7 +700,7 @@ class ActivityLaunchAnimator( return } - Log.d(TAG, "Remote animation timed out") + Log.i(TAG, "Remote animation timed out") timedOut = true controller.onLaunchAnimationCancelled() } @@ -696,7 +710,7 @@ class ActivityLaunchAnimator( return } - Log.d(TAG, "Remote animation was cancelled") + Log.i(TAG, "Remote animation was cancelled") cancelled = true removeTimeout() context.mainExecutor.execute { diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java index 7c81325d685f..6d088f090bcd 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java @@ -60,8 +60,16 @@ public interface ActivityStarter { */ void startActivity(Intent intent, boolean onlyProvisioned, boolean dismissShade, int flags); void startActivity(Intent intent, boolean dismissShade); + + default void startActivity(Intent intent, boolean dismissShade, + @Nullable ActivityLaunchAnimator.Controller animationController) { + startActivity(intent, dismissShade, animationController, + false /* showOverLockscreenWhenLocked */); + } + void startActivity(Intent intent, boolean dismissShade, - @Nullable ActivityLaunchAnimator.Controller animationController); + @Nullable ActivityLaunchAnimator.Controller animationController, + boolean showOverLockscreenWhenLocked); void startActivity(Intent intent, boolean onlyProvisioned, boolean dismissShade); void startActivity(Intent intent, boolean dismissShade, Callback callback); void postStartActivityDismissingKeyguard(Intent intent, int delay); diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml index 7c54b90f1915..a946318cb313 100644 --- a/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml +++ b/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml @@ -20,8 +20,8 @@ <com.android.keyguard.KeyguardPINView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:androidprv="http://schemas.android.com/apk/res-auto" - xmlns:tools="http://schemas.android.com/tools" - android:id="@+id/keyguard_pin_view" + xmlns:tools="http://schemas.android.com/tools" + android:id="@+id/keyguard_pin_view" android:layout_width="match_parent" android:layout_height="match_parent" androidprv:layout_maxWidth="@dimen/keyguard_security_width" @@ -52,7 +52,7 @@ androidprv:layout_constraintTop_toTopOf="parent" androidprv:layout_constraintBottom_toTopOf="@id/key1" - androidprv:layout_constraintVertical_bias="1.0"> + androidprv:layout_constraintVertical_bias="0.0"> <com.android.keyguard.PasswordTextView android:id="@+id/pinEntry" diff --git a/packages/SystemUI/res-keyguard/values-bn/strings.xml b/packages/SystemUI/res-keyguard/values-bn/strings.xml index 967d5cb05b4b..2908bae948d0 100644 --- a/packages/SystemUI/res-keyguard/values-bn/strings.xml +++ b/packages/SystemUI/res-keyguard/values-bn/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="6091488837901216962">"<xliff:g id="PERCENTAGE">%s</xliff:g> • চার্জ সাময়িকভাবে বন্ধ করা আছে"</string> + <string name="keyguard_plugged_in_charging_limited" msgid="6091488837901216962">"<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-de/strings.xml b/packages/SystemUI/res-keyguard/values-de/strings.xml index ac5a58e3efcc..5be929831b34 100644 --- a/packages/SystemUI/res-keyguard/values-de/strings.xml +++ b/packages/SystemUI/res-keyguard/values-de/strings.xml @@ -38,7 +38,7 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Wird geladen"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Wird schnell geladen"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Wird langsam geladen"</string> - <string name="keyguard_plugged_in_charging_limited" msgid="6091488837901216962">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Aufladen vorübergehend eingeschränkt"</string> + <string name="keyguard_plugged_in_charging_limited" msgid="6091488837901216962">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Laden vorübergehend eingeschränkt"</string> <string name="keyguard_low_battery" msgid="1868012396800230904">"Ladegerät anschließen."</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Zum Entsperren die Menütaste drücken."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Netzwerk gesperrt"</string> diff --git a/packages/SystemUI/res-keyguard/values-fr-rCA/strings.xml b/packages/SystemUI/res-keyguard/values-fr-rCA/strings.xml index 38a7139a9634..f8c8cadbbb03 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="6091488837901216962">"<xliff:g id="PERCENTAGE">%s</xliff:g> • recharge temporairement limitée"</string> + <string name="keyguard_plugged_in_charging_limited" msgid="6091488837901216962">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Recharge temporairement limitée"</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-gu/strings.xml b/packages/SystemUI/res-keyguard/values-gu/strings.xml index a41cce7b74bf..e87ee927a892 100644 --- a/packages/SystemUI/res-keyguard/values-gu/strings.xml +++ b/packages/SystemUI/res-keyguard/values-gu/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="6091488837901216962">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ચાર્જિંગ હંગામીરૂપે પ્રતિબંધિત કરવામાં આવ્યું છે"</string> + <string name="keyguard_plugged_in_charging_limited" msgid="6091488837901216962">"<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-in/strings.xml b/packages/SystemUI/res-keyguard/values-in/strings.xml index 62370756e606..edf8ab2b2b23 100644 --- a/packages/SystemUI/res-keyguard/values-in/strings.xml +++ b/packages/SystemUI/res-keyguard/values-in/strings.xml @@ -38,7 +38,7 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Mengisi daya"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Mengisi daya dengan cepat"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Mengisi daya dengan lambat"</string> - <string name="keyguard_plugged_in_charging_limited" msgid="6091488837901216962">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Pengisian daya dibatasi untuk sementara"</string> + <string name="keyguard_plugged_in_charging_limited" msgid="6091488837901216962">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Pengisian daya dibatasi sementara"</string> <string name="keyguard_low_battery" msgid="1868012396800230904">"Hubungkan pengisi daya."</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Tekan Menu untuk membuka kunci."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Jaringan terkunci"</string> diff --git a/packages/SystemUI/res-keyguard/values-iw/strings.xml b/packages/SystemUI/res-keyguard/values-iw/strings.xml index 4e7ac49a25d1..bbc5aa0d157c 100644 --- a/packages/SystemUI/res-keyguard/values-iw/strings.xml +++ b/packages/SystemUI/res-keyguard/values-iw/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="6091488837901216962">"<xliff:g id="PERCENTAGE">%s</xliff:g> • הטעינה מוגבלת זמנית"</string> + <string name="keyguard_plugged_in_charging_limited" msgid="6091488837901216962">"<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-ja/strings.xml b/packages/SystemUI/res-keyguard/values-ja/strings.xml index a1d58b789712..60b52ea6bf1e 100644 --- a/packages/SystemUI/res-keyguard/values-ja/strings.xml +++ b/packages/SystemUI/res-keyguard/values-ja/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="6091488837901216962">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 充電は一時的に制限されています"</string> + <string name="keyguard_plugged_in_charging_limited" msgid="6091488837901216962">"<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-km/strings.xml b/packages/SystemUI/res-keyguard/values-km/strings.xml index a0ae88a4557d..eca49577042f 100644 --- a/packages/SystemUI/res-keyguard/values-km/strings.xml +++ b/packages/SystemUI/res-keyguard/values-km/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="6091488837901216962">"<xliff:g id="PERCENTAGE">%s</xliff:g> • បានដាក់កម្រិតការសាកថ្មជាបណ្ដោះអាសន្ន"</string> + <string name="keyguard_plugged_in_charging_limited" msgid="6091488837901216962">"<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 98b1af6b8710..5cbe02e3f7f5 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="6091488837901216962">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ಚಾರ್ಜಿಂಗ್ ತಾತ್ಕಾಲಿಕವಾಗಿ ಸೀಮಿತವಾಗಿದೆ"</string> + <string name="keyguard_plugged_in_charging_limited" msgid="6091488837901216962">"<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-lo/strings.xml b/packages/SystemUI/res-keyguard/values-lo/strings.xml index 65b0409ecb6a..b6fd676cb29b 100644 --- a/packages/SystemUI/res-keyguard/values-lo/strings.xml +++ b/packages/SystemUI/res-keyguard/values-lo/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="6091488837901216962">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ຈຳກັດການສາກໄຟຊົ່ວຄາວ"</string> + <string name="keyguard_plugged_in_charging_limited" msgid="6091488837901216962">"<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 110b711faa4a..b4f709d3eb4e 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="6091488837901216962">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Цэнэглэхийг түр зуур хязгаарласан"</string> + <string name="keyguard_plugged_in_charging_limited" msgid="6091488837901216962">"<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-ms/strings.xml b/packages/SystemUI/res-keyguard/values-ms/strings.xml index 82579d437806..6092cc7f8cbc 100644 --- a/packages/SystemUI/res-keyguard/values-ms/strings.xml +++ b/packages/SystemUI/res-keyguard/values-ms/strings.xml @@ -38,7 +38,7 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Mengecas"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Mengecas dengan cepat"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Mengecas dengan perlahan"</string> - <string name="keyguard_plugged_in_charging_limited" msgid="6091488837901216962">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Pengecasan terhad buat sementara waktu"</string> + <string name="keyguard_plugged_in_charging_limited" msgid="6091488837901216962">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Pengecasan terhad sementara"</string> <string name="keyguard_low_battery" msgid="1868012396800230904">"Sambungkan pengecas anda."</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Tekan Menu untuk membuka kunci."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Rangkaian dikunci"</string> diff --git a/packages/SystemUI/res-keyguard/values-sl/strings.xml b/packages/SystemUI/res-keyguard/values-sl/strings.xml index 371d993d3a47..bb388ea4c577 100644 --- a/packages/SystemUI/res-keyguard/values-sl/strings.xml +++ b/packages/SystemUI/res-keyguard/values-sl/strings.xml @@ -38,7 +38,7 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • polnjenje"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • hitro polnjenje"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • počasno polnjenje"</string> - <string name="keyguard_plugged_in_charging_limited" msgid="6091488837901216962">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Začasno omejeno polnjenje"</string> + <string name="keyguard_plugged_in_charging_limited" msgid="6091488837901216962">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Polnjenje začasno omejeno"</string> <string name="keyguard_low_battery" msgid="1868012396800230904">"Priključite napajalnik."</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Če želite odkleniti, pritisnite meni."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Omrežje je zaklenjeno"</string> diff --git a/packages/SystemUI/res-keyguard/values-ta/strings.xml b/packages/SystemUI/res-keyguard/values-ta/strings.xml index adec7fe2127b..ef36fdd8298d 100644 --- a/packages/SystemUI/res-keyguard/values-ta/strings.xml +++ b/packages/SystemUI/res-keyguard/values-ta/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="6091488837901216962">"<xliff:g id="PERCENTAGE">%s</xliff:g> • சார்ஜாவது தற்காலிகமாக வரம்பிடப்பட்டுள்ளது"</string> + <string name="keyguard_plugged_in_charging_limited" msgid="6091488837901216962">"<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 03bb5ccb333b..f9544af5f6d1 100644 --- a/packages/SystemUI/res-keyguard/values-te/strings.xml +++ b/packages/SystemUI/res-keyguard/values-te/strings.xml @@ -40,7 +40,7 @@ <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="6091488837901216962">"<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_instructions_when_pattern_disabled" msgid="8448804180089936954">"అన్లాక్ చేయడానికి మెనూను నొక్కండి."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"నెట్వర్క్ లాక్ చేయబడింది"</string> <string name="keyguard_missing_sim_message_short" msgid="704159478161444907">"SIM కార్డ్ లేదు"</string> <string name="keyguard_missing_sim_instructions" msgid="1162120926141335918">"SIM కార్డ్ని చొప్పించండి."</string> @@ -83,8 +83,8 @@ <string name="kg_invalid_sim_puk_hint" msgid="5319756880543857694">"PUK కోడ్ అనేది 8 లేదా అంతకంటే ఎక్కువ సంఖ్యలు ఉండాలి."</string> <string name="kg_invalid_puk" msgid="1774337070084931186">"సరైన PUK కోడ్ను మళ్లీ నమోదు చేయండి. ఎక్కువసార్లు ప్రయత్నించడం వలన SIM శాశ్వతంగా నిలిపివేయబడుతుంది."</string> <string name="kg_login_too_many_attempts" msgid="4519957179182578690">"నమూనాని చాలా ఎక్కువసార్లు గీసారు"</string> - <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="544687656831558971">"మీరు మీ పిన్ను <xliff:g id="NUMBER_0">%1$d</xliff:g> సార్లు తప్పుగా టైప్ చేసారు. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> సెకన్లలో మళ్లీ ప్రయత్నించండి."</string> - <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"మీరు మీ పాస్వర్డ్ను <xliff:g id="NUMBER_0">%1$d</xliff:g> సార్లు తప్పుగా టైప్ చేసారు. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> సెకన్లలో మళ్లీ ప్రయత్నించండి."</string> + <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="544687656831558971">"మీరు మీ పిన్ను <xliff:g id="NUMBER_0">%1$d</xliff:g> సార్లు తప్పుగా టైప్ చేశారు. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> సెకన్లలో మళ్లీ ప్రయత్నించండి."</string> + <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"మీరు మీ పాస్వర్డ్ను <xliff:g id="NUMBER_0">%1$d</xliff:g> సార్లు తప్పుగా టైప్ చేశారు. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> సెకన్లలో మళ్లీ ప్రయత్నించండి."</string> <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"మీరు మీ అన్లాక్ నమూనాను <xliff:g id="NUMBER_0">%1$d</xliff:g> సార్లు తప్పుగా గీసారు. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> సెకన్లలో మళ్లీ ప్రయత్నించండి."</string> <string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"SIM పిన్ కోడ్ తప్పు, ఇప్పుడు మీ డివైజ్ను అన్లాక్ చేయాలంటే, మీరు తప్పనిసరిగా మీ క్యారియర్ను సంప్రదించాలి."</string> <plurals name="kg_password_wrong_pin_code" formatted="false" msgid="7030584350995485026"> diff --git a/packages/SystemUI/res-keyguard/values-vi/strings.xml b/packages/SystemUI/res-keyguard/values-vi/strings.xml index 3c3972c2a1bc..be10a50839a8 100644 --- a/packages/SystemUI/res-keyguard/values-vi/strings.xml +++ b/packages/SystemUI/res-keyguard/values-vi/strings.xml @@ -38,7 +38,7 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Đang sạc"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Đang sạc nhanh"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Đang sạc chậm"</string> - <string name="keyguard_plugged_in_charging_limited" msgid="6091488837901216962">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Khả năng sạc tạm thời bị hạn chế"</string> + <string name="keyguard_plugged_in_charging_limited" msgid="6091488837901216962">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Chức năng sạc tạm thời bị hạn chế"</string> <string name="keyguard_low_battery" msgid="1868012396800230904">"Kết nối bộ sạc của bạn."</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Nhấn vào Menu để mở khóa."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Mạng đã bị khóa"</string> diff --git a/packages/SystemUI/res-keyguard/values-zh-rTW/strings.xml b/packages/SystemUI/res-keyguard/values-zh-rTW/strings.xml index d950d4013455..621fe4902938 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="6091488837901216962">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 充電功能暫時受到限制"</string> + <string name="keyguard_plugged_in_charging_limited" msgid="6091488837901216962">"<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/donottranslate.xml b/packages/SystemUI/res-keyguard/values/donottranslate.xml index 878c0172c3ff..052d329ee63c 100644 --- a/packages/SystemUI/res-keyguard/values/donottranslate.xml +++ b/packages/SystemUI/res-keyguard/values/donottranslate.xml @@ -21,6 +21,9 @@ <!-- Skeleton string format for displaying the date when an alarm is set. --> <string name="abbrev_wday_month_day_no_year_alarm">EEEMMMd</string> + <!-- Skeleton string format for displaying the date shorter. --> + <string name="abbrev_month_day_no_year">MMMd</string> + <!-- Skeleton string format for displaying the time in 12-hour format. --> <string name="clock_12hr_format">hm</string> diff --git a/packages/SystemUI/res-keyguard/values/styles.xml b/packages/SystemUI/res-keyguard/values/styles.xml index ea769c6a6410..871b1c4eb3f6 100644 --- a/packages/SystemUI/res-keyguard/values/styles.xml +++ b/packages/SystemUI/res-keyguard/values/styles.xml @@ -62,8 +62,8 @@ <item name="android:src">@drawable/ic_backspace_24dp</item> </style> <style name="NumPadKey.Enter"> - <item name="android:colorControlNormal">?android:attr/textColorSecondary</item> - <item name="android:src">@drawable/ic_keyboard_tab_36dp</item> + <item name="android:colorControlNormal">?android:attr/textColorSecondary</item> + <item name="android:src">@drawable/ic_keyboard_tab_36dp</item> </style> <style name="Widget.TextView.NumPadKey.Klondike" parent="@android:style/Widget.DeviceDefault.TextView"> diff --git a/packages/SystemUI/res/drawable/fingerprint_dialog_error_to_fp.xml b/packages/SystemUI/res/drawable/fingerprint_dialog_error_to_fp.xml index 33263a9131a0..0ae5dc745478 100644 --- a/packages/SystemUI/res/drawable/fingerprint_dialog_error_to_fp.xml +++ b/packages/SystemUI/res/drawable/fingerprint_dialog_error_to_fp.xml @@ -1,413 +1,242 @@ -<?xml version="1.0" encoding="utf-8"?><!-- - ~ Copyright (C) 2018 The Android Open Source Project - ~ - ~ Licensed under the Apache License, Version 2.0 (the "License"); - ~ you may not use this file except in compliance with the License. - ~ You may obtain a copy of the License at - ~ - ~ http://www.apache.org/licenses/LICENSE-2.0 - ~ - ~ Unless required by applicable law or agreed to in writing, software - ~ distributed under the License is distributed on an "AS IS" BASIS, - ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ~ See the License for the specific language governing permissions and - ~ limitations under the License - --> +<?xml version="1.0" encoding="utf-8" ?> +<!-- Copyright (C) 2021 The Android Open Source Project + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> <animated-vector xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:aapt="http://schemas.android.com/aapt"> + xmlns:aapt="http://schemas.android.com/aapt"> <aapt:attr name="android:drawable"> - <vector - android:width="60dp" - android:height="60dp" - android:viewportHeight="60" - android:viewportWidth="60"> + <vector android:height="60dp" android:width="60dp" android:viewportHeight="60" + android:viewportWidth="60"> <group android:name="_R_G"> - <group - android:name="_R_G_L_1_G_N_4_T_0" - android:translateX="30" - android:translateY="30"> - <group - android:name="_R_G_L_1_G" - android:pivotX="114" - android:pivotY="114" - android:scaleX="0.42200000000000004" - android:scaleY="0.42200000000000004" - android:translateX="-114" - android:translateY="-114"> - <path - android:name="_R_G_L_1_G_D_0_P_0" - android:pathData=" M79.63 67.24 C79.63,67.24 111.5,47.42 147.83,67.24 " - android:strokeAlpha="1" - android:strokeColor="@color/biometric_dialog_accent" - android:strokeLineCap="round" - android:strokeLineJoin="round" - android:strokeWidth="5.5" - android:trimPathEnd="0" - android:trimPathOffset="0" - android:trimPathStart="0" /> - <path - android:name="_R_G_L_1_G_D_1_P_0" - android:pathData=" M64.27 98.07 C64.27,98.07 80.13,73.02 113.98,73.02 C147.83,73.02 163.56,97.26 163.56,97.26 " - android:strokeAlpha="1" - android:strokeColor="@color/biometric_dialog_accent" - android:strokeLineCap="round" - android:strokeLineJoin="round" - android:strokeWidth="5.5" - android:trimPathEnd="0" - android:trimPathOffset="0" - android:trimPathStart="0" /> - <path - android:name="_R_G_L_1_G_D_2_P_0" - android:pathData=" M72.53 151.07 C72.53,151.07 62.46,122.89 76.16,105.55 C89.86,88.21 106.72,86.73 113.98,86.73 C121.08,86.73 153.51,90.62 158.7,125.87 C159.14,128.82 158.8,132.88 157.18,136.09 C154.88,140.63 150.62,143.63 145.85,143.97 C133.78,144.85 129.76,137.92 129.26,128.49 C128.88,121.19 122.49,115.35 113.15,115.35 C102.91,115.35 95.97,126.69 99.77,139.74 C103.57,152.78 111.33,163.85 130.32,169.13 " - android:strokeAlpha="1" - android:strokeColor="@color/biometric_dialog_accent" - android:strokeLineCap="round" - android:strokeLineJoin="round" - android:strokeWidth="5.5" - android:trimPathEnd="0" - android:trimPathOffset="0" - android:trimPathStart="0" /> - <path - android:name="_R_G_L_1_G_D_3_P_0" - android:pathData=" M100.6 167.84 C100.6,167.84 82.76,152.1 83.75,130.31 C84.75,108.53 102.58,100.7 113.73,100.7 C124.87,100.7 144.19,108.56 144.19,130.01 " - android:strokeAlpha="1" - android:strokeColor="@color/biometric_dialog_accent" - android:strokeLineCap="round" - android:strokeLineJoin="round" - android:strokeWidth="5.5" - android:trimPathEnd="0" - android:trimPathOffset="0" - android:trimPathStart="0" /> - <path - android:name="_R_G_L_1_G_D_4_P_0" - android:pathData=" M113.73 129.17 C113.73,129.17 113.15,161.33 149.15,156.58 " - android:strokeAlpha="1" - android:strokeColor="@color/biometric_dialog_accent" - android:strokeLineCap="round" - android:strokeLineJoin="round" - android:strokeWidth="5.5" - android:trimPathEnd="0" - android:trimPathOffset="0" - android:trimPathStart="0" /> + <group android:name="_R_G_L_1_G" android:translateX="-0.05000000000000071"> + <group android:name="_R_G_L_1_G_D_0_P_0_G_0_T_0" android:translateX="30" + android:translateY="38.75" android:scaleX="1" android:scaleY="1"> + <path android:name="_R_G_L_1_G_D_0_P_0" + android:fillColor="@color/biometric_dialog_error" + android:fillAlpha="1" android:fillType="nonZero" + android:pathData=" M-1.2 -1.25 C-1.2,-1.25 1.2,-1.25 1.2,-1.25 C1.2,-1.25 1.2,1.25 1.2,1.25 C1.2,1.25 -1.2,1.25 -1.2,1.25 C-1.2,1.25 -1.2,-1.25 -1.2,-1.25c "/> </group> - </group> - <group - android:name="_R_G_L_0_G_N_4_T_0" - android:translateX="30" - android:translateY="30"> - <group - android:name="_R_G_L_0_G" - android:translateX="-30.05" - android:translateY="-30"> - <group - android:name="_R_G_L_0_G_D_0_P_0_G_0_T_0" - android:scaleX="1" - android:scaleY="1" - android:translateX="30" - android:translateY="38.75"> - <path - android:name="_R_G_L_0_G_D_0_P_0" - android:fillAlpha="1" - android:fillColor="@color/biometric_dialog_error" - android:fillType="nonZero" - android:pathData=" M-1.2 -1.25 C-1.2,-1.25 1.2,-1.25 1.2,-1.25 C1.2,-1.25 1.2,1.25 1.2,1.25 C1.2,1.25 -1.2,1.25 -1.2,1.25 C-1.2,1.25 -1.2,-1.25 -1.2,-1.25c " /> - </group> - <group - android:name="_R_G_L_0_G_D_1_P_0_G_0_T_0" - android:pivotX="0.002" - android:pivotY="7.488" - android:scaleX="1" - android:scaleY="1" - android:translateX="30" - android:translateY="25"> - <path - android:name="_R_G_L_0_G_D_1_P_0" - android:fillAlpha="1" - android:fillColor="@color/biometric_dialog_error" - android:fillType="nonZero" - android:pathData=" M-1.2 -7.5 C-1.2,-7.5 1.2,-7.5 1.2,-7.5 C1.2,-7.5 1.2,7.5 1.2,7.5 C1.2,7.5 -1.2,7.5 -1.2,7.5 C-1.2,7.5 -1.2,-7.5 -1.2,-7.5c " /> - </group> - <path - android:name="_R_G_L_0_G_D_2_P_0" - android:pathData=" M30 6.2 C16.9,6.2 6.3,16.8 6.3,30 C6.3,43.2 16.9,53.8 30,53.8 C43.1,53.8 53.8,43.2 53.8,30 C53.8,16.8 43.1,6.2 30,6.2c " - android:strokeAlpha="1" - android:strokeColor="@color/biometric_dialog_error" - android:strokeLineCap="round" - android:strokeLineJoin="round" - android:strokeWidth="2.5" - android:trimPathEnd="1" - android:trimPathOffset="0" - android:trimPathStart="0" /> + <group android:name="_R_G_L_1_G_D_1_P_0_G_0_T_0" android:translateX="30" + android:translateY="25" android:pivotX="0.002" android:pivotY="7.488" + android:scaleX="1" android:scaleY="1"> + <path android:name="_R_G_L_1_G_D_1_P_0" + android:fillColor="@color/biometric_dialog_error" + android:fillAlpha="1" android:fillType="nonZero" + android:pathData=" M-1.2 -7.5 C-1.2,-7.5 1.2,-7.5 1.2,-7.5 C1.2,-7.5 1.2,7.5 1.2,7.5 C1.2,7.5 -1.2,7.5 -1.2,7.5 C-1.2,7.5 -1.2,-7.5 -1.2,-7.5c "/> </group> + <path android:name="_R_G_L_1_G_D_2_P_0" + android:strokeColor="@color/biometric_dialog_error" + android:strokeLineCap="round" android:strokeLineJoin="round" + android:strokeWidth="2.5" android:strokeAlpha="1" + android:trimPathStart="0" android:trimPathEnd="1" + android:trimPathOffset="0" + android:pathData=" M30 6.2 C16.9,6.2 6.3,16.8 6.3,30 C6.3,43.2 16.9,53.8 30,53.8 C43.1,53.8 53.8,43.2 53.8,30 C53.8,16.8 43.1,6.2 30,6.2c "/> + </group> + <group android:name="_R_G_L_0_G" android:translateX="-10.325" + android:translateY="-10.25"> + <path android:name="_R_G_L_0_G_D_0_P_0" + android:strokeColor="@color/biometric_dialog_accent" + android:strokeLineCap="round" android:strokeLineJoin="round" + android:strokeWidth="2" android:strokeAlpha="1" android:trimPathStart="0" + android:trimPathEnd="0" android:trimPathOffset="0" + android:pathData=" M31.41 48.43 C30.78,46.69 30.78,44.91 30.78,44.91 C30.78,40.09 34.88,36.16 40.32,36.16 C45.77,36.16 49.87,40.09 49.87,44.91 C49.87,44.91 49.87,45.17 49.87,45.17 C49.87,46.97 48.41,48.43 46.61,48.43 C45.28,48.43 44.09,47.63 43.6,46.39 C43.6,46.39 42.51,43.66 42.51,43.66 C42.02,42.42 40.82,41.61 39.49,41.61 C37.69,41.61 36.23,43.07 36.23,44.87 C36.23,47.12 37.26,49.26 39.02,50.67 C39.02,50.67 39.64,51.16 39.64,51.16 "/> + <path android:name="_R_G_L_0_G_D_1_P_0" + android:strokeColor="@color/biometric_dialog_accent" + android:strokeLineCap="round" android:strokeLineJoin="round" + android:strokeWidth="2" android:strokeAlpha="1" android:trimPathStart="0" + android:trimPathEnd="0" android:trimPathOffset="0" + android:pathData=" M32.14 27.3 C34.5,26 37.31,25.25 40.33,25.25 C43.34,25.25 46.15,26 48.51,27.3 "/> + <path android:name="_R_G_L_0_G_D_2_P_0" + android:strokeColor="@color/biometric_dialog_accent" + android:strokeLineCap="round" android:strokeLineJoin="round" + android:strokeWidth="2" android:strokeAlpha="1" android:trimPathStart="0" + android:trimPathEnd="0" android:trimPathOffset="0" + android:pathData=" M29.42 36.16 C31.35,32.94 35.51,30.71 40.33,30.71 C45.14,30.71 49.3,32.94 51.23,36.16 "/> + <path android:name="_R_G_L_0_G_D_3_P_0" + android:strokeColor="@color/biometric_dialog_accent" + android:strokeLineCap="round" android:strokeLineJoin="round" + android:strokeWidth="2" android:strokeAlpha="1" android:trimPathStart="0" + android:trimPathEnd="0" android:trimPathOffset="0" + android:pathData=" M47.14 52.52 C45.33,54.21 42.94,55.25 40.33,55.25 C37.71,55.25 35.32,54.21 33.51,52.52 "/> </group> </group> - <group android:name="time_group" /> + <group android:name="time_group"/> </vector> </aapt:attr> - <target android:name="_R_G_L_1_G_D_0_P_0"> + <target android:name="_R_G_L_1_G_D_0_P_0_G_0_T_0"> <aapt:attr name="android:animation"> <set android:ordering="together"> - <objectAnimator - android:duration="83" - android:propertyName="trimPathEnd" - android:startOffset="0" - android:valueFrom="0" - android:valueTo="0" - android:valueType="floatType"> + <objectAnimator android:propertyName="scaleX" android:duration="67" + android:startOffset="0" android:valueFrom="1" android:valueTo="1.1" + android:valueType="floatType"> <aapt:attr name="android:interpolator"> - <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" /> + <pathInterpolator android:pathData="M 0.0,0.0 c0.853,0 0.6,1 1.0,1.0"/> </aapt:attr> </objectAnimator> - <objectAnimator - android:duration="250" - android:propertyName="trimPathEnd" - android:startOffset="83" - android:valueFrom="0" - android:valueTo="1" - android:valueType="floatType"> + <objectAnimator android:propertyName="scaleY" android:duration="67" + android:startOffset="0" android:valueFrom="1" android:valueTo="1.1" + android:valueType="floatType"> <aapt:attr name="android:interpolator"> - <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" /> + <pathInterpolator android:pathData="M 0.0,0.0 c0.853,0 0.6,1 1.0,1.0"/> </aapt:attr> </objectAnimator> - </set> - </aapt:attr> - </target> - <target android:name="_R_G_L_1_G_D_1_P_0"> - <aapt:attr name="android:animation"> - <set android:ordering="together"> - <objectAnimator - android:duration="83" - android:propertyName="trimPathEnd" - android:startOffset="0" - android:valueFrom="0" - android:valueTo="0" - android:valueType="floatType"> + <objectAnimator android:propertyName="scaleX" android:duration="100" + android:startOffset="67" android:valueFrom="1.1" android:valueTo="0" + android:valueType="floatType"> <aapt:attr name="android:interpolator"> - <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" /> + <pathInterpolator android:pathData="M 0.0,0.0 c0.8,0 0.92,1.06 1.0,1.0"/> </aapt:attr> </objectAnimator> - <objectAnimator - android:duration="250" - android:propertyName="trimPathEnd" - android:startOffset="83" - android:valueFrom="0" - android:valueTo="1" - android:valueType="floatType"> + <objectAnimator android:propertyName="scaleY" android:duration="100" + android:startOffset="67" android:valueFrom="1.1" android:valueTo="0" + android:valueType="floatType"> <aapt:attr name="android:interpolator"> - <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" /> + <pathInterpolator android:pathData="M 0.0,0.0 c0.8,0 0.92,1.06 1.0,1.0"/> </aapt:attr> </objectAnimator> </set> </aapt:attr> </target> - <target android:name="_R_G_L_1_G_D_2_P_0"> + <target android:name="_R_G_L_1_G_D_1_P_0_G_0_T_0"> <aapt:attr name="android:animation"> <set android:ordering="together"> - <objectAnimator - android:duration="83" - android:propertyName="trimPathEnd" - android:startOffset="0" - android:valueFrom="0" - android:valueTo="0" - android:valueType="floatType"> + <objectAnimator android:propertyName="scaleX" android:duration="67" + android:startOffset="0" android:valueFrom="1" android:valueTo="1" + android:valueType="floatType"> <aapt:attr name="android:interpolator"> - <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" /> + <pathInterpolator android:pathData="M 0.0,0.0 c0.8,0 0.659,1 1.0,1.0"/> </aapt:attr> </objectAnimator> - <objectAnimator - android:duration="250" - android:propertyName="trimPathEnd" - android:startOffset="83" - android:valueFrom="0" - android:valueTo="1" - android:valueType="floatType"> + <objectAnimator android:propertyName="scaleY" android:duration="67" + android:startOffset="0" android:valueFrom="1" android:valueTo="1.1" + android:valueType="floatType"> <aapt:attr name="android:interpolator"> - <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" /> + <pathInterpolator android:pathData="M 0.0,0.0 c0.8,0 0.6,1 1.0,1.0"/> </aapt:attr> </objectAnimator> - </set> - </aapt:attr> - </target> - <target android:name="_R_G_L_1_G_D_3_P_0"> - <aapt:attr name="android:animation"> - <set android:ordering="together"> - <objectAnimator - android:duration="83" - android:propertyName="trimPathEnd" - android:startOffset="0" - android:valueFrom="0" - android:valueTo="0" - android:valueType="floatType"> + <objectAnimator android:propertyName="scaleX" android:duration="100" + android:startOffset="67" android:valueFrom="1" android:valueTo="1" + android:valueType="floatType"> <aapt:attr name="android:interpolator"> - <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" /> + <pathInterpolator android:pathData="M 0.0,0.0 c0.8,0 0.6,1 1.0,1.0"/> </aapt:attr> </objectAnimator> - <objectAnimator - android:duration="250" - android:propertyName="trimPathEnd" - android:startOffset="83" - android:valueFrom="0" - android:valueTo="1" - android:valueType="floatType"> + <objectAnimator android:propertyName="scaleY" android:duration="100" + android:startOffset="67" android:valueFrom="1.1" android:valueTo="0" + android:valueType="floatType"> <aapt:attr name="android:interpolator"> - <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" /> + <pathInterpolator android:pathData="M 0.0,0.0 c0.8,0 0.92,1.096 1.0,1.0"/> </aapt:attr> </objectAnimator> </set> </aapt:attr> </target> - <target android:name="_R_G_L_1_G_D_4_P_0"> + <target android:name="_R_G_L_1_G_D_2_P_0"> <aapt:attr name="android:animation"> <set android:ordering="together"> - <objectAnimator - android:duration="83" - android:propertyName="trimPathEnd" - android:startOffset="0" - android:valueFrom="0" - android:valueTo="0" - android:valueType="floatType"> + <objectAnimator android:propertyName="trimPathEnd" android:duration="67" + android:startOffset="0" android:valueFrom="1" android:valueTo="1" + android:valueType="floatType"> <aapt:attr name="android:interpolator"> - <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" /> + <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0"/> </aapt:attr> </objectAnimator> - <objectAnimator - android:duration="250" - android:propertyName="trimPathEnd" - android:startOffset="83" - android:valueFrom="0" - android:valueTo="1" - android:valueType="floatType"> + <objectAnimator android:propertyName="trimPathEnd" android:duration="133" + android:startOffset="67" android:valueFrom="1" android:valueTo="0" + android:valueType="floatType"> <aapt:attr name="android:interpolator"> - <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" /> + <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0"/> </aapt:attr> </objectAnimator> </set> </aapt:attr> </target> - <target android:name="_R_G_L_0_G_D_0_P_0_G_0_T_0"> + <target android:name="_R_G_L_0_G_D_0_P_0"> <aapt:attr name="android:animation"> <set android:ordering="together"> - <objectAnimator - android:duration="67" - android:propertyName="scaleX" - android:startOffset="0" - android:valueFrom="1" - android:valueTo="1.1" - android:valueType="floatType"> - <aapt:attr name="android:interpolator"> - <pathInterpolator android:pathData="M 0.0,0.0 c0.853,0 0.6,1 1.0,1.0" /> - </aapt:attr> - </objectAnimator> - <objectAnimator - android:duration="67" - android:propertyName="scaleY" - android:startOffset="0" - android:valueFrom="1" - android:valueTo="1.1" - android:valueType="floatType"> - <aapt:attr name="android:interpolator"> - <pathInterpolator android:pathData="M 0.0,0.0 c0.853,0 0.6,1 1.0,1.0" /> - </aapt:attr> - </objectAnimator> - <objectAnimator - android:duration="100" - android:propertyName="scaleX" - android:startOffset="67" - android:valueFrom="1.1" - android:valueTo="0" - android:valueType="floatType"> + <objectAnimator android:propertyName="trimPathEnd" android:duration="83" + android:startOffset="0" android:valueFrom="0" android:valueTo="0" + android:valueType="floatType"> <aapt:attr name="android:interpolator"> - <pathInterpolator android:pathData="M 0.0,0.0 c0.8,0 0.92,1.06 1.0,1.0" /> + <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0"/> </aapt:attr> </objectAnimator> - <objectAnimator - android:duration="100" - android:propertyName="scaleY" - android:startOffset="67" - android:valueFrom="1.1" - android:valueTo="0" - android:valueType="floatType"> + <objectAnimator android:propertyName="trimPathEnd" android:duration="250" + android:startOffset="83" android:valueFrom="0" android:valueTo="1" + android:valueType="floatType"> <aapt:attr name="android:interpolator"> - <pathInterpolator android:pathData="M 0.0,0.0 c0.8,0 0.92,1.06 1.0,1.0" /> + <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0"/> </aapt:attr> </objectAnimator> </set> </aapt:attr> </target> - <target android:name="_R_G_L_0_G_D_1_P_0_G_0_T_0"> + <target android:name="_R_G_L_0_G_D_1_P_0"> <aapt:attr name="android:animation"> <set android:ordering="together"> - <objectAnimator - android:duration="67" - android:propertyName="scaleX" - android:startOffset="0" - android:valueFrom="1" - android:valueTo="1" - android:valueType="floatType"> + <objectAnimator android:propertyName="trimPathEnd" android:duration="83" + android:startOffset="0" android:valueFrom="0" android:valueTo="0" + android:valueType="floatType"> <aapt:attr name="android:interpolator"> - <pathInterpolator android:pathData="M 0.0,0.0 c0.8,0 0.659,1 1.0,1.0" /> + <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0"/> </aapt:attr> </objectAnimator> - <objectAnimator - android:duration="67" - android:propertyName="scaleY" - android:startOffset="0" - android:valueFrom="1" - android:valueTo="1.1" - android:valueType="floatType"> + <objectAnimator android:propertyName="trimPathEnd" android:duration="250" + android:startOffset="83" android:valueFrom="0" android:valueTo="1" + android:valueType="floatType"> <aapt:attr name="android:interpolator"> - <pathInterpolator android:pathData="M 0.0,0.0 c0.8,0 0.6,1 1.0,1.0" /> + <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0"/> </aapt:attr> </objectAnimator> - <objectAnimator - android:duration="100" - android:propertyName="scaleX" - android:startOffset="67" - android:valueFrom="1" - android:valueTo="1" - android:valueType="floatType"> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_0_G_D_2_P_0"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator android:propertyName="trimPathEnd" android:duration="83" + android:startOffset="0" android:valueFrom="0" android:valueTo="0" + android:valueType="floatType"> <aapt:attr name="android:interpolator"> - <pathInterpolator android:pathData="M 0.0,0.0 c0.8,0 0.6,1 1.0,1.0" /> + <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0"/> </aapt:attr> </objectAnimator> - <objectAnimator - android:duration="100" - android:propertyName="scaleY" - android:startOffset="67" - android:valueFrom="1.1" - android:valueTo="0" - android:valueType="floatType"> + <objectAnimator android:propertyName="trimPathEnd" android:duration="250" + android:startOffset="83" android:valueFrom="0" android:valueTo="1" + android:valueType="floatType"> <aapt:attr name="android:interpolator"> - <pathInterpolator android:pathData="M 0.0,0.0 c0.8,0 0.92,1.096 1.0,1.0" /> + <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0"/> </aapt:attr> </objectAnimator> </set> </aapt:attr> </target> - <target android:name="_R_G_L_0_G_D_2_P_0"> + <target android:name="_R_G_L_0_G_D_3_P_0"> <aapt:attr name="android:animation"> <set android:ordering="together"> - <objectAnimator - android:duration="67" - android:propertyName="trimPathEnd" - android:startOffset="0" - android:valueFrom="1" - android:valueTo="1" - android:valueType="floatType"> + <objectAnimator android:propertyName="trimPathEnd" android:duration="83" + android:startOffset="0" android:valueFrom="0" android:valueTo="0" + android:valueType="floatType"> <aapt:attr name="android:interpolator"> - <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" /> + <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0"/> </aapt:attr> </objectAnimator> - <objectAnimator - android:duration="133" - android:propertyName="trimPathEnd" - android:startOffset="67" - android:valueFrom="1" - android:valueTo="0" - android:valueType="floatType"> + <objectAnimator android:propertyName="trimPathEnd" android:duration="250" + android:startOffset="83" android:valueFrom="0" android:valueTo="1" + android:valueType="floatType"> <aapt:attr name="android:interpolator"> - <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" /> + <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0"/> </aapt:attr> </objectAnimator> </set> @@ -416,14 +245,10 @@ <target android:name="time_group"> <aapt:attr name="android:animation"> <set android:ordering="together"> - <objectAnimator - android:duration="350" - android:propertyName="translateX" - android:startOffset="0" - android:valueFrom="0" - android:valueTo="1" - android:valueType="floatType" /> + <objectAnimator android:propertyName="translateX" android:duration="417" + android:startOffset="0" android:valueFrom="0" android:valueTo="1" + android:valueType="floatType"/> </set> </aapt:attr> </target> -</animated-vector>
\ No newline at end of file +</animated-vector> diff --git a/packages/SystemUI/res/drawable/fingerprint_dialog_fp_to_error.xml b/packages/SystemUI/res/drawable/fingerprint_dialog_fp_to_error.xml index b899828cd85c..fc2c7d00f3a7 100644 --- a/packages/SystemUI/res/drawable/fingerprint_dialog_fp_to_error.xml +++ b/packages/SystemUI/res/drawable/fingerprint_dialog_fp_to_error.xml @@ -1,391 +1,235 @@ -<?xml version="1.0" encoding="utf-8"?><!-- - ~ Copyright (C) 2018 The Android Open Source Project - ~ - ~ Licensed under the Apache License, Version 2.0 (the "License"); - ~ you may not use this file except in compliance with the License. - ~ You may obtain a copy of the License at - ~ - ~ http://www.apache.org/licenses/LICENSE-2.0 - ~ - ~ Unless required by applicable law or agreed to in writing, software - ~ distributed under the License is distributed on an "AS IS" BASIS, - ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ~ See the License for the specific language governing permissions and - ~ limitations under the License - --> +<?xml version="1.0" encoding="utf-8" ?> +<!-- Copyright (C) 2021 The Android Open Source Project + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> <animated-vector xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:aapt="http://schemas.android.com/aapt"> + xmlns:aapt="http://schemas.android.com/aapt"> <aapt:attr name="android:drawable"> - <vector - android:width="60dp" - android:height="60dp" - android:viewportHeight="60" - android:viewportWidth="60"> + <vector android:height="60dp" android:width="60dp" android:viewportHeight="60" + android:viewportWidth="60"> <group android:name="_R_G"> - <group - android:name="_R_G_L_1_G_N_4_T_0" - android:translateX="30" - android:translateY="30"> - <group - android:name="_R_G_L_1_G" - android:pivotX="114" - android:pivotY="114" - android:scaleX="0.42244" - android:scaleY="0.42244" - android:translateX="-114" - android:translateY="-114"> - <path - android:name="_R_G_L_1_G_D_0_P_0" - android:pathData=" M79.63 67.24 C79.63,67.24 111.5,47.42 147.83,67.24 " - android:strokeAlpha="1" - android:strokeColor="@color/biometric_dialog_accent" - android:strokeLineCap="round" - android:strokeLineJoin="round" - android:strokeWidth="5.5" - android:trimPathEnd="1" - android:trimPathOffset="0" - android:trimPathStart="0" /> - <path - android:name="_R_G_L_1_G_D_1_P_0" - android:pathData=" M64.27 98.07 C64.27,98.07 80.13,73.02 113.98,73.02 C147.83,73.02 163.56,97.26 163.56,97.26 " - android:strokeAlpha="1" - android:strokeColor="@color/biometric_dialog_accent" - android:strokeLineCap="round" - android:strokeLineJoin="round" - android:strokeWidth="5.5" - android:trimPathEnd="1" - android:trimPathOffset="0" - android:trimPathStart="0" /> - <path - android:name="_R_G_L_1_G_D_2_P_0" - android:pathData=" M72.53 151.07 C72.53,151.07 62.46,122.89 76.16,105.55 C89.86,88.21 106.72,86.73 113.98,86.73 C121.08,86.73 153.51,90.62 158.7,125.87 C159.14,128.82 158.8,132.88 157.18,136.09 C154.88,140.63 150.62,143.63 145.85,143.97 C133.78,144.85 129.76,137.92 129.26,128.49 C128.88,121.19 122.49,115.35 113.15,115.35 C102.91,115.35 95.97,126.69 99.77,139.74 C103.57,152.78 111.33,163.85 130.32,169.13 " - android:strokeAlpha="1" - android:strokeColor="@color/biometric_dialog_accent" - android:strokeLineCap="round" - android:strokeLineJoin="round" - android:strokeWidth="5.5" - android:trimPathEnd="1" - android:trimPathOffset="0" - android:trimPathStart="0" /> - <path - android:name="_R_G_L_1_G_D_3_P_0" - android:pathData=" M100.6 167.84 C100.6,167.84 82.76,152.1 83.75,130.31 C84.75,108.53 102.58,100.7 113.73,100.7 C124.87,100.7 144.19,108.56 144.19,130.01 " - android:strokeAlpha="1" - android:strokeColor="@color/biometric_dialog_accent" - android:strokeLineCap="round" - android:strokeLineJoin="round" - android:strokeWidth="5.5" - android:trimPathEnd="1" - android:trimPathOffset="0" - android:trimPathStart="0" /> - <path - android:name="_R_G_L_1_G_D_4_P_0" - android:pathData=" M113.73 129.17 C113.73,129.17 113.15,161.33 149.15,156.58 " - android:strokeAlpha="1" - android:strokeColor="@color/biometric_dialog_accent" - android:strokeLineCap="round" - android:strokeLineJoin="round" - android:strokeWidth="5.5" - android:trimPathEnd="1" - android:trimPathOffset="0" - android:trimPathStart="0" /> + <group android:name="_R_G_L_1_G" android:translateX="-0.05000000000000071"> + <group android:name="_R_G_L_1_G_D_0_P_0_G_0_T_0" android:translateX="30" + android:translateY="38.75" android:scaleX="0" android:scaleY="0"> + <path android:name="_R_G_L_1_G_D_0_P_0" + android:fillColor="@color/biometric_dialog_error" + android:fillAlpha="1" android:fillType="nonZero" + android:pathData=" M-1.2 -1.25 C-1.2,-1.25 1.2,-1.25 1.2,-1.25 C1.2,-1.25 1.2,1.25 1.2,1.25 C1.2,1.25 -1.2,1.25 -1.2,1.25 C-1.2,1.25 -1.2,-1.25 -1.2,-1.25c "/> </group> - </group> - <group - android:name="_R_G_L_0_G_N_4_T_0" - android:translateX="30" - android:translateY="30"> - <group - android:name="_R_G_L_0_G" - android:translateX="-30.05" - android:translateY="-30"> - <group - android:name="_R_G_L_0_G_D_0_P_0_G_0_T_0" - android:scaleX="0" - android:scaleY="0" - android:translateX="30" - android:translateY="38.75"> - <path - android:name="_R_G_L_0_G_D_0_P_0" - android:fillAlpha="1" - android:fillColor="@color/biometric_dialog_error" - android:fillType="nonZero" - android:pathData=" M-1.2 -1.25 C-1.2,-1.25 1.2,-1.25 1.2,-1.25 C1.2,-1.25 1.2,1.25 1.2,1.25 C1.2,1.25 -1.2,1.25 -1.2,1.25 C-1.2,1.25 -1.2,-1.25 -1.2,-1.25c " /> - </group> - <group - android:name="_R_G_L_0_G_D_1_P_0_G_0_T_0" - android:pivotX="0.002" - android:pivotY="7.488" - android:scaleX="1" - android:scaleY="0" - android:translateX="30" - android:translateY="25"> - <path - android:name="_R_G_L_0_G_D_1_P_0" - android:fillAlpha="1" - android:fillColor="@color/biometric_dialog_error" - android:fillType="nonZero" - android:pathData=" M-1.2 -7.5 C-1.2,-7.5 1.2,-7.5 1.2,-7.5 C1.2,-7.5 1.2,7.5 1.2,7.5 C1.2,7.5 -1.2,7.5 -1.2,7.5 C-1.2,7.5 -1.2,-7.5 -1.2,-7.5c " /> - </group> - <path - android:name="_R_G_L_0_G_D_2_P_0" - android:pathData=" M30 6.2 C16.9,6.2 6.3,16.8 6.3,30 C6.3,43.2 16.9,53.8 30,53.8 C43.1,53.8 53.8,43.2 53.8,30 C53.8,16.8 43.1,6.2 30,6.2c " - android:strokeAlpha="1" - android:strokeColor="@color/biometric_dialog_error" - android:strokeLineCap="round" - android:strokeLineJoin="round" - android:strokeWidth="2.5" - android:trimPathEnd="1" - android:trimPathOffset="0" - android:trimPathStart="1" /> + <group android:name="_R_G_L_1_G_D_1_P_0_G_0_T_0" android:translateX="30" + android:translateY="25" android:pivotX="0.002" android:pivotY="7.488" + android:scaleX="1" android:scaleY="0"> + <path android:name="_R_G_L_1_G_D_1_P_0" + android:fillColor="@color/biometric_dialog_error" + android:fillAlpha="1" android:fillType="nonZero" + android:pathData=" M-1.2 -7.5 C-1.2,-7.5 1.2,-7.5 1.2,-7.5 C1.2,-7.5 1.2,7.5 1.2,7.5 C1.2,7.5 -1.2,7.5 -1.2,7.5 C-1.2,7.5 -1.2,-7.5 -1.2,-7.5c "/> </group> + <path android:name="_R_G_L_1_G_D_2_P_0" + android:strokeColor="@color/biometric_dialog_error" + android:strokeLineCap="round" android:strokeLineJoin="round" + android:strokeWidth="2.5" android:strokeAlpha="1" + android:trimPathStart="1" android:trimPathEnd="1" + android:trimPathOffset="0" + android:pathData=" M30 6.2 C16.9,6.2 6.3,16.8 6.3,30 C6.3,43.2 16.9,53.8 30,53.8 C43.1,53.8 53.8,43.2 53.8,30 C53.8,16.8 43.1,6.2 30,6.2c "/> + </group> + <group android:name="_R_G_L_0_G" android:translateX="-10.325" + android:translateY="-10.25"> + <path android:name="_R_G_L_0_G_D_0_P_0" + android:strokeColor="@color/biometric_dialog_accent" + android:strokeLineCap="round" android:strokeLineJoin="round" + android:strokeWidth="2" android:strokeAlpha="1" android:trimPathStart="0" + android:trimPathEnd="1" android:trimPathOffset="0" + android:pathData=" M31.41 48.43 C30.78,46.69 30.78,44.91 30.78,44.91 C30.78,40.09 34.88,36.16 40.32,36.16 C45.77,36.16 49.87,40.09 49.87,44.91 C49.87,44.91 49.87,45.17 49.87,45.17 C49.87,46.97 48.41,48.43 46.61,48.43 C45.28,48.43 44.09,47.63 43.6,46.39 C43.6,46.39 42.51,43.66 42.51,43.66 C42.02,42.42 40.82,41.61 39.49,41.61 C37.69,41.61 36.23,43.07 36.23,44.87 C36.23,47.12 37.26,49.26 39.02,50.67 C39.02,50.67 39.64,51.16 39.64,51.16 "/> + <path android:name="_R_G_L_0_G_D_1_P_0" + android:strokeColor="@color/biometric_dialog_accent" + android:strokeLineCap="round" android:strokeLineJoin="round" + android:strokeWidth="2" android:strokeAlpha="1" android:trimPathStart="0" + android:trimPathEnd="1" android:trimPathOffset="0" + android:pathData=" M32.14 27.3 C34.5,26 37.31,25.25 40.33,25.25 C43.34,25.25 46.15,26 48.51,27.3 "/> + <path android:name="_R_G_L_0_G_D_2_P_0" + android:strokeColor="@color/biometric_dialog_accent" + android:strokeLineCap="round" android:strokeLineJoin="round" + android:strokeWidth="2" android:strokeAlpha="1" android:trimPathStart="0" + android:trimPathEnd="1" android:trimPathOffset="0" + android:pathData=" M29.42 36.16 C31.35,32.94 35.51,30.71 40.33,30.71 C45.14,30.71 49.3,32.94 51.23,36.16 "/> + <path android:name="_R_G_L_0_G_D_3_P_0" + android:strokeColor="@color/biometric_dialog_accent" + android:strokeLineCap="round" android:strokeLineJoin="round" + android:strokeWidth="2" android:strokeAlpha="1" android:trimPathStart="0" + android:trimPathEnd="1" android:trimPathOffset="0" + android:pathData=" M47.14 52.52 C45.33,54.21 42.94,55.25 40.33,55.25 C37.71,55.25 35.32,54.21 33.51,52.52 "/> </group> </group> - <group android:name="time_group" /> + <group android:name="time_group"/> </vector> </aapt:attr> - <target android:name="_R_G_L_1_G_D_0_P_0"> + <target android:name="_R_G_L_1_G_D_0_P_0_G_0_T_0"> <aapt:attr name="android:animation"> <set android:ordering="together"> - <objectAnimator - android:duration="167" - android:propertyName="trimPathStart" - android:startOffset="0" - android:valueFrom="0" - android:valueTo="1" - android:valueType="floatType"> + <objectAnimator android:propertyName="scaleX" android:duration="167" + android:startOffset="0" android:valueFrom="0" android:valueTo="0" + android:valueType="floatType"> <aapt:attr name="android:interpolator"> - <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.6,1 1.0,1.0" /> + <pathInterpolator android:pathData="M 0.0,0.0 c0.08,0.06 0.2,1 1.0,1.0"/> </aapt:attr> </objectAnimator> - </set> - </aapt:attr> - </target> - <target android:name="_R_G_L_1_G_D_1_P_0"> - <aapt:attr name="android:animation"> - <set android:ordering="together"> - <objectAnimator - android:duration="167" - android:propertyName="trimPathStart" - android:startOffset="0" - android:valueFrom="0" - android:valueTo="1" - android:valueType="floatType"> + <objectAnimator android:propertyName="scaleY" android:duration="167" + android:startOffset="0" android:valueFrom="0" android:valueTo="0" + android:valueType="floatType"> <aapt:attr name="android:interpolator"> - <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.6,1 1.0,1.0" /> + <pathInterpolator android:pathData="M 0.0,0.0 c0.08,0.06 0.2,1 1.0,1.0"/> </aapt:attr> </objectAnimator> - </set> - </aapt:attr> - </target> - <target android:name="_R_G_L_1_G_D_2_P_0"> - <aapt:attr name="android:animation"> - <set android:ordering="together"> - <objectAnimator - android:duration="167" - android:propertyName="trimPathStart" - android:startOffset="0" - android:valueFrom="0" - android:valueTo="1" - android:valueType="floatType"> + <objectAnimator android:propertyName="scaleX" android:duration="100" + android:startOffset="167" android:valueFrom="0" + android:valueTo="1.1" android:valueType="floatType"> <aapt:attr name="android:interpolator"> - <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.6,1 1.0,1.0" /> + <pathInterpolator android:pathData="M 0.0,0.0 c0.08,0.06 0.2,1 1.0,1.0"/> </aapt:attr> </objectAnimator> - </set> - </aapt:attr> - </target> - <target android:name="_R_G_L_1_G_D_3_P_0"> - <aapt:attr name="android:animation"> - <set android:ordering="together"> - <objectAnimator - android:duration="167" - android:propertyName="trimPathStart" - android:startOffset="0" - android:valueFrom="0" - android:valueTo="1" - android:valueType="floatType"> + <objectAnimator android:propertyName="scaleY" android:duration="100" + android:startOffset="167" android:valueFrom="0" + android:valueTo="1.1" android:valueType="floatType"> <aapt:attr name="android:interpolator"> - <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.6,1 1.0,1.0" /> + <pathInterpolator android:pathData="M 0.0,0.0 c0.08,0.06 0.2,1 1.0,1.0"/> </aapt:attr> </objectAnimator> - </set> - </aapt:attr> - </target> - <target android:name="_R_G_L_1_G_D_4_P_0"> - <aapt:attr name="android:animation"> - <set android:ordering="together"> - <objectAnimator - android:duration="167" - android:propertyName="trimPathStart" - android:startOffset="0" - android:valueFrom="0" - android:valueTo="1" - android:valueType="floatType"> + <objectAnimator android:propertyName="scaleX" android:duration="67" + android:startOffset="267" android:valueFrom="1.1" + android:valueTo="1" android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.147,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="scaleY" android:duration="67" + android:startOffset="267" android:valueFrom="1.1" + android:valueTo="1" android:valueType="floatType"> <aapt:attr name="android:interpolator"> - <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.6,1 1.0,1.0" /> + <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.147,1 1.0,1.0"/> </aapt:attr> </objectAnimator> </set> </aapt:attr> </target> - <target android:name="_R_G_L_0_G_D_0_P_0_G_0_T_0"> + <target android:name="_R_G_L_1_G_D_1_P_0_G_0_T_0"> <aapt:attr name="android:animation"> <set android:ordering="together"> - <objectAnimator - android:duration="167" - android:propertyName="scaleX" - android:startOffset="0" - android:valueFrom="0" - android:valueTo="0" - android:valueType="floatType"> + <objectAnimator android:propertyName="scaleX" android:duration="167" + android:startOffset="0" android:valueFrom="1" android:valueTo="1" + android:valueType="floatType"> <aapt:attr name="android:interpolator"> - <pathInterpolator android:pathData="M 0.0,0.0 c0.08,0.06 0.2,1 1.0,1.0" /> + <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0"/> </aapt:attr> </objectAnimator> - <objectAnimator - android:duration="167" - android:propertyName="scaleY" - android:startOffset="0" - android:valueFrom="0" - android:valueTo="0" - android:valueType="floatType"> + <objectAnimator android:propertyName="scaleY" android:duration="167" + android:startOffset="0" android:valueFrom="0" android:valueTo="0" + android:valueType="floatType"> <aapt:attr name="android:interpolator"> - <pathInterpolator android:pathData="M 0.0,0.0 c0.08,0.06 0.2,1 1.0,1.0" /> + <pathInterpolator android:pathData="M 0.0,0.0 c0.08,0.096 0.2,1 1.0,1.0"/> </aapt:attr> </objectAnimator> - <objectAnimator - android:duration="100" - android:propertyName="scaleX" - android:startOffset="167" - android:valueFrom="0" - android:valueTo="1.1" - android:valueType="floatType"> + <objectAnimator android:propertyName="scaleX" android:duration="100" + android:startOffset="167" android:valueFrom="1" android:valueTo="1" + android:valueType="floatType"> <aapt:attr name="android:interpolator"> - <pathInterpolator android:pathData="M 0.0,0.0 c0.08,0.06 0.2,1 1.0,1.0" /> + <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0"/> </aapt:attr> </objectAnimator> - <objectAnimator - android:duration="100" - android:propertyName="scaleY" - android:startOffset="167" - android:valueFrom="0" - android:valueTo="1.1" - android:valueType="floatType"> + <objectAnimator android:propertyName="scaleY" android:duration="100" + android:startOffset="167" android:valueFrom="0" + android:valueTo="1.1" android:valueType="floatType"> <aapt:attr name="android:interpolator"> - <pathInterpolator android:pathData="M 0.0,0.0 c0.08,0.06 0.2,1 1.0,1.0" /> + <pathInterpolator android:pathData="M 0.0,0.0 c0.08,0.096 0.2,1 1.0,1.0"/> </aapt:attr> </objectAnimator> - <objectAnimator - android:duration="67" - android:propertyName="scaleX" - android:startOffset="267" - android:valueFrom="1.1" - android:valueTo="1" - android:valueType="floatType"> + <objectAnimator android:propertyName="scaleX" android:duration="67" + android:startOffset="267" android:valueFrom="1" android:valueTo="1" + android:valueType="floatType"> <aapt:attr name="android:interpolator"> - <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.147,1 1.0,1.0" /> + <pathInterpolator android:pathData="M 0.0,0.0 c0.341,0 0.2,1 1.0,1.0"/> </aapt:attr> </objectAnimator> - <objectAnimator - android:duration="67" - android:propertyName="scaleY" - android:startOffset="267" - android:valueFrom="1.1" - android:valueTo="1" - android:valueType="floatType"> + <objectAnimator android:propertyName="scaleY" android:duration="67" + android:startOffset="267" android:valueFrom="1.1" + android:valueTo="1" android:valueType="floatType"> <aapt:attr name="android:interpolator"> - <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.147,1 1.0,1.0" /> + <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0"/> </aapt:attr> </objectAnimator> </set> </aapt:attr> </target> - <target android:name="_R_G_L_0_G_D_1_P_0_G_0_T_0"> + <target android:name="_R_G_L_1_G_D_2_P_0"> <aapt:attr name="android:animation"> <set android:ordering="together"> - <objectAnimator - android:duration="167" - android:propertyName="scaleX" - android:startOffset="0" - android:valueFrom="1" - android:valueTo="1" - android:valueType="floatType"> + <objectAnimator android:propertyName="trimPathStart" android:duration="267" + android:startOffset="0" android:valueFrom="1" android:valueTo="0" + android:valueType="floatType"> <aapt:attr name="android:interpolator"> - <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" /> + <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0"/> </aapt:attr> </objectAnimator> - <objectAnimator - android:duration="167" - android:propertyName="scaleY" - android:startOffset="0" - android:valueFrom="0" - android:valueTo="0" - android:valueType="floatType"> - <aapt:attr name="android:interpolator"> - <pathInterpolator android:pathData="M 0.0,0.0 c0.08,0.096 0.2,1 1.0,1.0" /> - </aapt:attr> - </objectAnimator> - <objectAnimator - android:duration="100" - android:propertyName="scaleX" - android:startOffset="167" - android:valueFrom="1" - android:valueTo="1" - android:valueType="floatType"> - <aapt:attr name="android:interpolator"> - <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" /> - </aapt:attr> - </objectAnimator> - <objectAnimator - android:duration="100" - android:propertyName="scaleY" - android:startOffset="167" - android:valueFrom="0" - android:valueTo="1.1" - android:valueType="floatType"> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_0_G_D_0_P_0"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator android:propertyName="trimPathStart" android:duration="167" + android:startOffset="0" android:valueFrom="0" android:valueTo="1" + android:valueType="floatType"> <aapt:attr name="android:interpolator"> - <pathInterpolator android:pathData="M 0.0,0.0 c0.08,0.096 0.2,1 1.0,1.0" /> + <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0"/> </aapt:attr> </objectAnimator> - <objectAnimator - android:duration="67" - android:propertyName="scaleX" - android:startOffset="267" - android:valueFrom="1" - android:valueTo="1" - android:valueType="floatType"> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_0_G_D_1_P_0"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator android:propertyName="trimPathStart" android:duration="167" + android:startOffset="0" android:valueFrom="0" android:valueTo="1" + android:valueType="floatType"> <aapt:attr name="android:interpolator"> - <pathInterpolator android:pathData="M 0.0,0.0 c0.341,0 0.2,1 1.0,1.0" /> + <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0"/> </aapt:attr> </objectAnimator> - <objectAnimator - android:duration="67" - android:propertyName="scaleY" - android:startOffset="267" - android:valueFrom="1.1" - android:valueTo="1" - android:valueType="floatType"> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_0_G_D_2_P_0"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator android:propertyName="trimPathStart" android:duration="167" + android:startOffset="0" android:valueFrom="0" android:valueTo="1" + android:valueType="floatType"> <aapt:attr name="android:interpolator"> - <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" /> + <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0"/> </aapt:attr> </objectAnimator> </set> </aapt:attr> </target> - <target android:name="_R_G_L_0_G_D_2_P_0"> + <target android:name="_R_G_L_0_G_D_3_P_0"> <aapt:attr name="android:animation"> <set android:ordering="together"> - <objectAnimator - android:duration="267" - android:propertyName="trimPathStart" - android:startOffset="0" - android:valueFrom="1" - android:valueTo="0" - android:valueType="floatType"> + <objectAnimator android:propertyName="trimPathStart" android:duration="167" + android:startOffset="0" android:valueFrom="0" android:valueTo="1" + android:valueType="floatType"> <aapt:attr name="android:interpolator"> - <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" /> + <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0"/> </aapt:attr> </objectAnimator> </set> @@ -394,14 +238,10 @@ <target android:name="time_group"> <aapt:attr name="android:animation"> <set android:ordering="together"> - <objectAnimator - android:duration="350" - android:propertyName="translateX" - android:startOffset="0" - android:valueFrom="0" - android:valueTo="1" - android:valueType="floatType" /> + <objectAnimator android:propertyName="translateX" android:duration="350" + android:startOffset="0" android:valueFrom="0" android:valueTo="1" + android:valueType="floatType"/> </set> </aapt:attr> </target> -</animated-vector>
\ No newline at end of file +</animated-vector> diff --git a/packages/SystemUI/res/drawable/ic_signal_strength_zero_bar_no_internet.xml b/packages/SystemUI/res/drawable/ic_signal_strength_zero_bar_no_internet.xml new file mode 100644 index 000000000000..f38a36804bc8 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_signal_strength_zero_bar_no_internet.xml @@ -0,0 +1,34 @@ +<?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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0" + android:tint="?android:attr/colorControlNormal"> + <path + android:fillColor="#FF000000" + android:pathData="M2,22l16,0l0,-2l-11,0l13,-13l0,1l2,0l0,-6z" + android:strokeAlpha="0.3" + android:fillAlpha="0.3"/> + <path + android:fillColor="#FF000000" + android:pathData="M20,10h2v8h-2z"/> + <path + android:fillColor="#FF000000" + android:pathData="M20,20h2v2h-2z"/> +</vector>
\ No newline at end of file diff --git a/packages/SystemUI/res/drawable/keyguard_bottom_affordance_bg.xml b/packages/SystemUI/res/drawable/keyguard_bottom_affordance_bg.xml index 1535e7276a6f..3a08a7111d9a 100644 --- a/packages/SystemUI/res/drawable/keyguard_bottom_affordance_bg.xml +++ b/packages/SystemUI/res/drawable/keyguard_bottom_affordance_bg.xml @@ -22,11 +22,12 @@ android:color="?android:attr/textColorPrimary"> <item> <shape - android:shape="oval"> + android:shape="rectangle"> <solid android:color="?androidprv:attr/colorSurface"/> <size android:width="@dimen/keyguard_affordance_width" android:height="@dimen/keyguard_affordance_height"/> + <corners android:radius="@dimen/keyguard_affordance_fixed_radius"/> </shape> </item> </ripple> diff --git a/packages/SystemUI/res/layout/global_actions_grid_lite.xml b/packages/SystemUI/res/layout/global_actions_grid_lite.xml index 2430eec77678..5588fd391681 100644 --- a/packages/SystemUI/res/layout/global_actions_grid_lite.xml +++ b/packages/SystemUI/res/layout/global_actions_grid_lite.xml @@ -13,13 +13,12 @@ See the License for the specific language governing permissions and limitations under the License. --> -<LinearLayout +<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/global_actions_container" android:layout_width="match_parent" android:layout_height="match_parent" - android:orientation="vertical" android:gravity="center" android:layout_gravity="center"> <com.android.systemui.globalactions.GlobalActionsLayoutLite @@ -29,8 +28,11 @@ android:orientation="vertical" android:clipChildren="false" android:clipToPadding="false" - android:background="@drawable/global_actions_lite_background" - android:padding="@dimen/global_actions_lite_padding"> + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintEnd_toEndOf="parent" + android:layout_weight="1"> <androidx.constraintlayout.widget.ConstraintLayout android:layout_width="wrap_content" android:layout_height="wrap_content" @@ -38,6 +40,8 @@ android:gravity="center" android:translationZ="@dimen/global_actions_translate" android:orientation="horizontal" + android:background="@drawable/global_actions_lite_background" + android:padding="@dimen/global_actions_lite_padding" android:layoutDirection="ltr"> <androidx.constraintlayout.helper.widget.Flow android:id="@+id/list_flow" @@ -53,4 +57,4 @@ app:flow_horizontalStyle="packed"/> </androidx.constraintlayout.widget.ConstraintLayout> </com.android.systemui.globalactions.GlobalActionsLayoutLite> -</LinearLayout> +</androidx.constraintlayout.widget.ConstraintLayout> diff --git a/packages/SystemUI/res/layout/global_actions_toast.xml b/packages/SystemUI/res/layout/global_actions_toast.xml new file mode 100644 index 000000000000..1f0899623ddf --- /dev/null +++ b/packages/SystemUI/res/layout/global_actions_toast.xml @@ -0,0 +1,44 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2021 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center|bottom" + android:gravity="center" + android:layout_marginBottom="@dimen/global_actions_info_margin" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintWidth_max="382dp" + android:layout_weight="0" + android:background="@drawable/global_actions_lite_background" + android:theme="@style/Theme.SystemUI.QuickSettings" + android:paddingTop="14dp" + android:paddingBottom="14dp" + android:paddingStart="20dp" + android:paddingEnd="20dp" + android:orientation="horizontal"> + <TextView + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1" + android:textSize="14sp" + android:textColor="?android:attr/textColorSecondary" + android:text="@string/global_action_smart_lock_disabled" /> +</LinearLayout>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/qs_detail.xml b/packages/SystemUI/res/layout/qs_detail.xml index 59e1a755d7d2..78655c03dd73 100644 --- a/packages/SystemUI/res/layout/qs_detail.xml +++ b/packages/SystemUI/res/layout/qs_detail.xml @@ -20,9 +20,9 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:background="@drawable/qs_detail_background" + android:layout_marginTop="@dimen/qs_detail_margin_top" android:clickable="true" android:orientation="vertical" - android:layout_marginTop="@*android:dimen/quick_qs_offset_height" android:paddingBottom="8dp" android:visibility="invisible" android:elevation="4dp" diff --git a/packages/SystemUI/res/layout/qs_detail_header.xml b/packages/SystemUI/res/layout/qs_detail_header.xml index da80633f944b..d1ab054dd335 100644 --- a/packages/SystemUI/res/layout/qs_detail_header.xml +++ b/packages/SystemUI/res/layout/qs_detail_header.xml @@ -28,7 +28,7 @@ <com.android.systemui.ResizingSpace android:layout_width="match_parent" - android:layout_height="@dimen/qs_detail_margin_top" /> + android:layout_height="@dimen/qs_detail_header_margin_top" /> <LinearLayout android:layout_width="match_parent" diff --git a/packages/SystemUI/res/layout/quick_qs_status_icons.xml b/packages/SystemUI/res/layout/quick_qs_status_icons.xml index 5cac39f723e1..fc0d60aa193b 100644 --- a/packages/SystemUI/res/layout/quick_qs_status_icons.xml +++ b/packages/SystemUI/res/layout/quick_qs_status_icons.xml @@ -26,17 +26,38 @@ android:focusable="true" android:theme="@style/Theme.SystemUI.QuickSettings.Header"> - <com.android.systemui.statusbar.policy.Clock - android:id="@+id/clock" + <LinearLayout + android:id="@+id/clock_container" android:layout_width="wrap_content" android:layout_height="match_parent" - android:minWidth="48dp" - android:minHeight="@dimen/qs_header_row_min_height" + android:orientation="horizontal" + android:layout_gravity="center_vertical|start" android:gravity="center_vertical|start" - android:paddingStart="@dimen/status_bar_left_clock_starting_padding" - android:paddingEnd="@dimen/status_bar_left_clock_end_padding" - android:singleLine="true" - android:textAppearance="@style/TextAppearance.QS.Status" /> + > + + <com.android.systemui.statusbar.policy.Clock + android:id="@+id/clock" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:minWidth="48dp" + android:minHeight="@dimen/qs_header_row_min_height" + android:gravity="center_vertical|start" + android:paddingStart="@dimen/status_bar_left_clock_starting_padding" + android:paddingEnd="@dimen/status_bar_left_clock_end_padding" + android:singleLine="true" + android:textAppearance="@style/TextAppearance.QS.Status" /> + + <com.android.systemui.statusbar.policy.VariableDateView + android:id="@+id/date_clock" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:gravity="center_vertical|start" + android:singleLine="true" + android:textAppearance="@style/TextAppearance.QS.Status" + systemui:longDatePattern="@string/abbrev_wday_month_day_no_year_alarm" + systemui:shortDatePattern="@string/abbrev_month_day_no_year" + /> + </LinearLayout> <include layout="@layout/qs_carrier_group" android:id="@+id/carrier_group" diff --git a/packages/SystemUI/res/layout/quick_status_bar_header_date_privacy.xml b/packages/SystemUI/res/layout/quick_status_bar_header_date_privacy.xml index bff93a99258a..cc44b5e5df38 100644 --- a/packages/SystemUI/res/layout/quick_status_bar_header_date_privacy.xml +++ b/packages/SystemUI/res/layout/quick_status_bar_header_date_privacy.xml @@ -36,7 +36,7 @@ android:layout_weight="1" android:gravity="center_vertical|start" > - <com.android.systemui.statusbar.policy.DateView + <com.android.systemui.statusbar.policy.VariableDateView android:id="@+id/date" android:layout_width="wrap_content" android:layout_height="wrap_content" @@ -44,7 +44,9 @@ android:gravity="center_vertical" android:singleLine="true" android:textAppearance="@style/TextAppearance.QS.Status" - systemui:datePattern="@string/abbrev_wday_month_day_no_year_alarm" /> + systemui:longDatePattern="@string/abbrev_wday_month_day_no_year_alarm" + systemui:shortDatePattern="@string/abbrev_month_day_no_year" + /> </FrameLayout> <android.widget.Space diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml index b6921c682b1b..a95e462daed5 100644 --- a/packages/SystemUI/res/layout/status_bar_expanded.xml +++ b/packages/SystemUI/res/layout/status_bar_expanded.xml @@ -54,8 +54,7 @@ <com.android.keyguard.LockIconView android:id="@+id/lock_icon_view" android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_gravity="center"> + android:layout_height="wrap_content"> <!-- Background protection --> <ImageView android:id="@+id/lock_icon_bg" diff --git a/packages/SystemUI/res/layout/super_notification_shade.xml b/packages/SystemUI/res/layout/super_notification_shade.xml index bea50e87a29a..f1288e31fcb8 100644 --- a/packages/SystemUI/res/layout/super_notification_shade.xml +++ b/packages/SystemUI/res/layout/super_notification_shade.xml @@ -81,9 +81,10 @@ /> <!-- Keyguard messages --> - <FrameLayout - android:layout_width="wrap_content" - android:layout_height="wrap_content" + <LinearLayout + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical" android:layout_marginTop="@dimen/status_bar_height" android:layout_gravity="top|center_horizontal" android:gravity="center_horizontal"> @@ -97,7 +98,11 @@ android:singleLine="true" android:ellipsize="marquee" android:focusable="true" /> - </FrameLayout> + <FrameLayout android:id="@+id/keyboard_bouncer_container" + android:layout_height="0dp" + android:layout_width="match_parent" + android:layout_weight="1" /> + </LinearLayout> <com.android.systemui.biometrics.AuthRippleView android:id="@+id/auth_ripple" diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml index 8229661fbc24..b88962d5869f 100644 --- a/packages/SystemUI/res/values-af/strings.xml +++ b/packages/SystemUI/res/values-af/strings.xml @@ -77,6 +77,7 @@ <string name="compat_mode_on" msgid="4963711187149440884">"Zoem om skerm te vul"</string> <string name="compat_mode_off" msgid="7682459748279487945">"Strek om skerm te vul"</string> <string name="global_action_screenshot" msgid="2760267567509131654">"Skermkiekie"</string> + <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock is gedeaktiveer"</string> <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"het \'n prent gestuur"</string> <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Stoor tans skermkiekie..."</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"Stoor tans skermkiekie..."</string> diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml index 7f101d73b925..1110c99aa89b 100644 --- a/packages/SystemUI/res/values-am/strings.xml +++ b/packages/SystemUI/res/values-am/strings.xml @@ -77,6 +77,7 @@ <string name="compat_mode_on" msgid="4963711187149440884">"ማያ እንዲሞላ አጉላ"</string> <string name="compat_mode_off" msgid="7682459748279487945">"ማያ ለመሙለት ሳብ"</string> <string name="global_action_screenshot" msgid="2760267567509131654">"ቅጽበታዊ ገጽ እይታ"</string> + <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock ተሰናክሏል"</string> <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"ምስል ተልኳል"</string> <string name="screenshot_saving_ticker" msgid="6519186952674544916">"ቅጽበታዊ ገጽ እይታ በማስቀመጥ ላይ..."</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"ቅጽበታዊ ገጽ እይታ በማስቀመጥ ላይ..."</string> diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml index 8f230246a70e..6d6674869088 100644 --- a/packages/SystemUI/res/values-ar/strings.xml +++ b/packages/SystemUI/res/values-ar/strings.xml @@ -77,6 +77,7 @@ <string name="compat_mode_on" msgid="4963711187149440884">"تكبير/تصغير لملء الشاشة"</string> <string name="compat_mode_off" msgid="7682459748279487945">"توسيع بملء الشاشة"</string> <string name="global_action_screenshot" msgid="2760267567509131654">"لقطة شاشة"</string> + <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"تم إيقاف Smart Lock."</string> <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"أرسَل صورة"</string> <string name="screenshot_saving_ticker" msgid="6519186952674544916">"جارٍ حفظ لقطة الشاشة..."</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"جارٍ حفظ لقطة الشاشة..."</string> diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml index c4438fca51e3..ee9ae882d8cc 100644 --- a/packages/SystemUI/res/values-as/strings.xml +++ b/packages/SystemUI/res/values-as/strings.xml @@ -77,6 +77,7 @@ <string name="compat_mode_on" msgid="4963711187149440884">"স্ক্ৰীণ পূর্ণ কৰিবলৈ জুম কৰক"</string> <string name="compat_mode_off" msgid="7682459748279487945">"স্ক্ৰীণ পূর্ণ কৰিবলৈ প্ৰসাৰিত কৰক"</string> <string name="global_action_screenshot" msgid="2760267567509131654">"স্ক্ৰীনশ্বট"</string> + <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock অক্ষম কৰা হৈছে"</string> <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"এখন প্ৰতিচ্ছবি পঠিয়াইছে"</string> <string name="screenshot_saving_ticker" msgid="6519186952674544916">"স্ক্ৰীণশ্বট ছেভ কৰি থকা হৈছে…"</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"স্ক্ৰীণশ্বট ছেভ কৰি থকা হৈছে…"</string> diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml index 7e50b194000a..677983405276 100644 --- a/packages/SystemUI/res/values-az/strings.xml +++ b/packages/SystemUI/res/values-az/strings.xml @@ -77,6 +77,7 @@ <string name="compat_mode_on" msgid="4963711187149440884">"Ekranı doldurmaq üçün yaxınlaşdır"</string> <string name="compat_mode_off" msgid="7682459748279487945">"Ekranı doldurmaq üçün uzat"</string> <string name="global_action_screenshot" msgid="2760267567509131654">"Skrinşot"</string> + <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock deaktivdir"</string> <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"şəkil göndərdi"</string> <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Skrinşot yadda saxlanılır..."</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"Skrinşot yadda saxlanır..."</string> diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml index 701765f4036c..607391a7f440 100644 --- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml +++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml @@ -77,6 +77,7 @@ <string name="compat_mode_on" msgid="4963711187149440884">"Zumiraj na celom ekranu"</string> <string name="compat_mode_off" msgid="7682459748279487945">"Razvuci na ceo ekran"</string> <string name="global_action_screenshot" msgid="2760267567509131654">"Snimak ekrana"</string> + <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock je onemogućen"</string> <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"je poslao/la sliku"</string> <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Čuvanje snimka ekrana..."</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"Čuvanje snimka ekrana..."</string> diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml index 5da6cf8b9c35..d4954c1a2bc0 100644 --- a/packages/SystemUI/res/values-be/strings.xml +++ b/packages/SystemUI/res/values-be/strings.xml @@ -77,6 +77,7 @@ <string name="compat_mode_on" msgid="4963711187149440884">"Павял. на ўвесь экран"</string> <string name="compat_mode_off" msgid="7682459748279487945">"Расцягн. на ўвесь экран"</string> <string name="global_action_screenshot" msgid="2760267567509131654">"Здымак экрана"</string> + <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Функцыя \"Smart Lock\" адключана"</string> <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"адпраўлены відарыс"</string> <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Захаванне скрыншота..."</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"Захаванне скрыншота..."</string> diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml index 742de25d539c..b938880f8e31 100644 --- a/packages/SystemUI/res/values-bg/strings.xml +++ b/packages/SystemUI/res/values-bg/strings.xml @@ -77,6 +77,7 @@ <string name="compat_mode_on" msgid="4963711187149440884">"Мащаб – запълва екрана"</string> <string name="compat_mode_off" msgid="7682459748279487945">"Разпъване – запълва екрана"</string> <string name="global_action_screenshot" msgid="2760267567509131654">"Екранна снимка"</string> + <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Функцията Smart Lock е деактивирана"</string> <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"изпратено изображение"</string> <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Екранната снимка се запазва..."</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"Екранната снимка се запазва..."</string> diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml index b35ae1f1efde..811b6a500357 100644 --- a/packages/SystemUI/res/values-bn/strings.xml +++ b/packages/SystemUI/res/values-bn/strings.xml @@ -77,6 +77,8 @@ <string name="compat_mode_on" msgid="4963711187149440884">"স্ক্রীণ পূরণ করতে জুম করুন"</string> <string name="compat_mode_off" msgid="7682459748279487945">"ফুল স্ক্রিন করুন"</string> <string name="global_action_screenshot" msgid="2760267567509131654">"স্ক্রিনশট নিন"</string> + <!-- no translation found for global_action_smart_lock_disabled (9097102067802412936) --> + <skip /> <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"একটি ছবি পাঠানো হয়েছে"</string> <string name="screenshot_saving_ticker" msgid="6519186952674544916">"স্ক্রিনশট সেভ করা হচ্ছে..."</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"স্ক্রিনশট সেভ করা হচ্ছে..."</string> diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml index b87739732e4b..d326a51bcb4b 100644 --- a/packages/SystemUI/res/values-bs/strings.xml +++ b/packages/SystemUI/res/values-bs/strings.xml @@ -77,6 +77,7 @@ <string name="compat_mode_on" msgid="4963711187149440884">"Uvećaj prikaz na ekran"</string> <string name="compat_mode_off" msgid="7682459748279487945">"Razvuci prikaz na ekran"</string> <string name="global_action_screenshot" msgid="2760267567509131654">"Snimak ekrana"</string> + <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock je onemogućen"</string> <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"je poslao/la sliku"</string> <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Spašavanje snimka ekrana..."</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"Spašavanje snimka ekrana..."</string> diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml index a5504f02833e..431505798411 100644 --- a/packages/SystemUI/res/values-ca/strings.xml +++ b/packages/SystemUI/res/values-ca/strings.xml @@ -77,6 +77,7 @@ <string name="compat_mode_on" msgid="4963711187149440884">"Zoom per omplir pantalla"</string> <string name="compat_mode_off" msgid="7682459748279487945">"Estira per omplir pant."</string> <string name="global_action_screenshot" msgid="2760267567509131654">"Captura de pantalla"</string> + <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock desactivat"</string> <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"ha enviat una imatge"</string> <string name="screenshot_saving_ticker" msgid="6519186952674544916">"S\'està desant captura de pantalla..."</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"S\'està desant la captura de pantalla..."</string> diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml index f48c9fb1cd77..1cac5f70171c 100644 --- a/packages/SystemUI/res/values-cs/strings.xml +++ b/packages/SystemUI/res/values-cs/strings.xml @@ -77,6 +77,7 @@ <string name="compat_mode_on" msgid="4963711187149440884">"Přiblížit na celou obrazovku"</string> <string name="compat_mode_off" msgid="7682459748279487945">"Na celou obrazovku"</string> <string name="global_action_screenshot" msgid="2760267567509131654">"Snímek obrazovky"</string> + <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Funkce Smart Lock je deaktivována"</string> <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"odesílá obrázek"</string> <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Ukládání snímku obrazovky..."</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"Ukládání snímku obrazovky..."</string> diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml index cbef46488c5a..48f8086f9019 100644 --- a/packages/SystemUI/res/values-da/strings.xml +++ b/packages/SystemUI/res/values-da/strings.xml @@ -77,6 +77,7 @@ <string name="compat_mode_on" msgid="4963711187149440884">"Zoom til fuld skærm"</string> <string name="compat_mode_off" msgid="7682459748279487945">"Stræk til fuld skærm"</string> <string name="global_action_screenshot" msgid="2760267567509131654">"Screenshot"</string> + <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock er deaktiveret"</string> <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"sendte et billede"</string> <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Gemmer screenshot..."</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"Gemmer screenshot..."</string> diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml index 50fcdfb7032a..4c4a87603d55 100644 --- a/packages/SystemUI/res/values-de/strings.xml +++ b/packages/SystemUI/res/values-de/strings.xml @@ -77,6 +77,7 @@ <string name="compat_mode_on" msgid="4963711187149440884">"Zoom auf Bildschirmgröße"</string> <string name="compat_mode_off" msgid="7682459748279487945">"Auf Bildschirmgröße anpassen"</string> <string name="global_action_screenshot" msgid="2760267567509131654">"Screenshot"</string> + <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock deaktiviert"</string> <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"Bild gesendet"</string> <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Screenshot wird gespeichert..."</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"Screenshot wird gespeichert..."</string> diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml index b8d357f3ff95..0b37ed86bc7d 100644 --- a/packages/SystemUI/res/values-el/strings.xml +++ b/packages/SystemUI/res/values-el/strings.xml @@ -77,6 +77,7 @@ <string name="compat_mode_on" msgid="4963711187149440884">"Ζουμ σε πλήρη οθόνη"</string> <string name="compat_mode_off" msgid="7682459748279487945">"Προβoλή σε πλήρη οθ."</string> <string name="global_action_screenshot" msgid="2760267567509131654">"Στιγμιότυπο οθόνης"</string> + <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Το Smart Lock έχει απενεργοποιηθεί"</string> <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"έστειλε μια εικόνα"</string> <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Αποθήκ. στιγμιότυπου οθόνης..."</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"Αποθήκευση στιγμιότυπου οθόνης..."</string> diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml index d69a95bba878..e50c00e4fbed 100644 --- a/packages/SystemUI/res/values-en-rAU/strings.xml +++ b/packages/SystemUI/res/values-en-rAU/strings.xml @@ -77,6 +77,7 @@ <string name="compat_mode_on" msgid="4963711187149440884">"Zoom to fill screen"</string> <string name="compat_mode_off" msgid="7682459748279487945">"Stretch to fill screen"</string> <string name="global_action_screenshot" msgid="2760267567509131654">"Screenshot"</string> + <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock disabled"</string> <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"sent an image"</string> <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Saving screenshot…"</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"Saving screenshot…"</string> diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml index 3666069ee54b..fba313ee04c4 100644 --- a/packages/SystemUI/res/values-en-rCA/strings.xml +++ b/packages/SystemUI/res/values-en-rCA/strings.xml @@ -77,6 +77,7 @@ <string name="compat_mode_on" msgid="4963711187149440884">"Zoom to fill screen"</string> <string name="compat_mode_off" msgid="7682459748279487945">"Stretch to fill screen"</string> <string name="global_action_screenshot" msgid="2760267567509131654">"Screenshot"</string> + <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock disabled"</string> <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"sent an image"</string> <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Saving screenshot…"</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"Saving screenshot…"</string> diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml index d69a95bba878..e50c00e4fbed 100644 --- a/packages/SystemUI/res/values-en-rGB/strings.xml +++ b/packages/SystemUI/res/values-en-rGB/strings.xml @@ -77,6 +77,7 @@ <string name="compat_mode_on" msgid="4963711187149440884">"Zoom to fill screen"</string> <string name="compat_mode_off" msgid="7682459748279487945">"Stretch to fill screen"</string> <string name="global_action_screenshot" msgid="2760267567509131654">"Screenshot"</string> + <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock disabled"</string> <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"sent an image"</string> <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Saving screenshot…"</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"Saving screenshot…"</string> diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml index d69a95bba878..e50c00e4fbed 100644 --- a/packages/SystemUI/res/values-en-rIN/strings.xml +++ b/packages/SystemUI/res/values-en-rIN/strings.xml @@ -77,6 +77,7 @@ <string name="compat_mode_on" msgid="4963711187149440884">"Zoom to fill screen"</string> <string name="compat_mode_off" msgid="7682459748279487945">"Stretch to fill screen"</string> <string name="global_action_screenshot" msgid="2760267567509131654">"Screenshot"</string> + <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock disabled"</string> <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"sent an image"</string> <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Saving screenshot…"</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"Saving screenshot…"</string> diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml index 030c5884d3e3..a42a53170513 100644 --- a/packages/SystemUI/res/values-en-rXC/strings.xml +++ b/packages/SystemUI/res/values-en-rXC/strings.xml @@ -77,6 +77,7 @@ <string name="compat_mode_on" msgid="4963711187149440884">"Zoom to fill screen"</string> <string name="compat_mode_off" msgid="7682459748279487945">"Stretch to fill screen"</string> <string name="global_action_screenshot" msgid="2760267567509131654">"Screenshot"</string> + <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock disabled"</string> <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"sent an image"</string> <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Saving screenshot…"</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"Saving screenshot…"</string> diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml index bb43d81596be..992902049eb1 100644 --- a/packages/SystemUI/res/values-es-rUS/strings.xml +++ b/packages/SystemUI/res/values-es-rUS/strings.xml @@ -77,6 +77,7 @@ <string name="compat_mode_on" msgid="4963711187149440884">"Zoom para ocupar la pantalla"</string> <string name="compat_mode_off" msgid="7682459748279487945">"Estirar p/ ocupar la pantalla"</string> <string name="global_action_screenshot" msgid="2760267567509131654">"Captura de pantalla"</string> + <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Se inhabilitó Smart Lock"</string> <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"envió una imagen"</string> <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Guardando captura de pantalla"</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"Guardando la captura de pantalla..."</string> diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml index 820c708e4fe1..6a424607023e 100644 --- a/packages/SystemUI/res/values-es/strings.xml +++ b/packages/SystemUI/res/values-es/strings.xml @@ -77,6 +77,7 @@ <string name="compat_mode_on" msgid="4963711187149440884">"Zoom para ajustar"</string> <string name="compat_mode_off" msgid="7682459748279487945">"Expandir para ajustar"</string> <string name="global_action_screenshot" msgid="2760267567509131654">"Captura de pantalla"</string> + <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock inhabilitado"</string> <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"ha enviado una imagen"</string> <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Guardando captura..."</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"Guardando captura..."</string> @@ -677,8 +678,8 @@ <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Ajustes de pantalla de bloqueo"</string> <string name="status_bar_work" msgid="5238641949837091056">"Perfil de trabajo"</string> <string name="status_bar_airplane" msgid="4848702508684541009">"Modo avión"</string> - <string name="add_tile" msgid="6239678623873086686">"Añadir icono"</string> - <string name="broadcast_tile" msgid="5224010633596487481">"Icono de emisión"</string> + <string name="add_tile" msgid="6239678623873086686">"Añadir recuadro"</string> + <string name="broadcast_tile" msgid="5224010633596487481">"Recuadro de emisión"</string> <string name="zen_alarm_warning_indef" msgid="5252866591716504287">"No oirás la próxima alarma (<xliff:g id="WHEN">%1$s</xliff:g>) a menos que desactives esta opción antes"</string> <string name="zen_alarm_warning" msgid="7844303238486849503">"No oirás la próxima alarma (<xliff:g id="WHEN">%1$s</xliff:g>)"</string> <string name="alarm_template" msgid="2234991538018805736">"a las <xliff:g id="WHEN">%1$s</xliff:g>"</string> @@ -892,10 +893,10 @@ <string name="right_keycode" msgid="2480715509844798438">"Código de teclado a la derecha"</string> <string name="left_icon" msgid="5036278531966897006">"Icono a la izquierda"</string> <string name="right_icon" msgid="1103955040645237425">"Icono a la derecha"</string> - <string name="drag_to_add_tiles" msgid="8933270127508303672">"Pulsa y arrastra para añadir funciones"</string> - <string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Mantén pulsado un icono y arrástralo para reubicarlo"</string> + <string name="drag_to_add_tiles" msgid="8933270127508303672">"Pulsa y arrastra para añadir recuadros"</string> + <string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Mantén pulsado un recuadro y arrástralo para reubicarlo"</string> <string name="drag_to_remove_tiles" msgid="4682194717573850385">"Arrastra aquí para quitar una función"</string> - <string name="drag_to_remove_disabled" msgid="933046987838658850">"Necesitas al menos <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> iconos"</string> + <string name="drag_to_remove_disabled" msgid="933046987838658850">"Necesitas al menos <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> recuadros"</string> <string name="qs_edit" msgid="5583565172803472437">"Editar"</string> <string name="tuner_time" msgid="2450785840990529997">"Hora"</string> <string-array name="clock_options"> @@ -910,15 +911,15 @@ </string-array> <string name="tuner_low_priority" msgid="8412666814123009820">"Mostrar iconos de notificaciones con prioridad baja"</string> <string name="other" msgid="429768510980739978">"Otros"</string> - <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"quitar icono"</string> - <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"añadir icono al final"</string> - <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Mover icono"</string> - <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Añadir icono"</string> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"quitar recuadro"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"añadir recuadro al final"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Mover recuadro"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Añadir recuadro"</string> <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Mover a <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Añadir a la posición <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Posición <xliff:g id="POSITION">%1$d</xliff:g>"</string> - <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Tarjeta añadida"</string> - <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Tarjeta quitada"</string> + <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Recuadro añadido"</string> + <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Recuadro quitado"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor de ajustes rápidos."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Notificación de <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Abrir ajustes."</string> diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml index 6cd609fa6151..10becfb037dd 100644 --- a/packages/SystemUI/res/values-et/strings.xml +++ b/packages/SystemUI/res/values-et/strings.xml @@ -77,6 +77,7 @@ <string name="compat_mode_on" msgid="4963711187149440884">"Suumi ekraani täitmiseks"</string> <string name="compat_mode_off" msgid="7682459748279487945">"Venita ekraani täitmiseks"</string> <string name="global_action_screenshot" msgid="2760267567509131654">"Ekraanipilt"</string> + <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock on keelatud"</string> <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"saatis kujutise"</string> <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Kuvatõmmise salvestamine ..."</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"Kuvatõmmise salvestamine ..."</string> diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml index b6162a830186..3dbbc07c645e 100644 --- a/packages/SystemUI/res/values-eu/strings.xml +++ b/packages/SystemUI/res/values-eu/strings.xml @@ -77,6 +77,7 @@ <string name="compat_mode_on" msgid="4963711187149440884">"Handiagotu pantaila betetzeko"</string> <string name="compat_mode_off" msgid="7682459748279487945">"Luzatu pantaila betetzeko"</string> <string name="global_action_screenshot" msgid="2760267567509131654">"Pantaila-argazkia"</string> + <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Desgaitu da Smart Lock"</string> <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"erabiltzaileak irudi bat bidali du"</string> <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Pantaila-argazkia gordetzen…"</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"Pantaila-argazkia gordetzen…"</string> diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml index d1ddd8c042f4..4606ae7751aa 100644 --- a/packages/SystemUI/res/values-fa/strings.xml +++ b/packages/SystemUI/res/values-fa/strings.xml @@ -77,6 +77,7 @@ <string name="compat_mode_on" msgid="4963711187149440884">"بزرگنمایی برای پر کردن صفحه"</string> <string name="compat_mode_off" msgid="7682459748279487945">"گسترده کردن برای پر کردن صفحه"</string> <string name="global_action_screenshot" msgid="2760267567509131654">"نماگرفت"</string> + <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock غیرفعال شد"</string> <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"تصویری ارسال کرد"</string> <string name="screenshot_saving_ticker" msgid="6519186952674544916">"در حال ذخیره نماگرفت..."</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"درحال ذخیره نماگرفت…"</string> diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml index 652acf7ddd89..9d9b152c3a98 100644 --- a/packages/SystemUI/res/values-fi/strings.xml +++ b/packages/SystemUI/res/values-fi/strings.xml @@ -77,6 +77,7 @@ <string name="compat_mode_on" msgid="4963711187149440884">"Zoomaa koko näyttöön"</string> <string name="compat_mode_off" msgid="7682459748279487945">"Venytä koko näyttöön"</string> <string name="global_action_screenshot" msgid="2760267567509131654">"Kuvakaappaus"</string> + <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock poistettu käytöstä"</string> <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"lähetti kuvan"</string> <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Tallennetaan kuvakaappausta..."</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"Tallennetaan kuvakaappausta..."</string> diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml index 1ec1f9190a22..22e77a03b003 100644 --- a/packages/SystemUI/res/values-fr-rCA/strings.xml +++ b/packages/SystemUI/res/values-fr-rCA/strings.xml @@ -77,6 +77,8 @@ <string name="compat_mode_on" msgid="4963711187149440884">"Zoomer pour remplir l\'écran"</string> <string name="compat_mode_off" msgid="7682459748279487945">"Étirer pour remplir l\'écran"</string> <string name="global_action_screenshot" msgid="2760267567509131654">"Capture d\'écran"</string> + <!-- no translation found for global_action_smart_lock_disabled (9097102067802412936) --> + <skip /> <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"a envoyé une image"</string> <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Enregistrement capture écran…"</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"Enregistrement capture écran…"</string> diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml index 6a08d8716677..5510ef1c32c6 100644 --- a/packages/SystemUI/res/values-fr/strings.xml +++ b/packages/SystemUI/res/values-fr/strings.xml @@ -77,6 +77,7 @@ <string name="compat_mode_on" msgid="4963711187149440884">"Zoomer pour remplir l\'écran"</string> <string name="compat_mode_off" msgid="7682459748279487945">"Étirer pour remplir l\'écran"</string> <string name="global_action_screenshot" msgid="2760267567509131654">"Capture d\'écran"</string> + <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock désactivé"</string> <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"a envoyé une image"</string> <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Enregistrement capture écran…"</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"Enregistrement de la capture d\'écran…"</string> diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml index 15678e4cc907..a9f059325058 100644 --- a/packages/SystemUI/res/values-gl/strings.xml +++ b/packages/SystemUI/res/values-gl/strings.xml @@ -77,6 +77,7 @@ <string name="compat_mode_on" msgid="4963711187149440884">"Ampliar ata ocupar todo"</string> <string name="compat_mode_off" msgid="7682459748279487945">"Estirar ata ocupar todo"</string> <string name="global_action_screenshot" msgid="2760267567509131654">"Facer captura"</string> + <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock está desactivado"</string> <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"enviou unha imaxe"</string> <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Gardando captura de pantalla…"</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"Gardando captura de pantalla…"</string> diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml index 9255827aa64e..9f05de581d51 100644 --- a/packages/SystemUI/res/values-gu/strings.xml +++ b/packages/SystemUI/res/values-gu/strings.xml @@ -77,6 +77,8 @@ <string name="compat_mode_on" msgid="4963711187149440884">"સ્ક્રીન ભરવા માટે ઝૂમ કરો"</string> <string name="compat_mode_off" msgid="7682459748279487945">"સ્ક્રીન ભરવા માટે ખેંચો"</string> <string name="global_action_screenshot" msgid="2760267567509131654">"સ્ક્રીનશૉટ"</string> + <!-- no translation found for global_action_smart_lock_disabled (9097102067802412936) --> + <skip /> <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"છબી મોકલી"</string> <string name="screenshot_saving_ticker" msgid="6519186952674544916">"સ્ક્રીનશોટ સાચવી રહ્યું છે…"</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"સ્ક્રીનશોટ સાચવી રહ્યું છે…"</string> diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml index 1ebb517dc316..ffe3a5ba4359 100644 --- a/packages/SystemUI/res/values-hi/strings.xml +++ b/packages/SystemUI/res/values-hi/strings.xml @@ -77,6 +77,7 @@ <string name="compat_mode_on" msgid="4963711187149440884">"स्क्रीन भरने के लिए ज़ूम करें"</string> <string name="compat_mode_off" msgid="7682459748279487945">"स्क्रीन भरने के लिए खींचें"</string> <string name="global_action_screenshot" msgid="2760267567509131654">"स्क्रीनशॉट"</string> + <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock की सुविधा बंद कर दी गई है"</string> <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"एक इमेज भेजी गई"</string> <string name="screenshot_saving_ticker" msgid="6519186952674544916">"स्क्रीनशॉट सहेजा जा रहा है..."</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"स्क्रीनशॉट सहेजा जा रहा है..."</string> diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml index 9ec657d8d19d..260dba99416d 100644 --- a/packages/SystemUI/res/values-hr/strings.xml +++ b/packages/SystemUI/res/values-hr/strings.xml @@ -77,6 +77,7 @@ <string name="compat_mode_on" msgid="4963711187149440884">"Zumiraj i ispuni zaslon"</string> <string name="compat_mode_off" msgid="7682459748279487945">"Rastegni i ispuni zaslon"</string> <string name="global_action_screenshot" msgid="2760267567509131654">"Snimka zaslona"</string> + <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock onemogućen"</string> <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"šalje sliku"</string> <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Spremanje snimke zaslona..."</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"Spremanje snimke zaslona..."</string> diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml index d9d8148938de..4a7182677cd4 100644 --- a/packages/SystemUI/res/values-hu/strings.xml +++ b/packages/SystemUI/res/values-hu/strings.xml @@ -77,6 +77,7 @@ <string name="compat_mode_on" msgid="4963711187149440884">"Nagyítás a kitöltéshez"</string> <string name="compat_mode_off" msgid="7682459748279487945">"Nyújtás kitöltéshez"</string> <string name="global_action_screenshot" msgid="2760267567509131654">"Képernyőkép"</string> + <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock kikapcsolva"</string> <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"képet küldött"</string> <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Képernyőkép mentése..."</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"Képernyőkép mentése..."</string> diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml index 1d8094a4f61c..bdebdadaf397 100644 --- a/packages/SystemUI/res/values-hy/strings.xml +++ b/packages/SystemUI/res/values-hy/strings.xml @@ -77,6 +77,7 @@ <string name="compat_mode_on" msgid="4963711187149440884">"Խոշորացնել` էկրանը լցնելու համար"</string> <string name="compat_mode_off" msgid="7682459748279487945">"Ձգել` էկրանը լցնելու համար"</string> <string name="global_action_screenshot" msgid="2760267567509131654">"Սքրինշոթ"</string> + <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock-ն անջատված է"</string> <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"պատկեր է ուղարկվել"</string> <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Սքրինշոթը պահվում է…"</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"Սքրինշոթը պահվում է..."</string> diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml index 76ee7224933c..15b64c661b04 100644 --- a/packages/SystemUI/res/values-in/strings.xml +++ b/packages/SystemUI/res/values-in/strings.xml @@ -77,6 +77,7 @@ <string name="compat_mode_on" msgid="4963711187149440884">"Perbesar utk mengisi layar"</string> <string name="compat_mode_off" msgid="7682459748279487945">"Rentangkn utk mngisi layar"</string> <string name="global_action_screenshot" msgid="2760267567509131654">"Screenshot"</string> + <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock dinonaktifkan"</string> <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"mengirim gambar"</string> <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Menyimpan screenshot..."</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"Menyimpan screenshot..."</string> diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml index 01905542684d..e0ff373572f2 100644 --- a/packages/SystemUI/res/values-is/strings.xml +++ b/packages/SystemUI/res/values-is/strings.xml @@ -77,6 +77,7 @@ <string name="compat_mode_on" msgid="4963711187149440884">"Fylla skjá með aðdrætti"</string> <string name="compat_mode_off" msgid="7682459748279487945">"Teygja yfir allan skjáinn"</string> <string name="global_action_screenshot" msgid="2760267567509131654">"Skjámynd"</string> + <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Slökkt á Smart Lock"</string> <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"sendi mynd"</string> <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Vistar skjámynd…"</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"Vistar skjámynd…"</string> diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml index 0efab5ceb84e..3d26cbbf41ca 100644 --- a/packages/SystemUI/res/values-it/strings.xml +++ b/packages/SystemUI/res/values-it/strings.xml @@ -77,6 +77,7 @@ <string name="compat_mode_on" msgid="4963711187149440884">"Zoom per riempire schermo"</string> <string name="compat_mode_off" msgid="7682459748279487945">"Estendi per riemp. schermo"</string> <string name="global_action_screenshot" msgid="2760267567509131654">"Screenshot"</string> + <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Funzionalità Smart Lock disattivata"</string> <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"è stata inviata un\'immagine"</string> <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Salvataggio screenshot…"</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"Salvataggio screenshot…"</string> diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml index 1e4c9d66ed9c..ed1ea366814f 100644 --- a/packages/SystemUI/res/values-iw/strings.xml +++ b/packages/SystemUI/res/values-iw/strings.xml @@ -77,6 +77,8 @@ <string name="compat_mode_on" msgid="4963711187149440884">"הגדלת התצוגה למילוי המסך"</string> <string name="compat_mode_off" msgid="7682459748279487945">"מתיחה למילוי של המסך"</string> <string name="global_action_screenshot" msgid="2760267567509131654">"צילום מסך"</string> + <!-- no translation found for global_action_smart_lock_disabled (9097102067802412936) --> + <skip /> <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"נשלחה תמונה"</string> <string name="screenshot_saving_ticker" msgid="6519186952674544916">"צילום המסך נשמר..."</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"המערכת שומרת את צילום המסך..."</string> diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml index 27259791f664..502f9625d6cd 100644 --- a/packages/SystemUI/res/values-ja/strings.xml +++ b/packages/SystemUI/res/values-ja/strings.xml @@ -77,6 +77,7 @@ <string name="compat_mode_on" msgid="4963711187149440884">"画面サイズに合わせて拡大"</string> <string name="compat_mode_off" msgid="7682459748279487945">"画面サイズに合わせて拡大"</string> <string name="global_action_screenshot" msgid="2760267567509131654">"スクリーンショット"</string> + <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock は無効です"</string> <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"画像を送信しました"</string> <string name="screenshot_saving_ticker" msgid="6519186952674544916">"スクリーンショットを保存中..."</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"スクリーンショットを保存しています..."</string> diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml index 21e1c52ae136..38c59bff0edf 100644 --- a/packages/SystemUI/res/values-ka/strings.xml +++ b/packages/SystemUI/res/values-ka/strings.xml @@ -77,6 +77,7 @@ <string name="compat_mode_on" msgid="4963711187149440884">"მასშტაბი შეცვალეთ ეკრანის შესავსებად."</string> <string name="compat_mode_off" msgid="7682459748279487945">"გაწიეთ ეკრანის შესავსებად."</string> <string name="global_action_screenshot" msgid="2760267567509131654">"ეკრანის ანაბეჭდი"</string> + <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock გათიშულია"</string> <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"გაიგზავნა სურათი"</string> <string name="screenshot_saving_ticker" msgid="6519186952674544916">"სკრინშოტის შენახვა…"</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"ეკრანის სურათის შენახვა…"</string> diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml index 6a9a9b5d8d11..afb46e800aba 100644 --- a/packages/SystemUI/res/values-kk/strings.xml +++ b/packages/SystemUI/res/values-kk/strings.xml @@ -77,6 +77,7 @@ <string name="compat_mode_on" msgid="4963711187149440884">"Экранды толтыру үшін ұлғайту"</string> <string name="compat_mode_off" msgid="7682459748279487945">"Экранды толтыру үшін созу"</string> <string name="global_action_screenshot" msgid="2760267567509131654">"Скриншот"</string> + <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock өшірілді"</string> <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"сурет жіберілді"</string> <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Скриншотты сақтауда…"</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"Скриншотты сақтауда…"</string> diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml index 080ba1958e24..9f4ddf3a6a93 100644 --- a/packages/SystemUI/res/values-km/strings.xml +++ b/packages/SystemUI/res/values-km/strings.xml @@ -77,6 +77,7 @@ <string name="compat_mode_on" msgid="4963711187149440884">"ពង្រីកដើម្បីឲ្យពេញអេក្រង់"</string> <string name="compat_mode_off" msgid="7682459748279487945">"ទាញដើម្បីឲ្យពេញអេក្រង់"</string> <string name="global_action_screenshot" msgid="2760267567509131654">"រូបថតអេក្រង់"</string> + <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"បានបិទ Smart Lock"</string> <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"បានផ្ញើរូបភាព"</string> <string name="screenshot_saving_ticker" msgid="6519186952674544916">"កំពុងរក្សាទុករូបថតអេក្រង់…"</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"កំពុងរក្សាទុករូបថតអេក្រង់..."</string> diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml index f63d02f6682a..36d3d7d79c20 100644 --- a/packages/SystemUI/res/values-kn/strings.xml +++ b/packages/SystemUI/res/values-kn/strings.xml @@ -77,6 +77,8 @@ <string name="compat_mode_on" msgid="4963711187149440884">"ಪರದೆ ತುಂಬಿಸಲು ಝೂಮ್ ಮಾಡು"</string> <string name="compat_mode_off" msgid="7682459748279487945">"ಪರದೆ ತುಂಬಿಸಲು ವಿಸ್ತಾರಗೊಳಿಸು"</string> <string name="global_action_screenshot" msgid="2760267567509131654">"ಸ್ಕ್ರೀನ್ಶಾಟ್"</string> + <!-- no translation found for global_action_smart_lock_disabled (9097102067802412936) --> + <skip /> <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"ಚಿತ್ರವನ್ನು ಕಳುಹಿಸಲಾಗಿದೆ"</string> <string name="screenshot_saving_ticker" msgid="6519186952674544916">"ಸ್ಕ್ರೀನ್ಶಾಟ್ ಉಳಿಸಲಾಗುತ್ತಿದೆ…"</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"ಸ್ಕ್ರೀನ್ಶಾಟ್ ಉಳಿಸಲಾಗುತ್ತಿದೆ…"</string> diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml index 371b7de67806..a846bc1fc23a 100644 --- a/packages/SystemUI/res/values-ko/strings.xml +++ b/packages/SystemUI/res/values-ko/strings.xml @@ -77,6 +77,7 @@ <string name="compat_mode_on" msgid="4963711187149440884">"전체화면 모드로 확대"</string> <string name="compat_mode_off" msgid="7682459748279487945">"전체화면 모드로 확대"</string> <string name="global_action_screenshot" msgid="2760267567509131654">"스크린샷"</string> + <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock 사용 중지됨"</string> <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"이미지 보냄"</string> <string name="screenshot_saving_ticker" msgid="6519186952674544916">"캡쳐화면 저장 중..."</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"캡쳐화면 저장 중..."</string> diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml index 404b9ac45179..124dc53e1dcd 100644 --- a/packages/SystemUI/res/values-ky/strings.xml +++ b/packages/SystemUI/res/values-ky/strings.xml @@ -77,6 +77,7 @@ <string name="compat_mode_on" msgid="4963711187149440884">"Экрнд тлтр ү. чен өлч өзг"</string> <string name="compat_mode_off" msgid="7682459748279487945">"Экранды толтуруу ү-н чоюу"</string> <string name="global_action_screenshot" msgid="2760267567509131654">"Скриншот"</string> + <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock өчүрүлдү"</string> <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"сүрөт жөнөттү"</string> <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Скриншот сакталууда…"</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"Скриншот сакталууда..."</string> diff --git a/packages/SystemUI/res/values-land/config.xml b/packages/SystemUI/res/values-land/config.xml index ea456d81aa25..ac4dfd212bb8 100644 --- a/packages/SystemUI/res/values-land/config.xml +++ b/packages/SystemUI/res/values-land/config.xml @@ -28,9 +28,6 @@ <!-- The number of columns that the top level tiles span in the QuickSettings --> <integer name="quick_settings_user_time_settings_tile_span">2</integer> - <!-- We have only space for one notification on phone landscape layouts. --> - <integer name="keyguard_max_notification_count">1</integer> - <!-- orientation of the dead zone when touches have recently occurred elsewhere on screen --> <integer name="navigation_bar_deadzone_orientation">1</integer> diff --git a/packages/SystemUI/res/values-land/dimens.xml b/packages/SystemUI/res/values-land/dimens.xml index ee0e4b34e9ff..fc5edf3ade8f 100644 --- a/packages/SystemUI/res/values-land/dimens.xml +++ b/packages/SystemUI/res/values-land/dimens.xml @@ -41,7 +41,7 @@ <dimen name="battery_detail_graph_space_top">9dp</dimen> <dimen name="battery_detail_graph_space_bottom">9dp</dimen> - <dimen name="qs_detail_margin_top">14dp</dimen> + <dimen name="qs_detail_header_margin_top">14dp</dimen> <dimen name="volume_tool_tip_top_margin">12dp</dimen> <dimen name="volume_row_slider_height">128dp</dimen> diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml index 33fddef3efb3..c27394c79bc3 100644 --- a/packages/SystemUI/res/values-lo/strings.xml +++ b/packages/SystemUI/res/values-lo/strings.xml @@ -77,6 +77,7 @@ <string name="compat_mode_on" msgid="4963711187149440884">"ຊູມໃຫ້ເຕັມໜ້າຈໍ"</string> <string name="compat_mode_off" msgid="7682459748279487945">"ປັບໃຫ້ເຕັມໜ້າຈໍ"</string> <string name="global_action_screenshot" msgid="2760267567509131654">"ພາບໜ້າຈໍ"</string> + <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"ປິດການນຳໃຊ້ Smart Lock ແລ້ວ"</string> <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"ສົ່ງຮູບແລ້ວ"</string> <string name="screenshot_saving_ticker" msgid="6519186952674544916">"ກຳລັງບັນທຶກຮູບໜ້າຈໍ"</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"ກຳລັງບັນທຶກພາບໜ້າຈໍ..."</string> diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml index 94faaf42d1b9..a1cd602417f0 100644 --- a/packages/SystemUI/res/values-lt/strings.xml +++ b/packages/SystemUI/res/values-lt/strings.xml @@ -77,6 +77,7 @@ <string name="compat_mode_on" msgid="4963711187149440884">"Keisti mast., kad atit. ekr."</string> <string name="compat_mode_off" msgid="7682459748279487945">"Ištempti, kad atit. ekr."</string> <string name="global_action_screenshot" msgid="2760267567509131654">"Ekrano kopija"</string> + <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"„Smart Lock“ išjungta"</string> <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"išsiuntė vaizdą"</string> <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Išsaugoma ekrano kopija..."</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"Išsaugoma ekrano kopija..."</string> diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml index 0d14405ea4fb..b0da7f75c64c 100644 --- a/packages/SystemUI/res/values-lv/strings.xml +++ b/packages/SystemUI/res/values-lv/strings.xml @@ -77,6 +77,7 @@ <string name="compat_mode_on" msgid="4963711187149440884">"Tālumm., lai aizp. ekr."</string> <string name="compat_mode_off" msgid="7682459748279487945">"Stiepiet, lai aizp. ekr."</string> <string name="global_action_screenshot" msgid="2760267567509131654">"Ekrānuzņēmums"</string> + <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Sistēma Smart Lock ir atspējota"</string> <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"nosūtīts attēls"</string> <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Saglabā ekrānuzņēmumu…"</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"Notiek ekrānuzņēmuma saglabāšana..."</string> diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml index 5fda8b211ab1..bee9bffc74c3 100644 --- a/packages/SystemUI/res/values-mk/strings.xml +++ b/packages/SystemUI/res/values-mk/strings.xml @@ -77,6 +77,7 @@ <string name="compat_mode_on" msgid="4963711187149440884">"Зумирај да се исполни екранот"</string> <string name="compat_mode_off" msgid="7682459748279487945">"Растегни да се исполни екранот"</string> <string name="global_action_screenshot" msgid="2760267567509131654">"Слика од екранот"</string> + <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Оневозможено е Smart Lock"</string> <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"испрати слика"</string> <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Сликата на екранот се зачувува..."</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"Сликата на екранот се зачувува..."</string> diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml index 6956934d7af7..061f43c30302 100644 --- a/packages/SystemUI/res/values-ml/strings.xml +++ b/packages/SystemUI/res/values-ml/strings.xml @@ -77,6 +77,7 @@ <string name="compat_mode_on" msgid="4963711187149440884">"സ്ക്രീനിൽ ഉൾക്കൊള്ളിക്കാൻ സൂം ചെയ്യുക"</string> <string name="compat_mode_off" msgid="7682459748279487945">"സ്ക്രീനിൽ ഉൾക്കൊള്ളിക്കാൻ വലിച്ചുനീട്ടുക"</string> <string name="global_action_screenshot" msgid="2760267567509131654">"സ്ക്രീൻഷോട്ട്"</string> + <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock പ്രവർത്തനരഹിതമാക്കി"</string> <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"ചിത്രം അയച്ചു"</string> <string name="screenshot_saving_ticker" msgid="6519186952674544916">"സ്ക്രീൻഷോട്ട് സംരക്ഷിക്കുന്നു..."</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"സ്ക്രീൻഷോട്ട് സംരക്ഷിക്കുന്നു..."</string> diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml index 0d1ad4bebce6..8bf0dbc001cb 100644 --- a/packages/SystemUI/res/values-mn/strings.xml +++ b/packages/SystemUI/res/values-mn/strings.xml @@ -77,6 +77,7 @@ <string name="compat_mode_on" msgid="4963711187149440884">"Дэлгэц дүүргэх бол өсгөнө үү"</string> <string name="compat_mode_off" msgid="7682459748279487945">"Дэлгэц дүүргэх бол татна уу"</string> <string name="global_action_screenshot" msgid="2760267567509131654">"Дэлгэцийн зураг дарах"</string> + <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Ухаалаг түгжээг идэвхгүй болгосон"</string> <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"зураг илгээсэн"</string> <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Дэлгэцийн агшинг хадгалж байна…"</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"Дэлгэцийн агшинг хадгалж байна…"</string> diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml index 1202a7467a2c..7d2372936d76 100644 --- a/packages/SystemUI/res/values-mr/strings.xml +++ b/packages/SystemUI/res/values-mr/strings.xml @@ -77,6 +77,8 @@ <string name="compat_mode_on" msgid="4963711187149440884">"स्क्रीन भरण्यासाठी झूम करा"</string> <string name="compat_mode_off" msgid="7682459748279487945">"स्क्रीन भरण्यासाठी ताणा"</string> <string name="global_action_screenshot" msgid="2760267567509131654">"स्क्रीनशॉट"</string> + <!-- no translation found for global_action_smart_lock_disabled (9097102067802412936) --> + <skip /> <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"इमेज पाठवली आहे"</string> <string name="screenshot_saving_ticker" msgid="6519186952674544916">"स्क्रीनशॉट सेव्ह करत आहे…"</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"स्क्रीनशॉट सेव्ह करत आहे…"</string> @@ -553,7 +555,7 @@ <string name="disable_vpn" msgid="482685974985502922">"VPN अक्षम करा"</string> <string name="disconnect_vpn" msgid="26286850045344557">"VPN डिस्कनेक्ट करा"</string> <string name="monitoring_button_view_policies" msgid="3869724835853502410">"धोरणे पहा"</string> - <string name="monitoring_button_view_controls" msgid="8316440345340701117">"नियंत्रणे पाहा"</string> + <string name="monitoring_button_view_controls" msgid="8316440345340701117">"नियंत्रणे पहा"</string> <string name="monitoring_description_named_management" msgid="505833016545056036">"हे डिव्हाइस <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> चे आहे.\n\nतुमचा आयटी ॲडमिन सेटिंग्ज, कॉर्पोरेट अॅक्सेस, ॲप्स, तुमच्या डिव्हाइसशी संबंधित डेटा आणि तुमच्या डिव्हाइसच्या स्थानाची माहिती यांचे परीक्षण व व्यवस्थापन करू शकतो.\n\nअधिक माहितीसाठी तुमच्या आयटी ॲडमिनशी संपर्क साधा."</string> <string name="monitoring_financed_description_named_management" msgid="6108439201399938668">"<xliff:g id="ORGANIZATION_NAME_0">%1$s</xliff:g> कदाचित या डिव्हाइसशी संलग्न असलेला डेटा अॅक्सेस करू शकते, ॲप्सचे व्यवस्थापन करू शकते आणि ही डिव्हाइस सेटिंग्ज बदलू शकते.\n\nतुम्हाला प्रश्न असल्यास, <xliff:g id="ORGANIZATION_NAME_1">%2$s</xliff:g> शी संपर्क साधा."</string> <string name="monitoring_description_management" msgid="4308879039175729014">"हे डिव्हाइस तुमच्या संस्थेचे आहे.\n\nतुमचा आयटी ॲडमिन सेटिंग्ज, कॉर्पोरेट अॅक्सेस, ॲप्स, तुमच्या डिव्हाइसशी संबंधित डेटा आणि तुमच्या डिव्हाइसच्या स्थानाची माहिती यांचे परीक्षण व व्यवस्थापन करू शकतो.\n\nअधिक माहितीसाठी तुमच्या आयटी ॲडमिनशी संपर्क साधा."</string> @@ -651,7 +653,7 @@ <string name="output_title" msgid="3938776561655668350">"मीडिया आउटपुट"</string> <string name="output_calls_title" msgid="7085583034267889109">"फोन कॉल आउटपुट"</string> <string name="output_none_found" msgid="5488087293120982770">"कोणतीही डिव्हाइस सापडली नाहीत"</string> - <string name="output_none_found_service_off" msgid="935667567681386368">"कोणतीही डिव्हाइस सापडली नाहीत. <xliff:g id="SERVICE">%1$s</xliff:g> सुरू करून पाहा"</string> + <string name="output_none_found_service_off" msgid="935667567681386368">"कोणतीही डिव्हाइस सापडली नाहीत. <xliff:g id="SERVICE">%1$s</xliff:g> सुरू करून पहा"</string> <string name="output_service_bt" msgid="4315362133973911687">"ब्लूटूथ"</string> <string name="output_service_wifi" msgid="9003667810868222134">"वाय-फाय"</string> <string name="output_service_bt_wifi" msgid="7186882540475524124">"ब्लूटूथ आणि वाय-फाय"</string> @@ -754,7 +756,7 @@ <string name="notification_multichannel_desc" msgid="7414593090056236179">"या सूचनांचा संच येथे कॉंफिगर केला जाऊ शकत नाही"</string> <string name="notification_delegate_header" msgid="1264510071031479920">"प्रॉक्सी केलेल्या सूचना"</string> <string name="notification_channel_dialog_title" msgid="6856514143093200019">"सर्व <xliff:g id="APP_NAME">%1$s</xliff:g> वरील सूचना"</string> - <string name="see_more_title" msgid="7409317011708185729">"आणखी पाहा"</string> + <string name="see_more_title" msgid="7409317011708185729">"आणखी पहा"</string> <string name="appops_camera" msgid="5215967620896725715">"हे अॅप कॅमेरा वापरत आहे."</string> <string name="appops_microphone" msgid="8805468338613070149">"हे अॅप मायक्रोफोन वापरत आहे."</string> <string name="appops_overlay" msgid="4822261562576558490">"हे अॅप स्क्रीनवरील इतर अॅप्स वर प्रदर्शित होत आहे."</string> @@ -938,14 +940,14 @@ <string name="thermal_shutdown_title" msgid="2702966892682930264">"तापल्यामुळे फोन बंद झाला"</string> <string name="thermal_shutdown_message" msgid="6142269839066172984">"तुमचा फोन आता नेहमीप्रमाणे काम करत आहे.\nअधिक माहितीसाठी टॅप करा"</string> <string name="thermal_shutdown_dialog_message" msgid="6745684238183492031">"तुमचा फोन खूप तापलाय, म्हणून तो थंड होण्यासाठी बंद झाला आहे. तुमचा फोन आता व्यवस्थित सुरू आहे.\n\nतुम्ही असे केल्यास तुमचा फोन खूप तापेल:\n •संसाधन केंद्रित अॅप वापरणे (गेमिंग, व्हिडिओ किंवा नेव्हिगेशन अॅप यासारखे)\n •मोठ्या फाइल डाउनलोड किंवा अपलोड करणे\n •उच्च तापमानामध्ये तुमचा फोन वापरणे"</string> - <string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"काय काळजी घ्यावी ते पाहा"</string> + <string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"काय काळजी घ्यावी ते पहा"</string> <string name="high_temp_title" msgid="2218333576838496100">"फोन ऊष्ण होत आहे"</string> <string name="high_temp_notif_message" msgid="1277346543068257549">"फोन थंड होईपर्यंत काही वैशिष्ट्ये मर्यादित केली.\nअधिक माहितीसाठी टॅप करा"</string> <string name="high_temp_dialog_message" msgid="3793606072661253968">"तुमचा फोन स्वयंचलितपणे थंड होईल. तुम्ही अद्यापही तुमचा फोन वापरू शकता परंतु तो कदाचित धीमेपणे कार्य करेल.\n\nतुमचा फोन एकदा थंड झाला की, तो सामान्यपणे कार्य करेल."</string> - <string name="high_temp_dialog_help_text" msgid="7380171287943345858">"काय काळजी घ्यावी ते पाहा"</string> + <string name="high_temp_dialog_help_text" msgid="7380171287943345858">"काय काळजी घ्यावी ते पहा"</string> <string name="high_temp_alarm_title" msgid="2359958549570161495">"चार्जर अनप्लग करा"</string> <string name="high_temp_alarm_notify_message" msgid="7186272817783835089">"हे डिव्हाइस चार्ज करताना समस्या आहे. पॉवर अडॅप्टर अनप्लग करा आणि शक्य तेवढी काळजी घ्या कदाचित केबल गरम असू शकते."</string> - <string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"काय काळजी घ्यावी ते पाहा"</string> + <string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"काय काळजी घ्यावी ते पहा"</string> <string name="lockscreen_shortcut_left" msgid="1238765178956067599">"डावा शॉर्टकट"</string> <string name="lockscreen_shortcut_right" msgid="4138414674531853719">"उजवा शॉर्टकट"</string> <string name="lockscreen_unlock_left" msgid="1417801334370269374">"डावा शॉर्टकट देखील अनलॉक करतो"</string> @@ -1035,7 +1037,7 @@ <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"फुल स्क्रीन मॅग्निफाय करा"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"स्क्रीनचा काही भाग मॅग्निफाय करा"</string> <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"स्विच करा"</string> - <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"अॅक्सेसिबिलिटी जेश्चर हे आता अॅक्सेसिबिलिटी बटण आहे \n\n"<annotation id="link">"सेटिंग्ज पाहा"</annotation></string> + <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"अॅक्सेसिबिलिटी जेश्चर हे आता अॅक्सेसिबिलिटी बटण आहे \n\n"<annotation id="link">"सेटिंग्ज पहा"</annotation></string> <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"तुम्ही अॅक्सेसिबिलिटी जेश्चरवरून बटणवर स्विच करू शकता \n\n"<annotation id="link">"सेटिंग्ज"</annotation></string> <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"बटण तात्पुरते लपवण्यासाठी ते कोपर्यामध्ये हलवा"</string> <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"वर डावीकडे हलवा"</string> @@ -1144,7 +1146,7 @@ <string name="status_before_loading" msgid="1500477307859631381">"आशय लवकरच दाखवला जाईल"</string> <string name="missed_call" msgid="4228016077700161689">"मिस्ड कॉल"</string> <string name="messages_count_overflow_indicator" msgid="7850934067082006043">"<xliff:g id="NUMBER">%d</xliff:g>+"</string> - <string name="people_tile_description" msgid="8154966188085545556">"अलीकडील मेसेज, मिस्ड कॉल आणि स्टेटस अपडेट पाहा"</string> + <string name="people_tile_description" msgid="8154966188085545556">"अलीकडील मेसेज, मिस्ड कॉल आणि स्टेटस अपडेट पहा"</string> <string name="people_tile_title" msgid="6589377493334871272">"संभाषण"</string> <string name="paused_by_dnd" msgid="7856941866433556428">"व्यत्यय आणू नका द्वारे थांबवले गेले"</string> <string name="new_notification_text_content_description" msgid="2915029960094389291">"<xliff:g id="NAME">%1$s</xliff:g> यांनी मेसेज पाठवला: <xliff:g id="NOTIFICATION">%2$s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml index b7d0246c4095..b662271d9e1e 100644 --- a/packages/SystemUI/res/values-ms/strings.xml +++ b/packages/SystemUI/res/values-ms/strings.xml @@ -77,6 +77,7 @@ <string name="compat_mode_on" msgid="4963711187149440884">"Zum untuk memenuhi skrin"</string> <string name="compat_mode_off" msgid="7682459748279487945">"Regang utk memenuhi skrin"</string> <string name="global_action_screenshot" msgid="2760267567509131654">"Tangkapan skrin"</string> + <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock dilumpuhkan"</string> <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"menghantar imej"</string> <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Menyimpan tangkapan skrin..."</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"Menyimpan tangkapan skrin..."</string> diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml index 81d168b79065..900b98a0f3b9 100644 --- a/packages/SystemUI/res/values-my/strings.xml +++ b/packages/SystemUI/res/values-my/strings.xml @@ -77,6 +77,7 @@ <string name="compat_mode_on" msgid="4963711187149440884">"ဇူးမ်အပြည့်ဆွဲခြင်း"</string> <string name="compat_mode_off" msgid="7682459748279487945">"ဖန်သားပြင်အပြည့်ဆန့်ခြင်း"</string> <string name="global_action_screenshot" msgid="2760267567509131654">"ဖန်သားပြင်ဓာတ်ပုံ"</string> + <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock ပိတ်ထားသည်"</string> <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"ပုံပို့ထားသည်"</string> <string name="screenshot_saving_ticker" msgid="6519186952674544916">"ဖန်သားပြင်ဓါတ်ပုံသိမ်းစဉ်.."</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"ဖန်သားပြင်ဓါတ်ပုံရိုက်ခြင်းအား သိမ်းဆည်းပါမည်"</string> diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml index 360c58b9e2dc..938234d11181 100644 --- a/packages/SystemUI/res/values-nb/strings.xml +++ b/packages/SystemUI/res/values-nb/strings.xml @@ -77,6 +77,7 @@ <string name="compat_mode_on" msgid="4963711187149440884">"Zoom for å fylle skjermen"</string> <string name="compat_mode_off" msgid="7682459748279487945">"Strekk for å fylle skjerm"</string> <string name="global_action_screenshot" msgid="2760267567509131654">"Skjermdump"</string> + <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock er slått av"</string> <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"har sendt et bilde"</string> <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Lagrer skjermdumpen …"</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"Lagrer skjermdumpen …"</string> diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml index e5787b578430..440e8af6190c 100644 --- a/packages/SystemUI/res/values-ne/strings.xml +++ b/packages/SystemUI/res/values-ne/strings.xml @@ -77,6 +77,8 @@ <string name="compat_mode_on" msgid="4963711187149440884">"स्क्रिन भर्न जुम गर्नुहोस्"</string> <string name="compat_mode_off" msgid="7682459748279487945">"स्क्रिन भर्न तन्काउनुहोस्"</string> <string name="global_action_screenshot" msgid="2760267567509131654">"स्क्रिनसट"</string> + <!-- no translation found for global_action_smart_lock_disabled (9097102067802412936) --> + <skip /> <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"कुनै छवि पठाइयो"</string> <string name="screenshot_saving_ticker" msgid="6519186952674544916">"स्क्रिनसट बचत गर्दै…"</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"स्क्रिनसट बचत गर्दै…"</string> diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml index 7d75b6e20a1e..66ec98ab7f68 100644 --- a/packages/SystemUI/res/values-nl/strings.xml +++ b/packages/SystemUI/res/values-nl/strings.xml @@ -77,6 +77,7 @@ <string name="compat_mode_on" msgid="4963711187149440884">"Zoom om scherm te vullen"</string> <string name="compat_mode_off" msgid="7682459748279487945">"Rek uit v. schermvulling"</string> <string name="global_action_screenshot" msgid="2760267567509131654">"Screenshot"</string> + <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock staat uit"</string> <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"heeft een afbeelding gestuurd"</string> <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Screenshot opslaan..."</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"Screenshot opslaan..."</string> diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml index ee524bf2e3c8..f76adfa7f09e 100644 --- a/packages/SystemUI/res/values-or/strings.xml +++ b/packages/SystemUI/res/values-or/strings.xml @@ -77,6 +77,8 @@ <string name="compat_mode_on" msgid="4963711187149440884">"ସ୍କ୍ରୀନ ଭରିବା ପାଇଁ ଜୁମ୍ କରନ୍ତୁ"</string> <string name="compat_mode_off" msgid="7682459748279487945">"ସ୍କ୍ରୀନ୍କୁ ଭରିବା ପାଇଁ ଟାଣନ୍ତୁ"</string> <string name="global_action_screenshot" msgid="2760267567509131654">"ସ୍କ୍ରିନ୍ସଟ୍ ନିଅନ୍ତୁ"</string> + <!-- no translation found for global_action_smart_lock_disabled (9097102067802412936) --> + <skip /> <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"ଏକ ଛବି ପଠାଯାଇଛି"</string> <string name="screenshot_saving_ticker" msgid="6519186952674544916">"ସ୍କ୍ରୀନଶଟ୍ ସେଭ୍ କରାଯାଉଛି…"</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"ସ୍କ୍ରୀନଶଟ୍ ସେଭ୍ କରାଯାଉଛି…"</string> diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml index d68ab4fe347a..9d1edfa53383 100644 --- a/packages/SystemUI/res/values-pa/strings.xml +++ b/packages/SystemUI/res/values-pa/strings.xml @@ -77,6 +77,8 @@ <string name="compat_mode_on" msgid="4963711187149440884">"ਸਕ੍ਰੀਨ ਭਰਨ ਲਈ ਜ਼ੂਮ ਕਰੋ"</string> <string name="compat_mode_off" msgid="7682459748279487945">"ਸਕ੍ਰੀਨ ਭਰਨ ਲਈ ਸਟ੍ਰੈਚ ਕਰੋ"</string> <string name="global_action_screenshot" msgid="2760267567509131654">"ਸਕ੍ਰੀਨਸ਼ਾਟ"</string> + <!-- no translation found for global_action_smart_lock_disabled (9097102067802412936) --> + <skip /> <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"ਚਿੱਤਰ ਭੇਜਿਆ ਗਿਆ"</string> <string name="screenshot_saving_ticker" msgid="6519186952674544916">"ਸਕ੍ਰੀਨਸ਼ਾਟ ਸੁਰੱਖਿਅਤ ਕਰ ਰਿਹਾ ਹੈ…"</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"ਸਕ੍ਰੀਨਸ਼ਾਟ ਸੁਰੱਖਿਅਤ ਕਰ ਰਿਹਾ ਹੈ…"</string> diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml index 9aade18169ad..b6412e7805ea 100644 --- a/packages/SystemUI/res/values-pl/strings.xml +++ b/packages/SystemUI/res/values-pl/strings.xml @@ -77,6 +77,7 @@ <string name="compat_mode_on" msgid="4963711187149440884">"Powiększ, aby wypełnić ekran"</string> <string name="compat_mode_off" msgid="7682459748279487945">"Rozciągnij, aby wypełnić ekran"</string> <string name="global_action_screenshot" msgid="2760267567509131654">"Zrzut ekranu"</string> + <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Wyłączono Smart Lock"</string> <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"wysłano obraz"</string> <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Zapisywanie zrzutu ekranu..."</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"Zapisywanie zrzutu ekranu..."</string> diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml index cab482ae56fd..7c3f864caa1c 100644 --- a/packages/SystemUI/res/values-pt-rBR/strings.xml +++ b/packages/SystemUI/res/values-pt-rBR/strings.xml @@ -77,6 +77,7 @@ <string name="compat_mode_on" msgid="4963711187149440884">"Zoom p/ preencher a tela"</string> <string name="compat_mode_off" msgid="7682459748279487945">"Ampliar p/ preencher tela"</string> <string name="global_action_screenshot" msgid="2760267567509131654">"Capturar tela"</string> + <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"O Smart Lock foi desativado"</string> <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"enviou uma imagem"</string> <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Salvando captura de tela..."</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"Salvando captura de tela..."</string> diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml index eec34684ff4a..df487734e1f3 100644 --- a/packages/SystemUI/res/values-pt-rPT/strings.xml +++ b/packages/SystemUI/res/values-pt-rPT/strings.xml @@ -77,6 +77,7 @@ <string name="compat_mode_on" msgid="4963711187149440884">"Zoom para preencher o ecrã"</string> <string name="compat_mode_off" msgid="7682459748279487945">"Esticar p. caber em ec. int."</string> <string name="global_action_screenshot" msgid="2760267567509131654">"Captura de ecrã"</string> + <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock desativado"</string> <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"enviou uma imagem"</string> <string name="screenshot_saving_ticker" msgid="6519186952674544916">"A guardar captura de ecrã..."</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"A guardar captura de ecrã..."</string> diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml index cab482ae56fd..7c3f864caa1c 100644 --- a/packages/SystemUI/res/values-pt/strings.xml +++ b/packages/SystemUI/res/values-pt/strings.xml @@ -77,6 +77,7 @@ <string name="compat_mode_on" msgid="4963711187149440884">"Zoom p/ preencher a tela"</string> <string name="compat_mode_off" msgid="7682459748279487945">"Ampliar p/ preencher tela"</string> <string name="global_action_screenshot" msgid="2760267567509131654">"Capturar tela"</string> + <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"O Smart Lock foi desativado"</string> <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"enviou uma imagem"</string> <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Salvando captura de tela..."</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"Salvando captura de tela..."</string> diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml index 1e30ea713210..c36f7e7e50da 100644 --- a/packages/SystemUI/res/values-ro/strings.xml +++ b/packages/SystemUI/res/values-ro/strings.xml @@ -77,6 +77,7 @@ <string name="compat_mode_on" msgid="4963711187149440884">"Zoom pt. a umple ecranul"</string> <string name="compat_mode_off" msgid="7682459748279487945">"Înt. pt. a umple ecranul"</string> <string name="global_action_screenshot" msgid="2760267567509131654">"Captură de ecran"</string> + <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock dezactivat"</string> <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"a trimis o imagine"</string> <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Se salv. captura de ecran..."</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"Se salvează captura de ecran..."</string> diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml index 99465d54e124..cc63a91b5fe8 100644 --- a/packages/SystemUI/res/values-ru/strings.xml +++ b/packages/SystemUI/res/values-ru/strings.xml @@ -77,6 +77,7 @@ <string name="compat_mode_on" msgid="4963711187149440884">"Подогнать по размерам экрана"</string> <string name="compat_mode_off" msgid="7682459748279487945">"Растянуть на весь экран"</string> <string name="global_action_screenshot" msgid="2760267567509131654">"Скриншот"</string> + <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Функция Smart Lock отключена."</string> <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"отправлено изображение"</string> <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Сохранение..."</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"Сохранение..."</string> diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml index 6ea2a0dd5d7a..c08d6f707bc1 100644 --- a/packages/SystemUI/res/values-si/strings.xml +++ b/packages/SystemUI/res/values-si/strings.xml @@ -77,6 +77,7 @@ <string name="compat_mode_on" msgid="4963711187149440884">"තිරය පිරවීමට විශාලනය කරන්න"</string> <string name="compat_mode_off" msgid="7682459748279487945">"තිරය පිරවීමට අදින්න"</string> <string name="global_action_screenshot" msgid="2760267567509131654">"තිර රුව"</string> + <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock අබලයි"</string> <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"රූපයක් එවන ලදී"</string> <string name="screenshot_saving_ticker" msgid="6519186952674544916">"තිර රුව සුරකිමින්…"</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"තිර රුව සුරැකෙමින් පවතී…"</string> diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml index 399b9797d21f..0aabf7c48d8f 100644 --- a/packages/SystemUI/res/values-sk/strings.xml +++ b/packages/SystemUI/res/values-sk/strings.xml @@ -77,6 +77,7 @@ <string name="compat_mode_on" msgid="4963711187149440884">"Priblížiť na celú obrazovku"</string> <string name="compat_mode_off" msgid="7682459748279487945">"Na celú obrazovku"</string> <string name="global_action_screenshot" msgid="2760267567509131654">"Snímka obrazovky"</string> + <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Funkcia Smart Lock je deaktivovaná"</string> <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"odoslal(a) obrázok"</string> <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Prebieha ukladanie snímky obrazovky..."</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"Prebieha ukladanie snímky obrazovky..."</string> diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml index 0c93bced9f41..ecf5ca7c33a1 100644 --- a/packages/SystemUI/res/values-sl/strings.xml +++ b/packages/SystemUI/res/values-sl/strings.xml @@ -77,6 +77,7 @@ <string name="compat_mode_on" msgid="4963711187149440884">"Povečava čez cel zaslon"</string> <string name="compat_mode_off" msgid="7682459748279487945">"Raztegnitev čez zaslon"</string> <string name="global_action_screenshot" msgid="2760267567509131654">"Posnetek zaslona"</string> + <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Storitev Smart Lock je onemogočena."</string> <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"je poslal(-a) sliko"</string> <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Shranjev. posnetka zaslona ..."</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"Shranjevanje posnetka zaslona ..."</string> diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml index 7f101dbd3f57..86bd5a1c6fe8 100644 --- a/packages/SystemUI/res/values-sq/strings.xml +++ b/packages/SystemUI/res/values-sq/strings.xml @@ -77,6 +77,8 @@ <string name="compat_mode_on" msgid="4963711187149440884">"Zmadho për të mbushur ekranin"</string> <string name="compat_mode_off" msgid="7682459748279487945">"Shtrije për të mbushur ekranin"</string> <string name="global_action_screenshot" msgid="2760267567509131654">"Pamja e ekranit"</string> + <!-- no translation found for global_action_smart_lock_disabled (9097102067802412936) --> + <skip /> <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"dërgoi një imazh"</string> <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Po ruan pamjen e ekranit..."</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"Po ruan pamjen e ekranit…"</string> diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml index 1da42cfb2d3d..83317d2faf8a 100644 --- a/packages/SystemUI/res/values-sr/strings.xml +++ b/packages/SystemUI/res/values-sr/strings.xml @@ -77,6 +77,7 @@ <string name="compat_mode_on" msgid="4963711187149440884">"Зумирај на целом екрану"</string> <string name="compat_mode_off" msgid="7682459748279487945">"Развуци на цео екран"</string> <string name="global_action_screenshot" msgid="2760267567509131654">"Снимак екрана"</string> + <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock је онемогућен"</string> <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"је послао/ла слику"</string> <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Чување снимка екрана..."</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"Чување снимка екрана..."</string> diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml index 05aeffbc300d..86b0fc7c9473 100644 --- a/packages/SystemUI/res/values-sv/strings.xml +++ b/packages/SystemUI/res/values-sv/strings.xml @@ -77,6 +77,7 @@ <string name="compat_mode_on" msgid="4963711187149440884">"Zooma för att fylla skärm"</string> <string name="compat_mode_off" msgid="7682459748279487945">"Dra för att fylla skärmen"</string> <string name="global_action_screenshot" msgid="2760267567509131654">"Skärmbild"</string> + <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock har inaktiverats"</string> <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"har skickat en bild"</string> <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Skärmbilden sparas ..."</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"Skärmbilden sparas ..."</string> diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml index 7f51e82683b7..58401b5e1030 100644 --- a/packages/SystemUI/res/values-sw/strings.xml +++ b/packages/SystemUI/res/values-sw/strings.xml @@ -77,6 +77,7 @@ <string name="compat_mode_on" msgid="4963711187149440884">"Kuza ili kujaza skrini"</string> <string name="compat_mode_off" msgid="7682459748279487945">"Tanua ili kujaza skrini"</string> <string name="global_action_screenshot" msgid="2760267567509131654">"Picha ya skrini"</string> + <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Kipengele cha Smart Lock kimezimwa"</string> <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"imetuma picha"</string> <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Inahifadhi picha ya skrini..."</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"Inahifadhi picha ya skrini..."</string> diff --git a/packages/SystemUI/res/values-sw600dp/dimens.xml b/packages/SystemUI/res/values-sw600dp/dimens.xml index 527537b6155e..c2aad2d11c1d 100644 --- a/packages/SystemUI/res/values-sw600dp/dimens.xml +++ b/packages/SystemUI/res/values-sw600dp/dimens.xml @@ -100,4 +100,7 @@ same as phones in portrait --> <dimen name="qs_security_footer_single_line_height">48dp</dimen> <dimen name="qs_security_footer_background_inset">0dp</dimen> + + <!-- When split shade is used, this panel should be aligned to the top --> + <dimen name="qs_detail_margin_top">0dp</dimen> </resources> diff --git a/packages/SystemUI/res/values-sw720dp/config.xml b/packages/SystemUI/res/values-sw720dp/config.xml index 64e2760e7778..436f8d0998f5 100644 --- a/packages/SystemUI/res/values-sw720dp/config.xml +++ b/packages/SystemUI/res/values-sw720dp/config.xml @@ -22,8 +22,5 @@ <resources> <integer name="status_bar_config_maxNotificationIcons">5</integer> - <!-- The maximum count of notifications on Keyguard. The rest will be collapsed in an overflow - card. --> - <integer name="keyguard_max_notification_count">5</integer> </resources> diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml index b51fb9efbbee..efb704f523b8 100644 --- a/packages/SystemUI/res/values-ta/strings.xml +++ b/packages/SystemUI/res/values-ta/strings.xml @@ -77,6 +77,7 @@ <string name="compat_mode_on" msgid="4963711187149440884">"திரையை நிரப்ப அளவை மாற்று"</string> <string name="compat_mode_off" msgid="7682459748279487945">"திரையை நிரப்ப இழு"</string> <string name="global_action_screenshot" msgid="2760267567509131654">"ஸ்கிரீன்ஷாட்"</string> + <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock முடக்கப்பட்டது"</string> <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"படம் அனுப்பப்பட்டது"</string> <string name="screenshot_saving_ticker" msgid="6519186952674544916">"ஸ்க்ரீன் ஷாட்டைச் சேமிக்கிறது…"</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"ஸ்க்ரீன் ஷாட்டைச் சேமிக்கிறது…"</string> @@ -122,10 +123,10 @@ <string name="screenrecord_delete_error" msgid="2870506119743013588">"திரை ரெக்கார்டிங்கை நீக்குவதில் பிழை"</string> <string name="screenrecord_permission_error" msgid="7856841237023137686">"அனுமதிகளைப் பெற இயலவில்லை"</string> <string name="screenrecord_start_error" msgid="2200660692479682368">"ஸ்கிரீன் ரெக்கார்டிங்கைத் தொடங்குவதில் பிழை"</string> - <string name="usb_preference_title" msgid="1439924437558480718">"USB கோப்பு இடமாற்ற விருப்பங்கள்"</string> + <string name="usb_preference_title" msgid="1439924437558480718">"USB ஃபைல் இடமாற்ற விருப்பங்கள்"</string> <string name="use_mtp_button_title" msgid="5036082897886518086">"(MTP) மீடியா பிளேயராக ஏற்று"</string> <string name="use_ptp_button_title" msgid="7676427598943446826">"(PTP) கேமராவாக ஏற்று"</string> - <string name="installer_cd_button_title" msgid="5499998592841984743">"Mac க்கான Android கோப்பு இடமாற்ற ஆப்ஸை நிறுவு"</string> + <string name="installer_cd_button_title" msgid="5499998592841984743">"Mac க்கான Android ஃபைல் இடமாற்ற ஆப்ஸை நிறுவு"</string> <string name="accessibility_back" msgid="6530104400086152611">"பின்செல்"</string> <string name="accessibility_home" msgid="5430449841237966217">"முகப்பு"</string> <string name="accessibility_menu" msgid="2701163794470513040">"மெனு"</string> diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml index 45236a795573..654605991aaf 100644 --- a/packages/SystemUI/res/values-te/strings.xml +++ b/packages/SystemUI/res/values-te/strings.xml @@ -54,7 +54,7 @@ <string name="usb_accessory_confirm_prompt" msgid="5728408382798643421">"<xliff:g id="USB_ACCESSORY">%2$s</xliff:g>ని నిర్వహించడానికి <xliff:g id="APPLICATION">%1$s</xliff:g>ని తెరవాలా?"</string> <string name="usb_accessory_uri_prompt" msgid="6756649383432542382">"ఈ USB ఉపకరణంతో ఇన్స్టాల్ చేయబడిన యాప్లు ఏవీ పని చేయవు. ఈ ఉపకరణం గురించి <xliff:g id="URL">%1$s</xliff:g>లో మరింత తెలుసుకోండి"</string> <string name="title_usb_accessory" msgid="1236358027511638648">"USB ఉపకరణం"</string> - <string name="label_view" msgid="6815442985276363364">"వీక్షించండి"</string> + <string name="label_view" msgid="6815442985276363364">"చూడండి"</string> <string name="always_use_device" msgid="210535878779644679">"<xliff:g id="USB_DEVICE">%2$s</xliff:g> కనెక్ట్ అయి ఉన్న ఎల్లప్పుడూ <xliff:g id="APPLICATION">%1$s</xliff:g>ని తెరవండి"</string> <string name="always_use_accessory" msgid="1977225429341838444">"<xliff:g id="USB_ACCESSORY">%2$s</xliff:g> కనెక్ట్ అయి ఉన్న ఎల్లప్పుడూ <xliff:g id="APPLICATION">%1$s</xliff:g>ని తెరవండి"</string> <string name="usb_debugging_title" msgid="8274884945238642726">"USB డీబగ్గింగ్ను అనుమతించాలా?"</string> @@ -77,6 +77,8 @@ <string name="compat_mode_on" msgid="4963711187149440884">"స్క్రీన్కు నింపేలా జూమ్ చేయండి"</string> <string name="compat_mode_off" msgid="7682459748279487945">"స్క్రీన్కు నింపేలా విస్తరించండి"</string> <string name="global_action_screenshot" msgid="2760267567509131654">"స్క్రీన్షాట్"</string> + <!-- no translation found for global_action_smart_lock_disabled (9097102067802412936) --> + <skip /> <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"ఇమేజ్ను పంపారు"</string> <string name="screenshot_saving_ticker" msgid="6519186952674544916">"స్క్రీన్షాట్ను సేవ్ చేస్తోంది…"</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"స్క్రీన్షాట్ను సేవ్ చేస్తోంది…"</string> @@ -113,7 +115,7 @@ <string name="screenrecord_stop_text" msgid="6549288689506057686">"ఆపడానికి ట్యాప్ చేయండి"</string> <string name="screenrecord_stop_label" msgid="72699670052087989">"ఆపివేయి"</string> <string name="screenrecord_pause_label" msgid="6004054907104549857">"పాజ్ చేయి"</string> - <string name="screenrecord_resume_label" msgid="4972223043729555575">"కొనసాగించు"</string> + <string name="screenrecord_resume_label" msgid="4972223043729555575">"కొనసాగించండి"</string> <string name="screenrecord_cancel_label" msgid="7850926573274483294">"రద్దు చేయి"</string> <string name="screenrecord_share_label" msgid="5025590804030086930">"షేర్ చేయి"</string> <string name="screenrecord_cancel_success" msgid="1775448688137393901">"స్క్రీన్ రికార్డ్ రద్దు చేయబడింది"</string> @@ -308,7 +310,7 @@ <string name="data_usage_disabled_dialog_enable" msgid="2796648546086408937">"పునఃప్రారంభించు"</string> <string name="gps_notification_searching_text" msgid="231304732649348313">"GPS కోసం శోధిస్తోంది"</string> <string name="gps_notification_found_text" msgid="3145873880174658526">"స్థానం GPS ద్వారా సెట్ చేయబడింది"</string> - <string name="accessibility_location_active" msgid="2845747916764660369">"స్థాన అభ్యర్థనలు సక్రియంగా ఉన్నాయి"</string> + <string name="accessibility_location_active" msgid="2845747916764660369">"స్థాన రిక్వెస్ట్లు సక్రియంగా ఉన్నాయి"</string> <string name="accessibility_sensors_off_active" msgid="2619725434618911551">"సెన్సార్లు ఆఫ్ యాక్టివ్లో ఉంది"</string> <string name="accessibility_clear_all" msgid="970525598287244592">"అన్ని నోటిఫికేషన్లను క్లియర్ చేయండి."</string> <string name="notification_group_overflow_indicator" msgid="7605120293801012648">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string> @@ -506,7 +508,7 @@ <string name="battery_saver_notification_title" msgid="8419266546034372562">"బ్యాటరీ సేవర్ ఆన్లో ఉంది"</string> <string name="battery_saver_notification_text" msgid="2617841636449016951">"పనితీరుని మరియు నేపథ్య డేటాను తగ్గిస్తుంది"</string> <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"బ్యాటరీ సేవర్ను ఆఫ్ చేయండి"</string> - <string name="media_projection_dialog_text" msgid="1755705274910034772">"రికార్డ్ చేస్తున్నప్పుడు లేదా ప్రసారం చేస్తున్నప్పుడు, మీ స్క్రీన్పై ప్రదర్శించబడిన లేదా మీ పరికరం నుండి ప్లే చేయబడిన సమాచారం మొత్తాన్ని, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> యాక్సెస్ చేయగలుగుతుంది. ఈ సమాచారంలో, పాస్వర్డ్లు, చెల్లింపు వివరాలు, ఫోటోలు, సందేశాలు, మీరు ప్లే చేసే ఆడియో వంటివి ఉంటాయి."</string> + <string name="media_projection_dialog_text" msgid="1755705274910034772">"రికార్డ్ చేస్తున్నప్పుడు లేదా ప్రసారం చేస్తున్నప్పుడు, మీ స్క్రీన్పై ప్రదర్శించబడిన లేదా మీ పరికరం నుండి ప్లే చేయబడిన సమాచారం మొత్తాన్ని, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> యాక్సెస్ చేయగలుగుతుంది. ఈ సమాచారంలో, పాస్వర్డ్లు, చెల్లింపు వివరాలు, ఫోటోలు, మెసేజ్లు, మీరు ప్లే చేసే ఆడియో వంటివి ఉంటాయి."</string> <string name="media_projection_dialog_service_text" msgid="958000992162214611">"రికార్డ్ చేస్తున్నప్పుడు లేదా ప్రసారం చేస్తున్నప్పుడు మీ స్క్రీన్పై ప్రదర్శించబడిన లేదా మీ పరికరం నుండి ప్లే చేయబడిన సమాచారం మొత్తాన్ని, ఈ ఫంక్షన్ను అందిస్తున్న సర్వీస్ యాక్సెస్ చేయగలదు. ఈ సమాచారంలో, పాస్వర్డ్లు, పేమెంట్ వివరాలు, ఫోటోలు, మెసేజ్లు, మీరు ప్లే చేసే ఆడియో వంటివి ఉంటాయి."</string> <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"రికార్డ్ చేయడం లేదా ప్రసారం చేయడం ప్రారంభించాలా?"</string> <string name="media_projection_dialog_title" msgid="3316063622495360646">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>తో రికార్డ్ చేయడం లేదా ప్రసారం చేయడం ప్రారంభించాలా?"</string> @@ -552,7 +554,7 @@ <string name="monitoring_subtitle_ca_certificate" msgid="8588092029755175800">"CA ప్రమాణపత్రాలు"</string> <string name="disable_vpn" msgid="482685974985502922">"VPNని నిలిపివేయి"</string> <string name="disconnect_vpn" msgid="26286850045344557">"VPNను డిస్కనెక్ట్ చేయి"</string> - <string name="monitoring_button_view_policies" msgid="3869724835853502410">"విధానాలను వీక్షించండి"</string> + <string name="monitoring_button_view_policies" msgid="3869724835853502410">"విధానాలను చూడండి"</string> <string name="monitoring_button_view_controls" msgid="8316440345340701117">"నియంత్రణలను చూడండి"</string> <string name="monitoring_description_named_management" msgid="505833016545056036">"ఈ పరికరం <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>కు చెందినది.\n\nసెట్టింగ్లను, కార్పొరేట్ యాక్సెస్ను, యాప్లను, మీ పరికరానికి సంబంధించిన డేటాను, అలాగే మీ పరికరం యొక్క లొకేషన్ సమాచారాన్ని మీ IT అడ్మిన్ పర్యవేక్షించగలరు, మేనేజ్ చేయగలరు.\n\nమరింత సమాచారం కోసం, మీ IT అడ్మిన్ను సంప్రదించండి."</string> <string name="monitoring_financed_description_named_management" msgid="6108439201399938668">"<xliff:g id="ORGANIZATION_NAME_0">%1$s</xliff:g>, ఈ పరికరంతో అనుబంధించబడిన డేటాను యాక్సెస్ చేయవచ్చు, యాప్లను మేనేజ్ చేయవచ్చు అలాగే ఈ పరికరాల సెట్టింగ్లను మార్చవచ్చు.\n\nమీకు ఏవైనా సందేహాలు ఉంటే, <xliff:g id="ORGANIZATION_NAME_1">%2$s</xliff:g>ను కాంటాక్ట్ చేయండి."</string> @@ -560,7 +562,7 @@ <string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"ఈ పరికరంలో మీ సంస్థ ఒక ప్రమాణపత్ర అధికారాన్ని ఇన్స్టాల్ చేసింది. మీ సురక్షిత నెట్వర్క్ ట్రాఫిక్ పర్యవేక్షించబడవచ్చు లేదా సవరించబడవచ్చు."</string> <string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"మీ కార్యాలయ ప్రొఫైల్లో మీ సంస్థ ఒక ప్రమాణపత్ర అధికారాన్ని ఇన్స్టాల్ చేసింది. మీ సురక్షిత నెట్వర్క్ ట్రాఫిక్ పర్యవేక్షించబడవచ్చు లేదా సవరించబడవచ్చు."</string> <string name="monitoring_description_ca_certificate" msgid="448923057059097497">"ఈ పరికరంలో ప్రమాణపత్ర అధికారం ఇన్స్టాల్ చేయబడింది. మీ సురక్షిత నెట్వర్క్ ట్రాఫిక్ పర్యవేక్షించబడవచ్చు లేదా సవరించబడవచ్చు."</string> - <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"మీ నిర్వాహకులు మీ పరికరంలోని ట్రాఫిక్ని పర్యవేక్షించగల నెట్వర్క్ లాగింగ్ని ఆన్ చేసారు."</string> + <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"మీ నిర్వాహకులు మీ పరికరంలోని ట్రాఫిక్ని పర్యవేక్షించగల నెట్వర్క్ లాగింగ్ని ఆన్ చేశారు."</string> <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"మీ అడ్మిన్ నెట్వర్క్ లాగింగ్ను ఆన్ చేశారు, ఇది మీ వర్క్ ప్రొఫైల్లోని ట్రాఫిక్ను పర్యవేక్షిస్తుంది కానీ మీ వ్యక్తిగత ప్రొఫైల్లో కాదు."</string> <string name="monitoring_description_named_vpn" msgid="5749932930634037027">"మీరు <xliff:g id="VPN_APP">%1$s</xliff:g>కి కనెక్ట్ చేయబడ్డారు, ఇది ఇమెయిల్లు, యాప్లు మరియు వెబ్సైట్లతో సహా మీ నెట్వర్క్ కార్యాచరణను పర్యవేక్షించగలదు."</string> <string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"మీరు ఇమెయిల్లు, యాప్లు మరియు వెబ్సైట్లతో సహా మీ నెట్వర్క్ కార్యాచరణను పర్యవేక్షించగల <xliff:g id="VPN_APP_0">%1$s</xliff:g> మరియు <xliff:g id="VPN_APP_1">%2$s</xliff:g>కి కనెక్ట్ చేయబడ్డారు."</string> @@ -576,8 +578,8 @@ <string name="monitoring_description_vpn_settings" msgid="5264167033247632071">"VPN సెట్టింగ్లను తెరవండి"</string> <string name="monitoring_description_ca_cert_settings_separator" msgid="7107390013344435439">" "</string> <string name="monitoring_description_ca_cert_settings" msgid="8329781950135541003">"విశ్వసనీయ ఆధారాలను తెరువు"</string> - <string name="monitoring_description_network_logging" msgid="577305979174002252">"మీ నిర్వాహకులు మీ పరికరంలోని ట్రాఫిక్ని పర్యవేక్షించగల నెట్వర్క్ లాగింగ్ని ఆన్ చేసారు.\n\nమరింత సమాచారం కావాలంటే, మీ నిర్వాహకులను సంప్రదించండి."</string> - <string name="monitoring_description_vpn" msgid="1685428000684586870">"మీరు VPN కనెక్షన్ సెటప్ చేయడానికి ఒక యాప్నకు అనుమతి ఇచ్చారు.\n\nఈ యాప్ ఇమెయిల్లు,యాప్లు మరియు వెబ్సైట్లతో సహా మీ డివైజ్ మరియు నెట్వర్క్ కార్యకలాపాన్ని పర్యవేక్షించగలదు."</string> + <string name="monitoring_description_network_logging" msgid="577305979174002252">"మీ నిర్వాహకులు మీ పరికరంలోని ట్రాఫిక్ని పర్యవేక్షించగల నెట్వర్క్ లాగింగ్ని ఆన్ చేశారు.\n\nమరింత సమాచారం కావాలంటే, మీ నిర్వాహకులను సంప్రదించండి."</string> + <string name="monitoring_description_vpn" msgid="1685428000684586870">"మీరు VPN కనెక్షన్ సెటప్ చేయడానికి ఒక యాప్నకు అనుమతి ఇచ్చారు.\n\nఈ యాప్ ఇమెయిల్లు,యాప్లు మరియు వెబ్సైట్లతో సహా మీ డివైజ్ మరియు నెట్వర్క్ యాక్టివిటీని పర్యవేక్షించగలదు."</string> <string name="monitoring_description_vpn_profile_owned" msgid="4964237035412372751">"<xliff:g id="ORGANIZATION">%1$s</xliff:g> ద్వారా మీ కార్యాలయ ప్రొఫైల్ నిర్వహించబడుతోంది.\n\nఇమెయిల్లు, యాప్లు మరియు వెబ్సైట్లతో సహా మీ నెట్వర్క్ కార్యాచరణను పర్యవేక్షించగల సామర్థ్యం మీ నిర్వాహకులకు ఉంది.\n\nమరింత సమాచారం కావాలంటే, మీ నిర్వాహకులను సంప్రదించండి.\n\nమీరు VPNకి కూడా కనెక్ట్ అయ్యారు, ఇది మీ నెట్వర్క్ కార్యాచరణను పర్యవేక్షించగలదు."</string> <string name="monitoring_description_parental_controls" msgid="8184693528917051626">"ఈ పరికరాన్ని మీ తల్లి/తండ్రి మేనేజ్ చేస్తున్నారు. మీ తల్లి/తండ్రి, మీరు ఉపయోగించే యాప్లు, మీ లొకేషన్, అలాగే మీ పరికర వినియోగ వ్యవధి వంటి సమాచారాన్ని చూడగలరు, మేనేజ్ చేయగలరు."</string> <string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string> @@ -709,7 +711,7 @@ <string name="tuner_full_importance_settings" msgid="1388025816553459059">"పవర్ నోటిఫికేషన్ నియంత్రణలు"</string> <string name="tuner_full_importance_settings_on" msgid="917981436602311547">"ఆన్లో ఉన్నాయి"</string> <string name="tuner_full_importance_settings_off" msgid="5580102038749680829">"ఆఫ్లో ఉన్నాయి"</string> - <string name="power_notification_controls_description" msgid="1334963837572708952">"పవర్ నోటిఫికేషన్ నియంత్రణలతో, మీరు యాప్ నోటిఫికేషన్ల కోసం ప్రాముఖ్యత స్థాయిని 0 నుండి 5 వరకు సెట్ చేయవచ్చు. \n\n"<b>"స్థాయి 5"</b>" \n- నోటిఫికేషన్ జాబితా పైభాగంలో చూపబడతాయి \n- పూర్తి స్క్రీన్ అంతరాయం అనుమతించబడుతుంది \n- ఎల్లప్పుడూ త్వరిత వీక్షణ అందించబడుతుంది \n\n"<b>"స్థాయి 4"</b>\n"- పూర్తి స్క్రీన్ అంతరాయం నిరోధించబడుతుంది \n- ఎల్లప్పుడూ త్వరిత వీక్షణ అందించబడుతుంది \n\n"<b>"స్థాయి 3"</b>" \n- పూర్తి స్క్రీన్ అంతరాయం నిరోధించబడుతుంది \n- ఎప్పుడూ త్వరిత వీక్షణ అందించబడదు \n\n"<b>"స్థాయి 2"</b>" \n- పూర్తి స్క్రీన్ అంతరాయం నిరోధించబడుతుంది \n- ఎప్పుడూ త్వరిత వీక్షణ అందించబడదు \n- ఎప్పుడూ శబ్దం మరియు వైబ్రేషన్ చేయవు \n\n"<b>"స్థాయి 1"</b>" \n- పూర్తి స్క్రీన్ అంతరాయం నిరోధించబడుతుంది \n- ఎప్పుడూ త్వరిత వీక్షణ అందించబడదు \n- ఎప్పుడూ శబ్దం లేదా వైబ్రేట్ చేయవు \n- లాక్ స్క్రీన్ మరియు స్థితి పట్టీ నుండి దాచబడతాయి \n- నోటిఫికేషన్ జాబితా దిగువ భాగంలో చూపబడతాయి \n\n"<b>"స్థాయి 0"</b>" \n- యాప్ నుండి అన్ని నోటిఫికేషన్లు బ్లాక్ చేయబడతాయి"</string> + <string name="power_notification_controls_description" msgid="1334963837572708952">"పవర్ నోటిఫికేషన్ నియంత్రణలతో, మీరు యాప్ నోటిఫికేషన్ల కోసం ప్రాముఖ్యత స్థాయిని 0 నుండి 5 వరకు సెట్ చేయవచ్చు. \n\n"<b>"స్థాయి 5"</b>" \n- నోటిఫికేషన్ లిస్ట్ పైభాగంలో చూపబడతాయి \n- పూర్తి స్క్రీన్ అంతరాయం అనుమతించబడుతుంది \n- ఎల్లప్పుడూ త్వరిత వీక్షణ అందించబడుతుంది \n\n"<b>"స్థాయి 4"</b>\n"- పూర్తి స్క్రీన్ అంతరాయం నిరోధించబడుతుంది \n- ఎల్లప్పుడూ త్వరిత వీక్షణ అందించబడుతుంది \n\n"<b>"స్థాయి 3"</b>" \n- పూర్తి స్క్రీన్ అంతరాయం నిరోధించబడుతుంది \n- ఎప్పుడూ త్వరిత వీక్షణ అందించబడదు \n\n"<b>"స్థాయి 2"</b>" \n- పూర్తి స్క్రీన్ అంతరాయం నిరోధించబడుతుంది \n- ఎప్పుడూ త్వరిత వీక్షణ అందించబడదు \n- ఎప్పుడూ శబ్దం మరియు వైబ్రేషన్ చేయవు \n\n"<b>"స్థాయి 1"</b>" \n- పూర్తి స్క్రీన్ అంతరాయం నిరోధించబడుతుంది \n- ఎప్పుడూ త్వరిత వీక్షణ అందించబడదు \n- ఎప్పుడూ శబ్దం లేదా వైబ్రేట్ చేయవు \n- లాక్ స్క్రీన్ మరియు స్థితి పట్టీ నుండి దాచబడతాయి \n- నోటిఫికేషన్ లిస్ట్ దిగువ భాగంలో చూపబడతాయి \n\n"<b>"స్థాయి 0"</b>" \n- యాప్ నుండి అన్ని నోటిఫికేషన్లు బ్లాక్ చేయబడతాయి"</string> <string name="notification_header_default_channel" msgid="225454696914642444">"నోటిఫికేషన్లు"</string> <string name="notification_channel_disabled" msgid="928065923928416337">"ఇకపై మీకు ఈ నోటిఫికేషన్లు కనిపించవు"</string> <string name="notification_channel_minimized" msgid="6892672757877552959">"ఈ నోటిఫికేషన్లు కుదించబడ్డాయి"</string> @@ -925,13 +927,13 @@ <string name="accessibility_quick_settings_expand" msgid="2609275052412521467">"శీఘ్ర సెట్టింగ్లను తెరవండి."</string> <string name="accessibility_quick_settings_collapse" msgid="4674876336725041982">"శీఘ్ర సెట్టింగ్లను మూసివేయండి."</string> <string name="accessibility_quick_settings_alarm_set" msgid="7237918261045099853">"అలారం సెట్ చేయబడింది."</string> - <string name="accessibility_quick_settings_user" msgid="505821942882668619">"<xliff:g id="ID_1">%s</xliff:g> వలె సైన్ ఇన్ చేసారు"</string> + <string name="accessibility_quick_settings_user" msgid="505821942882668619">"<xliff:g id="ID_1">%s</xliff:g> వలె సైన్ ఇన్ చేశారు"</string> <string name="accessibility_quick_settings_choose_user_action" msgid="4554388498186576087">"యూజర్ను ఎంపిక చేయండి"</string> <string name="data_connection_no_internet" msgid="691058178914184544">"ఇంటర్నెట్ లేదు"</string> <string name="accessibility_quick_settings_open_details" msgid="4879279912389052142">"వివరాలను తెరవండి."</string> <string name="accessibility_quick_settings_not_available" msgid="6860875849497473854">"<xliff:g id="REASON">%s</xliff:g> కారణంగా అందుబాటులో లేదు"</string> <string name="accessibility_quick_settings_open_settings" msgid="536838345505030893">"<xliff:g id="ID_1">%s</xliff:g> సెట్టింగ్లను తెరవండి."</string> - <string name="accessibility_quick_settings_edit" msgid="1523745183383815910">"సెట్టింగ్ల క్రమాన్ని సవరించండి."</string> + <string name="accessibility_quick_settings_edit" msgid="1523745183383815910">"సెట్టింగ్ల క్రమాన్ని ఎడిట్ చేయండి."</string> <string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"పవర్ మెనూ"</string> <string name="accessibility_quick_settings_page" msgid="7506322631645550961">"<xliff:g id="ID_2">%2$d</xliff:g>లో <xliff:g id="ID_1">%1$d</xliff:g>వ పేజీ"</string> <string name="tuner_lock_screen" msgid="2267383813241144544">"లాక్ స్క్రీన్"</string> @@ -963,7 +965,7 @@ <string name="notification_channel_alerts" msgid="3385787053375150046">"అలర్ట్లు"</string> <string name="notification_channel_battery" msgid="9219995638046695106">"బ్యాటరీ"</string> <string name="notification_channel_screenshot" msgid="7665814998932211997">"స్క్రీన్షాట్లు"</string> - <string name="notification_channel_general" msgid="4384774889645929705">"సాధారణ సందేశాలు"</string> + <string name="notification_channel_general" msgid="4384774889645929705">"సాధారణ మెసేజ్లు"</string> <string name="notification_channel_storage" msgid="2720725707628094977">"స్టోరేజ్"</string> <string name="notification_channel_hints" msgid="7703783206000346876">"సూచనలు"</string> <string name="instant_apps" msgid="8337185853050247304">"ఇన్స్టంట్ యాప్లు"</string> @@ -978,9 +980,9 @@ <string name="wifi_is_off" msgid="5389597396308001471">"Wi-Fi ఆఫ్లో ఉంది"</string> <string name="bt_is_off" msgid="7436344904889461591">"బ్లూటూత్ ఆఫ్లో ఉంది"</string> <string name="dnd_is_off" msgid="3185706903793094463">"అంతరాయం కలిగించవద్దు ఆఫ్లో ఉంది"</string> - <string name="qs_dnd_prompt_auto_rule" msgid="3535469468310002616">"స్వయంచాలక నియమం (<xliff:g id="ID_1">%s</xliff:g>) ద్వారా అంతరాయం కలిగించవద్దు ఆన్ చేయబడింది."</string> + <string name="qs_dnd_prompt_auto_rule" msgid="3535469468310002616">"ఆటోమేటిక్ నియమం (<xliff:g id="ID_1">%s</xliff:g>) ద్వారా అంతరాయం కలిగించవద్దు ఆన్ చేయబడింది."</string> <string name="qs_dnd_prompt_app" msgid="4027984447935396820">"యాప్ (<xliff:g id="ID_1">%s</xliff:g>) ద్వారా అంతరాయం కలిగించవద్దు ఆన్ చేయబడింది."</string> - <string name="qs_dnd_prompt_auto_rule_app" msgid="1841469944118486580">"స్వయంచాలక నియమం లేదా యాప్ ద్వారా అంతరాయం కలిగించవద్దు ఆన్ చేయబడింది."</string> + <string name="qs_dnd_prompt_auto_rule_app" msgid="1841469944118486580">"ఆటోమేటిక్ నియమం లేదా యాప్ ద్వారా అంతరాయం కలిగించవద్దు ఆన్ చేయబడింది."</string> <string name="qs_dnd_until" msgid="7844269319043747955">"<xliff:g id="ID_1">%s</xliff:g> వరకు"</string> <string name="qs_dnd_keep" msgid="3829697305432866434">"Keep"</string> <string name="qs_dnd_replace" msgid="7712119051407052689">"భర్తీ చేయి"</string> @@ -989,7 +991,7 @@ <string name="mobile_data_disable_title" msgid="5366476131671617790">"మొబైల్ డేటాను ఆఫ్ చేయాలా?"</string> <string name="mobile_data_disable_message" msgid="8604966027899770415">"\"<xliff:g id="CARRIER">%s</xliff:g>\" ద్వారా మీకు డేటా లేదా ఇంటర్నెట్కు యాక్సెస్ ఉండదు. Wi-Fi ద్వారా మాత్రమే ఇంటర్నెట్ అందుబాటులో ఉంటుంది."</string> <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"మీ క్యారియర్"</string> - <string name="touch_filtered_warning" msgid="8119511393338714836">"అనుమతి అభ్యర్థనకు ఒక యాప్ అడ్డు తగులుతున్నందున సెట్టింగ్లు మీ ప్రతిస్పందనను ధృవీకరించలేకపోయాయి."</string> + <string name="touch_filtered_warning" msgid="8119511393338714836">"అనుమతి రిక్వెస్ట్కు ఒక యాప్ అడ్డు తగులుతున్నందున సెట్టింగ్లు మీ ప్రతిస్పందనను ధృవీకరించలేకపోయాయి."</string> <string name="slice_permission_title" msgid="3262615140094151017">"<xliff:g id="APP_2">%2$s</xliff:g> స్లైస్లను చూపించడానికి <xliff:g id="APP_0">%1$s</xliff:g>ని అనుమతించండి?"</string> <string name="slice_permission_text_1" msgid="6675965177075443714">"- ఇది <xliff:g id="APP">%1$s</xliff:g> నుండి సమాచారాన్ని చదువుతుంది"</string> <string name="slice_permission_text_2" msgid="6758906940360746983">"- ఇది <xliff:g id="APP">%1$s</xliff:g> లోపల చర్యలు తీసుకుంటుంది"</string> diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml index 1fd1313719dc..df358a3c27d8 100644 --- a/packages/SystemUI/res/values-th/strings.xml +++ b/packages/SystemUI/res/values-th/strings.xml @@ -77,6 +77,7 @@ <string name="compat_mode_on" msgid="4963711187149440884">"ขยายจนเต็มหน้าจอ"</string> <string name="compat_mode_off" msgid="7682459748279487945">"ยืดจนเต็มหน้าจอ"</string> <string name="global_action_screenshot" msgid="2760267567509131654">"ภาพหน้าจอ"</string> + <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"ปิดใช้ Smart Lock แล้ว"</string> <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"ส่งรูปภาพ"</string> <string name="screenshot_saving_ticker" msgid="6519186952674544916">"กำลังบันทึกภาพหน้าจอ..."</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"กำลังบันทึกภาพหน้าจอ..."</string> diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml index 3837364887cf..a74345d4f0ca 100644 --- a/packages/SystemUI/res/values-tl/strings.xml +++ b/packages/SystemUI/res/values-tl/strings.xml @@ -77,6 +77,7 @@ <string name="compat_mode_on" msgid="4963711187149440884">"I-zoom upang punan screen"</string> <string name="compat_mode_off" msgid="7682459748279487945">"I-stretch upang mapuno screen"</string> <string name="global_action_screenshot" msgid="2760267567509131654">"Screenshot"</string> + <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Naka-disable ang Smart Lock"</string> <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"nagpadala ng larawan"</string> <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Sine-save ang screenshot…"</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"Sine-save ang screenshot…"</string> diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml index 1cd547ca7793..51e324588bf3 100644 --- a/packages/SystemUI/res/values-tr/strings.xml +++ b/packages/SystemUI/res/values-tr/strings.xml @@ -77,6 +77,7 @@ <string name="compat_mode_on" msgid="4963711187149440884">"Yakınlaştır (ekranı kaplasın)"</string> <string name="compat_mode_off" msgid="7682459748279487945">"Genişlet (ekran kapansın)"</string> <string name="global_action_screenshot" msgid="2760267567509131654">"Ekran görüntüsü"</string> + <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock devre dışı"</string> <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"bir resim gönderildi"</string> <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Ekran görüntüsü kaydediliyor..."</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"Ekran görüntüsü kaydediliyor..."</string> diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml index 70922aa77db3..b1febf53f736 100644 --- a/packages/SystemUI/res/values-uk/strings.xml +++ b/packages/SystemUI/res/values-uk/strings.xml @@ -77,6 +77,7 @@ <string name="compat_mode_on" msgid="4963711187149440884">"Масштабув. на весь екран"</string> <string name="compat_mode_off" msgid="7682459748279487945">"Розтягнути на весь екран"</string> <string name="global_action_screenshot" msgid="2760267567509131654">"Знімок екрана"</string> + <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock вимкнено"</string> <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"надіслане зображення"</string> <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Збереження знімка екрана..."</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"Збереження знімка екрана..."</string> diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml index 7ef014923960..fcb53a466714 100644 --- a/packages/SystemUI/res/values-ur/strings.xml +++ b/packages/SystemUI/res/values-ur/strings.xml @@ -77,6 +77,7 @@ <string name="compat_mode_on" msgid="4963711187149440884">"پوری سکرین پر زوم کریں"</string> <string name="compat_mode_off" msgid="7682459748279487945">"پوری سکرین پر پھیلائیں"</string> <string name="global_action_screenshot" msgid="2760267567509131654">"اسکرین شاٹ"</string> + <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock کو غیر فعال کیا گیا"</string> <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"ایک تصویر بھیجی"</string> <string name="screenshot_saving_ticker" msgid="6519186952674544916">"اسکرین شاٹ محفوظ ہو رہا ہے…"</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"اسکرین شاٹ محفوظ ہو رہا ہے…"</string> diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml index 08ac980d3bee..7822b84180e1 100644 --- a/packages/SystemUI/res/values-uz/strings.xml +++ b/packages/SystemUI/res/values-uz/strings.xml @@ -77,6 +77,7 @@ <string name="compat_mode_on" msgid="4963711187149440884">"Ekranga moslashtirish"</string> <string name="compat_mode_off" msgid="7682459748279487945">"Ekran hajmida cho‘zish"</string> <string name="global_action_screenshot" msgid="2760267567509131654">"Skrinshot"</string> + <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock faolsizlantirildi"</string> <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"rasm yuborildi"</string> <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Skrinshot saqlanmoqda…"</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"Skrinshot saqlanmoqda…"</string> diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml index 60b1232cd8a3..5a1ea4c3fc3a 100644 --- a/packages/SystemUI/res/values-vi/strings.xml +++ b/packages/SystemUI/res/values-vi/strings.xml @@ -77,6 +77,7 @@ <string name="compat_mode_on" msgid="4963711187149440884">"T.phóng để lấp đầy m.hình"</string> <string name="compat_mode_off" msgid="7682459748279487945">"Giãn ra để lấp đầy m.hình"</string> <string name="global_action_screenshot" msgid="2760267567509131654">"Chụp ảnh màn hình"</string> + <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Tính năng Smart Lock đã tắt"</string> <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"đã gửi hình ảnh"</string> <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Đang lưu ảnh chụp màn hình..."</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"Đang lưu ảnh chụp màn hình..."</string> diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml index 357e59e7ece5..b1968ee19535 100644 --- a/packages/SystemUI/res/values-zh-rCN/strings.xml +++ b/packages/SystemUI/res/values-zh-rCN/strings.xml @@ -77,6 +77,7 @@ <string name="compat_mode_on" msgid="4963711187149440884">"缩放以填满屏幕"</string> <string name="compat_mode_off" msgid="7682459748279487945">"拉伸以填满屏幕"</string> <string name="global_action_screenshot" msgid="2760267567509131654">"屏幕截图"</string> + <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock 已停用"</string> <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"发送了一张图片"</string> <string name="screenshot_saving_ticker" msgid="6519186952674544916">"正在保存屏幕截图..."</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"正在保存屏幕截图..."</string> @@ -1058,7 +1059,7 @@ <string name="accessibility_control_change_favorite" msgid="2943178027582253261">"收藏"</string> <string name="accessibility_control_change_unfavorite" msgid="6997408061750740327">"取消收藏"</string> <string name="accessibility_control_move" msgid="8980344493796647792">"移至位置 <xliff:g id="NUMBER">%d</xliff:g>"</string> - <string name="controls_favorite_default_title" msgid="967742178688938137">"控件"</string> + <string name="controls_favorite_default_title" msgid="967742178688938137">"控制"</string> <string name="controls_favorite_subtitle" msgid="6481675111056961083">"选择要从“快捷设置”菜单访问的控制项"</string> <string name="controls_favorite_rearrange" msgid="5616952398043063519">"按住并拖动即可重新排列控制器"</string> <string name="controls_favorite_removed" msgid="5276978408529217272">"已移除所有控制器"</string> diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml index f8ede3a55b88..7c048eab9893 100644 --- a/packages/SystemUI/res/values-zh-rHK/strings.xml +++ b/packages/SystemUI/res/values-zh-rHK/strings.xml @@ -77,6 +77,7 @@ <string name="compat_mode_on" msgid="4963711187149440884">"放大為全螢幕"</string> <string name="compat_mode_off" msgid="7682459748279487945">"放大為全螢幕"</string> <string name="global_action_screenshot" msgid="2760267567509131654">"螢幕截圖"</string> + <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock 已停用"</string> <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"已傳送圖片"</string> <string name="screenshot_saving_ticker" msgid="6519186952674544916">"正在儲存螢幕擷取畫面..."</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"正在儲存螢幕擷取畫面..."</string> diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml index df84fb0699c9..8647aed8c8bf 100644 --- a/packages/SystemUI/res/values-zh-rTW/strings.xml +++ b/packages/SystemUI/res/values-zh-rTW/strings.xml @@ -77,6 +77,7 @@ <string name="compat_mode_on" msgid="4963711187149440884">"放大為全螢幕"</string> <string name="compat_mode_off" msgid="7682459748279487945">"放大為全螢幕"</string> <string name="global_action_screenshot" msgid="2760267567509131654">"螢幕截圖"</string> + <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock 已停用"</string> <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"傳送了一張圖片"</string> <string name="screenshot_saving_ticker" msgid="6519186952674544916">"正在儲存螢幕截圖…"</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"正在儲存螢幕截圖…"</string> diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml index fe97daa7682e..b0c8121925ca 100644 --- a/packages/SystemUI/res/values-zu/strings.xml +++ b/packages/SystemUI/res/values-zu/strings.xml @@ -77,6 +77,7 @@ <string name="compat_mode_on" msgid="4963711187149440884">"Sondeza ukugcwalisa isikrini"</string> <string name="compat_mode_off" msgid="7682459748279487945">"Nweba ukugcwalisa isikrini"</string> <string name="global_action_screenshot" msgid="2760267567509131654">"Isithombe-skrini"</string> + <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Ukhiye oSmathi ukhutshaziwe"</string> <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"uthumele isithombe"</string> <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Ilondoloz umfanekiso weskrini..."</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"Ilondoloz umfanekiso weskrini..."</string> diff --git a/packages/SystemUI/res/values/attrs.xml b/packages/SystemUI/res/values/attrs.xml index b5337d363e12..3121ce37490a 100644 --- a/packages/SystemUI/res/values/attrs.xml +++ b/packages/SystemUI/res/values/attrs.xml @@ -69,6 +69,10 @@ <declare-styleable name="DateView"> <attr name="datePattern" format="string" /> </declare-styleable> + <declare-styleable name="VariableDateView"> + <attr name="longDatePattern" format="string" /> + <attr name="shortDatePattern" format="string" /> + </declare-styleable> <declare-styleable name="PseudoGridView"> <attr name="numColumns" format="integer" /> <attr name="verticalSpacing" format="dimension" /> diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml index 2260d2175268..50710c406480 100644 --- a/packages/SystemUI/res/values/colors.xml +++ b/packages/SystemUI/res/values/colors.xml @@ -69,6 +69,9 @@ <!-- Shadows under the clock, date and other keyguard text fields --> <color name="keyguard_shadow_color">#B2000000</color> + <!-- Color for the images in keyguard number pad buttons --> + <color name="keyguard_keypad_image_color">@android:color/background_light</color> + <!-- Color for rounded background for activated user in keyguard user switcher --> <color name="kg_user_switcher_activated_background_color">#26000000</color> <!-- Icon color for user avatars in keyguard user switcher --> diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index dc5c472ea0ed..9f2938e826e5 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -162,7 +162,7 @@ <!-- The maximum count of notifications on Keyguard. The rest will be collapsed in an overflow card. --> - <integer name="keyguard_max_notification_count">3</integer> + <integer name="keyguard_max_notification_count">-1</integer> <!-- Defines the implementation of the velocity tracker to be used for the panel expansion. Can be 'platform' or 'noisy' (i.e. for noisy touch screens). --> @@ -197,6 +197,11 @@ low powered state yet. --> <bool name="doze_long_press_uses_prox">true</bool> + <!-- Doze: whether the brightness sensor uses the proximity sensor. + If both this parameter and doze_selectively_register_prox are true, registration for the + brightness sensor won't occur when the display state is ON. --> + <bool name="doze_brightness_uses_prox">true</bool> + <!-- Doze: should notifications be used as a pulse signal? --> <bool name="doze_pulse_on_notifications">true</bool> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index e701ec7d714b..e383d8a7f519 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -56,11 +56,6 @@ <!-- The amount by which the arrow is shifted to avoid the finger--> <dimen name="navigation_edge_finger_offset">48dp</dimen> - <!-- Luminance threshold to determine black/white contrast for the navigation affordances --> - <item name="navigation_luminance_threshold" type="dimen" format="float">0.5</item> - <!-- Luminance change threshold that allows applying new value if difference was exceeded --> - <item name="navigation_luminance_change_threshold" type="dimen" format="float">0.05</item> - <dimen name="floating_rotation_button_diameter">40dp</dimen> <dimen name="floating_rotation_button_min_margin">20dp</dimen> <dimen name="floating_rotation_button_taskbar_left_margin">20dp</dimen> @@ -613,7 +608,7 @@ <dimen name="qs_detail_item_primary_text_size">16sp</dimen> <dimen name="qs_detail_item_secondary_text_size">14sp</dimen> <dimen name="qs_detail_empty_text_size">14sp</dimen> - <dimen name="qs_detail_margin_top">28dp</dimen> + <dimen name="qs_detail_header_margin_top">28dp</dimen> <dimen name="qs_detail_back_margin_end">16dp</dimen> <dimen name="qs_detail_header_text_padding">16dp</dimen> <dimen name="qs_data_usage_text_size">14sp</dimen> @@ -664,6 +659,8 @@ <!-- Padding between subtitles and the following text in the QSFooter dialog --> <dimen name="qs_footer_dialog_subtitle_padding">20dp</dimen> + <dimen name="qs_detail_margin_top">@*android:dimen/quick_qs_offset_height</dimen> + <dimen name="seek_bar_height">3dp</dimen> <dimen name="seek_bar_corner_radius">3dp</dimen> @@ -921,6 +918,7 @@ <dimen name="keyguard_affordance_fixed_height">48dp</dimen> <dimen name="keyguard_affordance_fixed_width">48dp</dimen> + <dimen name="keyguard_affordance_fixed_radius">24dp</dimen> <dimen name="keyguard_affordance_horizontal_offset">32dp</dimen> <dimen name="keyguard_affordance_vertical_offset">32dp</dimen> @@ -1138,6 +1136,7 @@ <dimen name="global_actions_button_padding">38dp</dimen> <dimen name="global_actions_corner_radius">28dp</dimen> <dimen name="global_actions_lite_padding">24dp</dimen> + <dimen name="global_actions_info_margin">32dp</dimen> <!-- The maximum offset in either direction that elements are moved horizontally to prevent burn-in on AOD. --> @@ -1471,6 +1470,10 @@ <!-- Maximum overshoot for the pulse expansion --> <dimen name="pulse_expansion_max_top_overshoot">32dp</dimen> + <!-- Alpha in duration in ms for the auth ripple to become fully vislble. If set to 0, + it is immediately visible. --> + <integer name="auth_ripple_alpha_in_duration">100</integer> + <dimen name="people_space_widget_radius">28dp</dimen> <dimen name="people_space_image_radius">20dp</dimen> <dimen name="people_space_messages_count_radius">12dp</dimen> @@ -1597,4 +1600,7 @@ <dimen name="ongoing_call_chip_corner_radius">28dp</dimen> <dimen name="drag_and_drop_icon_size">70dp</dimen> + + <!-- Signal icon in internet dialog --> + <dimen name="signal_strength_icon_size">24dp</dimen> </resources> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 47f474061301..1944a0fc6485 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -211,6 +211,8 @@ <!-- Power menu item for taking a screenshot [CHAR LIMIT=20]--> <string name="global_action_screenshot">Screenshot</string> + <!-- Message shown in power menu when smart lock has been disabled [CHAR_LIMIT=NONE] --> + <string name="global_action_smart_lock_disabled">Smart Lock disabled</string> <!-- text to show in place of RemoteInput images when they cannot be shown. [CHAR LIMIT=50] --> @@ -2987,4 +2989,37 @@ <!-- Content description for a chip in the status bar showing that the user is currently on a phone call. [CHAR LIMIT=NONE] --> <string name="ongoing_phone_call_content_description">Ongoing phone call</string> + + <!-- Provider Model: Title of the airplane mode in the internet dialog. [CHAR LIMIT=50] --> + <string name="airplane_mode">Airplane mode</string> + <!-- Provider Model: Default title of the mobile network in the mobile layout. [CHAR LIMIT=50] --> + <string name="mobile_data_settings_title">Mobile data</string> + <!-- Provider Model: Summary text separator for preferences including a short description + (eg. "Connected / 5G"). [CHAR LIMIT=50] --> + <string name="preference_summary_default_combination"><xliff:g id="state" example="Connected">%1$s</xliff:g> / <xliff:g id="networkMode" example="LTE">%2$s</xliff:g></string> + <!-- Provider Model: + Summary indicating that a SIM has an active mobile data connection [CHAR LIMIT=50] --> + <string name="mobile_data_connection_active">Connected</string> + <!-- Provider Model: + Summary indicating that a SIM has no mobile data connection [CHAR LIMIT=50] --> + <string name="mobile_data_off_summary">Internet won\u0027t auto\u2011connect</string> + <!-- Provider Model: + Summary indicating that a active SIM and no network available [CHAR LIMIT=50] --> + <string name="mobile_data_no_connection">No connection</string> + <!-- Provider Model: Summary indicating that no other networks available [CHAR LIMIT=50] --> + <string name="non_carrier_network_unavailable">No other networks available</string> + <!-- Provider Model: Summary indicating that no networks available [CHAR LIMIT=50] --> + <string name="all_network_unavailable">No networks available</string> + <!-- Provider Model: Panel title text for turning on the Wi-Fi networks. [CHAR LIMIT=40] --> + <string name="turn_on_wifi">Wi\u2011Fi</string> + <!-- Provider Model: Title for detail page of wifi network [CHAR LIMIT=30] --> + <string name="pref_title_network_details" msgid="7329759534269363308">"Network details"</string> + <!-- Provider Model: Panel subtitle for tapping a network to connect to internet. [CHAR LIMIT=60] --> + <string name="tap_a_network_to_connect">Tap a network to connect</string> + <!-- Provider Model: Wi-Fi settings. text displayed when Wi-Fi is on and network list is empty [CHAR LIMIT=50]--> + <string name="wifi_empty_list_wifi_on">Searching for networks\u2026</string> + <!-- Provider Model: Failure notification for connect --> + <string name="wifi_failed_connect_message">Failed to connect to network</string> + <!-- Provider Model: Title to see all the networks [CHAR LIMIT=50] --> + <string name="see_all_networks">See all</string> </resources> diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/animation/UnfoldMoveFromCenterAnimator.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/animation/UnfoldMoveFromCenterAnimator.kt index 4bb4eb9c020e..915e7f63ffb2 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/animation/UnfoldMoveFromCenterAnimator.kt +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/animation/UnfoldMoveFromCenterAnimator.kt @@ -20,7 +20,7 @@ import android.util.MathUtils.lerp import android.view.Surface import android.view.View import android.view.WindowManager -import com.android.unfold.UnfoldTransitionProgressProvider +import com.android.systemui.unfold.UnfoldTransitionProgressProvider import java.lang.ref.WeakReference /** diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/RegionSamplingHelper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/navigationbar/RegionSamplingHelper.java index c9a939974cc9..bcfb77461307 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/RegionSamplingHelper.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/navigationbar/RegionSamplingHelper.java @@ -14,12 +14,13 @@ * limitations under the License. */ -package com.android.systemui.navigationbar.gestural; +package com.android.systemui.shared.navigationbar; import static android.view.Display.DEFAULT_DISPLAY; -import android.content.res.Resources; +import android.annotation.TargetApi; import android.graphics.Rect; +import android.os.Build; import android.os.Handler; import android.view.CompositionSamplingListener; import android.view.SurfaceControl; @@ -27,17 +28,22 @@ import android.view.View; import android.view.ViewRootImpl; import android.view.ViewTreeObserver; -import com.android.systemui.R; - import java.io.PrintWriter; import java.util.concurrent.Executor; /** * A helper class to sample regions on the screen and inspect its luminosity. */ +@TargetApi(Build.VERSION_CODES.Q) public class RegionSamplingHelper implements View.OnAttachStateChangeListener, View.OnLayoutChangeListener { + // Luminance threshold to determine black/white contrast for the navigation affordances. + // Passing the threshold of this luminance value will make the button black otherwise white + private static final float NAVIGATION_LUMINANCE_THRESHOLD = 0.5f; + // Luminance change threshold that allows applying new value if difference was exceeded + private static final float NAVIGATION_LUMINANCE_CHANGE_THRESHOLD = 0.05f; + private final Handler mHandler = new Handler(); private final View mSampledView; @@ -62,9 +68,6 @@ public class RegionSamplingHelper implements View.OnAttachStateChangeListener, private boolean mWaitingOnDraw; private boolean mIsDestroyed; - // Passing the threshold of this luminance value will make the button black otherwise white - private final float mLuminanceThreshold; - private final float mLuminanceChangeThreshold; private boolean mFirstSamplingAfterStart; private boolean mWindowVisible; private boolean mWindowHasBlurs; @@ -100,9 +103,6 @@ public class RegionSamplingHelper implements View.OnAttachStateChangeListener, mSampledView.addOnAttachStateChangeListener(this); mSampledView.addOnLayoutChangeListener(this); - final Resources res = sampledView.getResources(); - mLuminanceThreshold = res.getFloat(R.dimen.navigation_luminance_threshold); - mLuminanceChangeThreshold = res.getFloat(R.dimen.navigation_luminance_change_threshold); mCallback = samplingCallback; } @@ -217,8 +217,10 @@ public class RegionSamplingHelper implements View.OnAttachStateChangeListener, // If the difference between the new luma and the current luma is larger than threshold // then apply the current luma, this is to prevent small changes causing colors to flicker - if (Math.abs(mCurrentMedianLuma - mLastMedianLuma) > mLuminanceChangeThreshold) { - mCallback.onRegionDarknessChanged(medianLuma < mLuminanceThreshold /* isRegionDark */); + if (Math.abs(mCurrentMedianLuma - mLastMedianLuma) + > NAVIGATION_LUMINANCE_CHANGE_THRESHOLD) { + mCallback.onRegionDarknessChanged( + medianLuma < NAVIGATION_LUMINANCE_THRESHOLD /* isRegionDark */); mLastMedianLuma = medianLuma; } } diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInitializer.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInitializer.java index 42bc1d0ea0ff..895b6cd96d6f 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInitializer.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInitializer.java @@ -15,31 +15,20 @@ package com.android.systemui.shared.plugins; import android.content.Context; -import android.os.Looper; /** * Provides necessary components for initializing {@link PluginManagerImpl}. */ public interface PluginInitializer { - Looper getBgLooper(); - /** - * Called from the bg looper during initialization of {@link PluginManagerImpl}. + * Return a list of plugins that don't get disabled when an exception occurs. */ - void onPluginManagerInit(); - - String[] getWhitelistedPlugins(Context context); + String[] getPrivilegedPlugins(Context context); - PluginEnabler getPluginEnabler(Context context); /** - * Called from {@link PluginManagerImpl#handleWtfs()}. + * Called from {@link PluginInstanceManager}. */ void handleWtfs(); - - /** - * Returns if pluging manager should run in debug mode. - */ - boolean isDebuggable(); } diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java index 2b35bcd9a3ea..dcd3b3eb5dd4 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java @@ -14,6 +14,7 @@ package com.android.systemui.shared.plugins; +import android.app.LoadedApk; import android.app.Notification; import android.app.Notification.Action; import android.app.NotificationManager; @@ -28,9 +29,8 @@ import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ResolveInfo; import android.content.res.Resources; import android.net.Uri; -import android.os.Handler; -import android.os.Looper; -import android.os.Message; +import android.text.TextUtils; +import android.util.ArrayMap; import android.util.ArraySet; import android.util.Log; import android.view.LayoutInflater; @@ -42,9 +42,13 @@ import com.android.systemui.plugins.PluginFragment; import com.android.systemui.plugins.PluginListener; import com.android.systemui.shared.plugins.VersionInfo.InvalidVersionException; +import dalvik.system.PathClassLoader; + +import java.io.File; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; +import java.util.Map; +import java.util.concurrent.Executor; public class PluginInstanceManager<T extends Plugin> { @@ -58,81 +62,70 @@ public class PluginInstanceManager<T extends Plugin> { private final String mAction; private final boolean mAllowMultiple; private final VersionInfo mVersion; + private final NotificationManager mNotificationManager; + private final PluginEnabler mPluginEnabler; + private final InstanceFactory<T> mInstanceFactory; + private final ArraySet<String> mPrivilegedPlugins = new ArraySet<>(); + private final Map<String, ClassLoader> mClassLoaders = new ArrayMap<>(); @VisibleForTesting - final MainHandler mMainHandler; - @VisibleForTesting - final PluginHandler mPluginHandler; - private final boolean isDebuggable; + private final ArrayList<PluginInfo<T>> mPlugins = new ArrayList<>(); + private final boolean mIsDebuggable; private final PackageManager mPm; - private final PluginManagerImpl mManager; - private final ArraySet<String> mWhitelistedPlugins = new ArraySet<>(); - - PluginInstanceManager(Context context, String action, PluginListener<T> listener, - boolean allowMultiple, Looper looper, VersionInfo version, PluginManagerImpl manager) { - this(context, context.getPackageManager(), action, listener, allowMultiple, looper, version, - manager, manager.isDebuggable(), manager.getWhitelistedPlugins()); - } - - @VisibleForTesting - PluginInstanceManager(Context context, PackageManager pm, String action, - PluginListener<T> listener, boolean allowMultiple, Looper looper, VersionInfo version, - PluginManagerImpl manager, boolean debuggable, String[] pluginWhitelist) { - mMainHandler = new MainHandler(Looper.getMainLooper()); - mPluginHandler = new PluginHandler(looper); - mManager = manager; + private final PluginInitializer mInitializer; + private final Executor mMainExecutor; + private final Executor mBgExecutor; + + private PluginManagerImpl.ClassLoaderFilter mParentClassLoader; + + private PluginInstanceManager(Context context, PackageManager pm, String action, + PluginListener<T> listener, boolean allowMultiple, Executor mainExecutor, + Executor bgExecutor, VersionInfo version, boolean debuggable, + PluginInitializer initializer, NotificationManager notificationManager, + PluginEnabler pluginEnabler, List<String> privilegedPlugins, + InstanceFactory<T> instanceFactory) { + mInitializer = initializer; + mMainExecutor = mainExecutor; + mBgExecutor = bgExecutor; mContext = context; mPm = pm; mAction = action; mListener = listener; mAllowMultiple = allowMultiple; mVersion = version; - mWhitelistedPlugins.addAll(Arrays.asList(pluginWhitelist)); - isDebuggable = debuggable; - } - - public PluginInfo<T> getPlugin() { - if (Looper.myLooper() != Looper.getMainLooper()) { - throw new RuntimeException("Must be called from UI thread"); - } - mPluginHandler.handleQueryPlugins(null /* All packages */); - if (mPluginHandler.mPlugins.size() > 0) { - mMainHandler.removeMessages(MainHandler.PLUGIN_CONNECTED); - PluginInfo<T> info = mPluginHandler.mPlugins.get(0); - PluginPrefs.setHasPlugins(mContext); - info.mPlugin.onCreate(mContext, info.mPluginContext); - return info; - } - return null; + mNotificationManager = notificationManager; + mPluginEnabler = pluginEnabler; + mInstanceFactory = instanceFactory; + mPrivilegedPlugins.addAll(privilegedPlugins); + mIsDebuggable = debuggable; } public void loadAll() { if (DEBUG) Log.d(TAG, "startListening"); - mPluginHandler.sendEmptyMessage(PluginHandler.QUERY_ALL); + mBgExecutor.execute(this::queryAll); } public void destroy() { if (DEBUG) Log.d(TAG, "stopListening"); - ArrayList<PluginInfo> plugins = new ArrayList<PluginInfo>(mPluginHandler.mPlugins); - for (PluginInfo plugin : plugins) { - mMainHandler.obtainMessage(MainHandler.PLUGIN_DISCONNECTED, - plugin.mPlugin).sendToTarget(); + ArrayList<PluginInfo<T>> plugins = new ArrayList<>(mPlugins); + for (PluginInfo<T> pluginInfo : plugins) { + mMainExecutor.execute(() -> onPluginDisconnected(pluginInfo.mPlugin)); } } public void onPackageRemoved(String pkg) { - mPluginHandler.obtainMessage(PluginHandler.REMOVE_PKG, pkg).sendToTarget(); + mBgExecutor.execute(() -> removePkg(pkg)); } public void onPackageChange(String pkg) { - mPluginHandler.obtainMessage(PluginHandler.REMOVE_PKG, pkg).sendToTarget(); - mPluginHandler.obtainMessage(PluginHandler.QUERY_PKG, pkg).sendToTarget(); + mBgExecutor.execute(() -> removePkg(pkg)); + mBgExecutor.execute(() -> queryPkg(pkg)); } public boolean checkAndDisable(String className) { boolean disableAny = false; - ArrayList<PluginInfo> plugins = new ArrayList<PluginInfo>(mPluginHandler.mPlugins); - for (PluginInfo info : plugins) { + ArrayList<PluginInfo<T>> plugins = new ArrayList<>(mPlugins); + for (PluginInfo<T> info : plugins) { if (className.startsWith(info.mPackage)) { disableAny |= disable(info, PluginEnabler.DISABLED_FROM_EXPLICIT_CRASH); } @@ -141,7 +134,7 @@ public class PluginInstanceManager<T extends Plugin> { } public boolean disableAll() { - ArrayList<PluginInfo> plugins = new ArrayList<PluginInfo>(mPluginHandler.mPlugins); + ArrayList<PluginInfo<T>> plugins = new ArrayList<>(mPlugins); boolean disabledAny = false; for (int i = 0; i < plugins.size(); i++) { disabledAny |= disable(plugins.get(i), PluginEnabler.DISABLED_FROM_SYSTEM_CRASH); @@ -149,8 +142,22 @@ public class PluginInstanceManager<T extends Plugin> { return disabledAny; } - private boolean isPluginWhitelisted(ComponentName pluginName) { - for (String componentNameOrPackage : mWhitelistedPlugins) { + private boolean isPluginPackagePrivileged(String packageName) { + for (String componentNameOrPackage : mPrivilegedPlugins) { + ComponentName componentName = ComponentName.unflattenFromString(componentNameOrPackage); + if (componentName != null) { + if (componentName.getPackageName().equals(packageName)) { + return true; + } + } else if (componentNameOrPackage.equals(packageName)) { + return true; + } + } + return false; + } + + private boolean isPluginPrivileged(ComponentName pluginName) { + for (String componentNameOrPackage : mPrivilegedPlugins) { ComponentName componentName = ComponentName.unflattenFromString(componentNameOrPackage); if (componentName == null) { if (componentNameOrPackage.equals(pluginName.getPackageName())) { @@ -165,7 +172,7 @@ public class PluginInstanceManager<T extends Plugin> { return false; } - private boolean disable(PluginInfo info, @PluginEnabler.DisableReason int reason) { + private boolean disable(PluginInfo<T> info, @PluginEnabler.DisableReason int reason) { // Live by the sword, die by the sword. // Misbehaving plugins get disabled and won't come back until uninstall/reinstall. @@ -173,19 +180,19 @@ public class PluginInstanceManager<T extends Plugin> { // If a plugin is detected in the stack of a crash then this will be called for that // plugin, if the plugin causing a crash cannot be identified, they are all disabled // assuming one of them must be bad. - if (isPluginWhitelisted(pluginComponent)) { + if (isPluginPrivileged(pluginComponent)) { // Don't disable whitelisted plugins as they are a part of the OS. return false; } Log.w(TAG, "Disabling plugin " + pluginComponent.flattenToShortString()); - mManager.getPluginEnabler().setDisabled(pluginComponent, reason); + mPluginEnabler.setDisabled(pluginComponent, reason); return true; } - public <T> boolean dependsOn(Plugin p, Class<T> cls) { - ArrayList<PluginInfo> plugins = new ArrayList<PluginInfo>(mPluginHandler.mPlugins); - for (PluginInfo info : plugins) { + <C> boolean dependsOn(Plugin p, Class<C> cls) { + ArrayList<PluginInfo<T>> plugins = new ArrayList<>(mPlugins); + for (PluginInfo<T> info : plugins) { if (info.mPlugin.getClass().getName().equals(p.getClass().getName())) { return info.mVersion != null && info.mVersion.hasClass(cls); } @@ -199,221 +206,259 @@ public class PluginInstanceManager<T extends Plugin> { getClass().getSimpleName(), hashCode(), mAction); } - private class MainHandler extends Handler { - private static final int PLUGIN_CONNECTED = 1; - private static final int PLUGIN_DISCONNECTED = 2; - - public MainHandler(Looper looper) { - super(looper); + private void onPluginConnected(PluginInfo<T> pluginInfo) { + if (DEBUG) Log.d(TAG, "onPluginConnected"); + PluginPrefs.setHasPlugins(mContext); + mInitializer.handleWtfs(); + if (!(pluginInfo.mPlugin instanceof PluginFragment)) { + // Only call onCreate for plugins that aren't fragments, as fragments + // will get the onCreate as part of the fragment lifecycle. + pluginInfo.mPlugin.onCreate(mContext, pluginInfo.mPluginContext); } + mListener.onPluginConnected(pluginInfo.mPlugin, pluginInfo.mPluginContext); + } - @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case PLUGIN_CONNECTED: - if (DEBUG) Log.d(TAG, "onPluginConnected"); - PluginPrefs.setHasPlugins(mContext); - PluginInfo<T> info = (PluginInfo<T>) msg.obj; - mManager.handleWtfs(); - if (!(msg.obj instanceof PluginFragment)) { - // Only call onDestroy for plugins that aren't fragments, as fragments - // will get the onCreate as part of the fragment lifecycle. - info.mPlugin.onCreate(mContext, info.mPluginContext); - } - mListener.onPluginConnected(info.mPlugin, info.mPluginContext); - break; - case PLUGIN_DISCONNECTED: - if (DEBUG) Log.d(TAG, "onPluginDisconnected"); - mListener.onPluginDisconnected((T) msg.obj); - if (!(msg.obj instanceof PluginFragment)) { - // Only call onDestroy for plugins that aren't fragments, as fragments - // will get the onDestroy as part of the fragment lifecycle. - ((T) msg.obj).onDestroy(); - } - break; - default: - super.handleMessage(msg); - break; - } + private void onPluginDisconnected(T plugin) { + if (DEBUG) Log.d(TAG, "onPluginDisconnected"); + mListener.onPluginDisconnected(plugin); + if (!(plugin instanceof PluginFragment)) { + // Only call onDestroy for plugins that aren't fragments, as fragments + // will get the onDestroy as part of the fragment lifecycle. + plugin.onDestroy(); } } - private class PluginHandler extends Handler { - private static final int QUERY_ALL = 1; - private static final int QUERY_PKG = 2; - private static final int REMOVE_PKG = 3; - - private final ArrayList<PluginInfo<T>> mPlugins = new ArrayList<>(); - - public PluginHandler(Looper looper) { - super(looper); + private void queryAll() { + if (DEBUG) Log.d(TAG, "queryAll " + mAction); + for (int i = mPlugins.size() - 1; i >= 0; i--) { + PluginInfo<T> pluginInfo = mPlugins.get(i); + mMainExecutor.execute(() -> onPluginDisconnected(pluginInfo.mPlugin)); } + mPlugins.clear(); + handleQueryPlugins(null); + } - @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case QUERY_ALL: - if (DEBUG) Log.d(TAG, "queryAll " + mAction); - for (int i = mPlugins.size() - 1; i >= 0; i--) { - PluginInfo<T> pluginInfo = mPlugins.get(i); - mMainHandler.obtainMessage( - MainHandler.PLUGIN_DISCONNECTED, pluginInfo.mPlugin).sendToTarget(); - } - mPlugins.clear(); - handleQueryPlugins(null); - break; - case REMOVE_PKG: - String pkg = (String) msg.obj; - for (int i = mPlugins.size() - 1; i >= 0; i--) { - final PluginInfo<T> plugin = mPlugins.get(i); - if (plugin.mPackage.equals(pkg)) { - mMainHandler.obtainMessage(MainHandler.PLUGIN_DISCONNECTED, - plugin.mPlugin).sendToTarget(); - mPlugins.remove(i); - } - } - break; - case QUERY_PKG: - String p = (String) msg.obj; - if (DEBUG) Log.d(TAG, "queryPkg " + p + " " + mAction); - if (mAllowMultiple || (mPlugins.size() == 0)) { - handleQueryPlugins(p); - } else { - if (DEBUG) Log.d(TAG, "Too many of " + mAction); - } - break; - default: - super.handleMessage(msg); + private void removePkg(String pkg) { + for (int i = mPlugins.size() - 1; i >= 0; i--) { + final PluginInfo<T> pluginInfo = mPlugins.get(i); + if (pluginInfo.mPackage.equals(pkg)) { + mMainExecutor.execute(() -> onPluginDisconnected(pluginInfo.mPlugin)); + mPlugins.remove(i); } } + } - private void handleQueryPlugins(String pkgName) { - // This isn't actually a service and shouldn't ever be started, but is - // a convenient PM based way to manage our plugins. - Intent intent = new Intent(mAction); - if (pkgName != null) { - intent.setPackage(pkgName); - } - List<ResolveInfo> result = mPm.queryIntentServices(intent, 0); - if (DEBUG) Log.d(TAG, "Found " + result.size() + " plugins"); - if (result.size() > 1 && !mAllowMultiple) { - // TODO: Show warning. - Log.w(TAG, "Multiple plugins found for " + mAction); - if (DEBUG) { - for (ResolveInfo info : result) { - ComponentName name = new ComponentName(info.serviceInfo.packageName, - info.serviceInfo.name); - Log.w(TAG, " " + name); - } + private void queryPkg(String pkg) { + if (DEBUG) Log.d(TAG, "queryPkg " + pkg + " " + mAction); + if (mAllowMultiple || (mPlugins.size() == 0)) { + handleQueryPlugins(pkg); + } else { + if (DEBUG) Log.d(TAG, "Too many of " + mAction); + } + } + + private void handleQueryPlugins(String pkgName) { + // This isn't actually a service and shouldn't ever be started, but is + // a convenient PM based way to manage our plugins. + Intent intent = new Intent(mAction); + if (pkgName != null) { + intent.setPackage(pkgName); + } + List<ResolveInfo> result = mPm.queryIntentServices(intent, 0); + if (DEBUG) Log.d(TAG, "Found " + result.size() + " plugins"); + if (result.size() > 1 && !mAllowMultiple) { + // TODO: Show warning. + Log.w(TAG, "Multiple plugins found for " + mAction); + if (DEBUG) { + for (ResolveInfo info : result) { + ComponentName name = new ComponentName(info.serviceInfo.packageName, + info.serviceInfo.name); + Log.w(TAG, " " + name); } - return; - } - for (ResolveInfo info : result) { - ComponentName name = new ComponentName(info.serviceInfo.packageName, - info.serviceInfo.name); - PluginInfo<T> t = handleLoadPlugin(name); - if (t == null) continue; - - // add plugin before sending PLUGIN_CONNECTED message - mPlugins.add(t); - mMainHandler.obtainMessage(mMainHandler.PLUGIN_CONNECTED, t).sendToTarget(); } + return; + } + for (ResolveInfo info : result) { + ComponentName name = new ComponentName(info.serviceInfo.packageName, + info.serviceInfo.name); + PluginInfo<T> pluginInfo = handleLoadPlugin(name); + if (pluginInfo == null) continue; + + // add plugin before sending PLUGIN_CONNECTED message + mPlugins.add(pluginInfo); + mMainExecutor.execute(() -> onPluginConnected(pluginInfo)); } + } - protected PluginInfo<T> handleLoadPlugin(ComponentName component) { - // This was already checked, but do it again here to make extra extra sure, we don't - // use these on production builds. - if (!isDebuggable && !isPluginWhitelisted(component)) { - // Never ever ever allow these on production builds, they are only for prototyping. - Log.w(TAG, "Plugin cannot be loaded on production build: " + component); - return null; - } - if (!mManager.getPluginEnabler().isEnabled(component)) { - if (DEBUG) Log.d(TAG, "Plugin is not enabled, aborting load: " + component); + protected PluginInfo<T> handleLoadPlugin(ComponentName component) { + // This was already checked, but do it again here to make extra extra sure, we don't + // use these on production builds. + if (!mIsDebuggable && !isPluginPrivileged(component)) { + // Never ever ever allow these on production builds, they are only for prototyping. + Log.w(TAG, "Plugin cannot be loaded on production build: " + component); + return null; + } + if (!mPluginEnabler.isEnabled(component)) { + if (DEBUG) Log.d(TAG, "Plugin is not enabled, aborting load: " + component); + return null; + } + String pkg = component.getPackageName(); + String cls = component.getClassName(); + try { + ApplicationInfo info = mPm.getApplicationInfo(pkg, 0); + // TODO: This probably isn't needed given that we don't have IGNORE_SECURITY on + if (mPm.checkPermission(PLUGIN_PERMISSION, pkg) + != PackageManager.PERMISSION_GRANTED) { + Log.d(TAG, "Plugin doesn't have permission: " + pkg); return null; } - String pkg = component.getPackageName(); - String cls = component.getClassName(); + // Create our own ClassLoader so we can use our own code as the parent. + ClassLoader classLoader = getClassLoader(info); + Context pluginContext = new PluginContextWrapper( + mContext.createApplicationContext(info, 0), classLoader); + Class<?> pluginClass = Class.forName(cls, true, classLoader); + // TODO: Only create the plugin before version check if we need it for + // legacy version check. + T plugin = mInstanceFactory.create(pluginClass); try { - ApplicationInfo info = mPm.getApplicationInfo(pkg, 0); - // TODO: This probably isn't needed given that we don't have IGNORE_SECURITY on - if (mPm.checkPermission(PLUGIN_PERMISSION, pkg) - != PackageManager.PERMISSION_GRANTED) { - Log.d(TAG, "Plugin doesn't have permission: " + pkg); - return null; - } - // Create our own ClassLoader so we can use our own code as the parent. - ClassLoader classLoader = mManager.getClassLoader(info); - Context pluginContext = new PluginContextWrapper( - mContext.createApplicationContext(info, 0), classLoader); - Class<?> pluginClass = Class.forName(cls, true, classLoader); - // TODO: Only create the plugin before version check if we need it for - // legacy version check. - T plugin = (T) pluginClass.newInstance(); + VersionInfo version = checkVersion(pluginClass, plugin, mVersion); + if (DEBUG) Log.d(TAG, "createPlugin"); + return new PluginInfo<>(pkg, cls, plugin, pluginContext, version); + } catch (InvalidVersionException e) { + final int icon = Resources.getSystem().getIdentifier( + "stat_sys_warning", "drawable", "android"); + final int color = Resources.getSystem().getIdentifier( + "system_notification_accent_color", "color", "android"); + final Notification.Builder nb = new Notification.Builder(mContext, + PluginManager.NOTIFICATION_CHANNEL_ID) + .setStyle(new Notification.BigTextStyle()) + .setSmallIcon(icon) + .setWhen(0) + .setShowWhen(false) + .setVisibility(Notification.VISIBILITY_PUBLIC) + .setColor(mContext.getColor(color)); + String label = cls; try { - VersionInfo version = checkVersion(pluginClass, plugin, mVersion); - if (DEBUG) Log.d(TAG, "createPlugin"); - return new PluginInfo(pkg, cls, plugin, pluginContext, version); - } catch (InvalidVersionException e) { - final int icon = Resources.getSystem().getIdentifier( - "stat_sys_warning", "drawable", "android"); - final int color = Resources.getSystem().getIdentifier( - "system_notification_accent_color", "color", "android"); - final Notification.Builder nb = new Notification.Builder(mContext, - PluginManager.NOTIFICATION_CHANNEL_ID) - .setStyle(new Notification.BigTextStyle()) - .setSmallIcon(icon) - .setWhen(0) - .setShowWhen(false) - .setVisibility(Notification.VISIBILITY_PUBLIC) - .setColor(mContext.getColor(color)); - String label = cls; - try { - label = mPm.getServiceInfo(component, 0).loadLabel(mPm).toString(); - } catch (NameNotFoundException e2) { - } - if (!e.isTooNew()) { - // Localization not required as this will never ever appear in a user build. - nb.setContentTitle("Plugin \"" + label + "\" is too old") - .setContentText("Contact plugin developer to get an updated" - + " version.\n" + e.getMessage()); - } else { - // Localization not required as this will never ever appear in a user build. - nb.setContentTitle("Plugin \"" + label + "\" is too new") - .setContentText("Check to see if an OTA is available.\n" - + e.getMessage()); - } - Intent i = new Intent(PluginManagerImpl.DISABLE_PLUGIN).setData( - Uri.parse("package://" + component.flattenToString())); - PendingIntent pi = PendingIntent.getBroadcast(mContext, 0, i, - PendingIntent.FLAG_IMMUTABLE); - nb.addAction(new Action.Builder(null, "Disable plugin", pi).build()); - mContext.getSystemService(NotificationManager.class) - .notify(SystemMessage.NOTE_PLUGIN, nb.build()); - // TODO: Warn user. - Log.w(TAG, "Plugin has invalid interface version " + plugin.getVersion() - + ", expected " + mVersion); - return null; + label = mPm.getServiceInfo(component, 0).loadLabel(mPm).toString(); + } catch (NameNotFoundException e2) { } - } catch (Throwable e) { - Log.w(TAG, "Couldn't load plugin: " + pkg, e); + if (!e.isTooNew()) { + // Localization not required as this will never ever appear in a user build. + nb.setContentTitle("Plugin \"" + label + "\" is too old") + .setContentText("Contact plugin developer to get an updated" + + " version.\n" + e.getMessage()); + } else { + // Localization not required as this will never ever appear in a user build. + nb.setContentTitle("Plugin \"" + label + "\" is too new") + .setContentText("Check to see if an OTA is available.\n" + + e.getMessage()); + } + Intent i = new Intent(PluginManagerImpl.DISABLE_PLUGIN).setData( + Uri.parse("package://" + component.flattenToString())); + PendingIntent pi = PendingIntent.getBroadcast(mContext, 0, i, + PendingIntent.FLAG_IMMUTABLE); + nb.addAction(new Action.Builder(null, "Disable plugin", pi).build()); + mNotificationManager.notify(SystemMessage.NOTE_PLUGIN, nb.build()); + // TODO: Warn user. + Log.w(TAG, "Plugin has invalid interface version " + plugin.getVersion() + + ", expected " + mVersion); return null; } + } catch (Throwable e) { + Log.w(TAG, "Couldn't load plugin: " + pkg, e); + return null; } + } - private VersionInfo checkVersion(Class<?> pluginClass, T plugin, VersionInfo version) - throws InvalidVersionException { - VersionInfo pv = new VersionInfo().addClass(pluginClass); - if (pv.hasVersionInfo()) { - version.checkVersion(pv); - } else { - int fallbackVersion = plugin.getVersion(); - if (fallbackVersion != version.getDefaultVersion()) { - throw new InvalidVersionException("Invalid legacy version", false); - } - return null; + private VersionInfo checkVersion(Class<?> pluginClass, T plugin, VersionInfo version) + throws InvalidVersionException { + VersionInfo pv = new VersionInfo().addClass(pluginClass); + if (pv.hasVersionInfo()) { + version.checkVersion(pv); + } else { + int fallbackVersion = plugin.getVersion(); + if (fallbackVersion != version.getDefaultVersion()) { + throw new InvalidVersionException("Invalid legacy version", false); } - return pv; + return null; + } + return pv; + } + + /** Returns class loader specific for the given plugin. */ + public ClassLoader getClassLoader(ApplicationInfo appInfo) { + if (!mIsDebuggable && !isPluginPackagePrivileged(appInfo.packageName)) { + Log.w(TAG, "Cannot get class loader for non-privileged plugin. Src:" + + appInfo.sourceDir + ", pkg: " + appInfo.packageName); + return null; + } + if (mClassLoaders.containsKey(appInfo.packageName)) { + return mClassLoaders.get(appInfo.packageName); + } + + List<String> zipPaths = new ArrayList<>(); + List<String> libPaths = new ArrayList<>(); + LoadedApk.makePaths(null, true, appInfo, zipPaths, libPaths); + ClassLoader classLoader = new PathClassLoader( + TextUtils.join(File.pathSeparator, zipPaths), + TextUtils.join(File.pathSeparator, libPaths), + getParentClassLoader()); + mClassLoaders.put(appInfo.packageName, classLoader); + return classLoader; + } + + private ClassLoader getParentClassLoader() { + if (mParentClassLoader == null) { + // Lazily load this so it doesn't have any effect on devices without plugins. + mParentClassLoader = new PluginManagerImpl.ClassLoaderFilter( + getClass().getClassLoader(), "com.android.systemui.plugin"); + } + return mParentClassLoader; + } + + /** + * Construct a {@link PluginInstanceManager} + */ + public static class Factory { + private final Context mContext; + private final PackageManager mPackageManager; + private final Executor mMainExecutor; + private final Executor mBgExecutor; + private final PluginInitializer mInitializer; + private final NotificationManager mNotificationManager; + private final PluginEnabler mPluginEnabler; + private final List<String> mPrivilegedPlugins; + private InstanceFactory<?> mInstanceFactory; + + public Factory(Context context, PackageManager packageManager, + Executor mainExecutor, Executor bgExecutor, PluginInitializer initializer, + NotificationManager notificationManager, PluginEnabler pluginEnabler, + List<String> privilegedPlugins) { + mContext = context; + mPackageManager = packageManager; + mMainExecutor = mainExecutor; + mBgExecutor = bgExecutor; + mInitializer = initializer; + mNotificationManager = notificationManager; + mPluginEnabler = pluginEnabler; + mPrivilegedPlugins = privilegedPlugins; + + mInstanceFactory = new InstanceFactory<>(); + } + + @VisibleForTesting + <T extends Plugin> Factory setInstanceFactory(InstanceFactory<T> instanceFactory) { + mInstanceFactory = instanceFactory; + return this; + } + + <T extends Plugin> PluginInstanceManager<T> create( + String action, PluginListener<T> listener, boolean allowMultiple, + VersionInfo version, boolean debuggable) { + return new PluginInstanceManager<T>(mContext, mPackageManager, action, listener, + allowMultiple, mMainExecutor, mBgExecutor, version, debuggable, + mInitializer, mNotificationManager, mPluginEnabler, + mPrivilegedPlugins, (InstanceFactory<T>) mInstanceFactory); } } @@ -443,10 +488,10 @@ public class PluginInstanceManager<T extends Plugin> { } } - static class PluginInfo<T> { + static class PluginInfo<T extends Plugin> { private final Context mPluginContext; private final VersionInfo mVersion; - private String mClass; + private final String mClass; T mPlugin; String mPackage; @@ -459,4 +504,10 @@ public class PluginInstanceManager<T extends Plugin> { mVersion = info; } } + + static class InstanceFactory<T extends Plugin> { + T create(Class cls) throws IllegalAccessException, InstantiationException { + return (T) cls.newInstance(); + } + } } diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManager.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManager.java index 3f907a8aa348..d264bf2fae52 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManager.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManager.java @@ -27,10 +27,8 @@ public interface PluginManager { // must be one of the channels created in NotificationChannels.java String NOTIFICATION_CHANNEL_ID = "ALR"; - String[] getWhitelistedPlugins(); - - <T extends Plugin> T getOneShotPlugin(Class<T> cls); - <T extends Plugin> T getOneShotPlugin(String action, Class<?> cls); + /** Returns plugins that don't get disabled when an exceptoin occurs. */ + String[] getPrivilegedPlugins(); <T extends Plugin> void addPluginListener(PluginListener<T> listener, Class<?> cls); <T extends Plugin> void addPluginListener(PluginListener<T> listener, Class<?> cls, @@ -38,7 +36,7 @@ public interface PluginManager { <T extends Plugin> void addPluginListener(String action, PluginListener<T> listener, Class<?> cls); <T extends Plugin> void addPluginListener(String action, PluginListener<T> listener, - Class cls, boolean allowMultiple); + Class<?> cls, boolean allowMultiple); void removePluginListener(PluginListener<?> listener); diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java index 2b4cdd6cf575..ea7b0c34136a 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java @@ -14,48 +14,31 @@ package com.android.systemui.shared.plugins; -import android.app.LoadedApk; -import android.app.Notification; -import android.app.Notification.Action; import android.app.NotificationManager; -import android.app.PendingIntent; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageManager; -import android.content.pm.PackageManager.NameNotFoundException; -import android.content.res.Resources; import android.net.Uri; import android.os.Build; -import android.os.Handler; -import android.os.Looper; import android.os.SystemProperties; -import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; import android.util.Log; import android.widget.Toast; -import com.android.internal.annotations.VisibleForTesting; import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; import com.android.systemui.plugins.Plugin; import com.android.systemui.plugins.PluginListener; -import com.android.systemui.plugins.annotations.ProvidesInterface; -import com.android.systemui.shared.plugins.PluginInstanceManager.PluginInfo; -import dalvik.system.PathClassLoader; - -import java.io.File; import java.io.FileDescriptor; import java.io.PrintWriter; import java.lang.Thread.UncaughtExceptionHandler; -import java.util.ArrayList; -import java.util.Arrays; import java.util.List; import java.util.Map; +import java.util.Optional; + /** * @see Plugin */ @@ -64,92 +47,42 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage private static final String TAG = PluginManagerImpl.class.getSimpleName(); static final String DISABLE_PLUGIN = "com.android.systemui.action.DISABLE_PLUGIN"; - private final ArrayMap<PluginListener<?>, PluginInstanceManager> mPluginMap + private final ArrayMap<PluginListener<?>, PluginInstanceManager<?>> mPluginMap = new ArrayMap<>(); private final Map<String, ClassLoader> mClassLoaders = new ArrayMap<>(); - private final ArraySet<String> mOneShotPackages = new ArraySet<>(); - private final ArraySet<String> mWhitelistedPlugins = new ArraySet<>(); + private final ArraySet<String> mPrivilegedPlugins = new ArraySet<>(); private final Context mContext; - private final PluginInstanceManagerFactory mFactory; + private final PluginInstanceManager.Factory mInstanceManagerFactory; private final boolean mIsDebuggable; private final PluginPrefs mPluginPrefs; private final PluginEnabler mPluginEnabler; - private final PluginInitializer mPluginInitializer; - private ClassLoaderFilter mParentClassLoader; private boolean mListening; - private boolean mHasOneShot; - private Looper mLooper; - - public PluginManagerImpl(Context context, PluginInitializer initializer) { - this(context, new PluginInstanceManagerFactory(), initializer.isDebuggable(), - Thread.getUncaughtExceptionPreHandler(), initializer); - } - @VisibleForTesting - PluginManagerImpl(Context context, PluginInstanceManagerFactory factory, boolean debuggable, - UncaughtExceptionHandler defaultHandler, final PluginInitializer initializer) { + public PluginManagerImpl(Context context, + PluginInstanceManager.Factory instanceManagerFactory, + boolean debuggable, + Optional<UncaughtExceptionHandler> defaultHandlerOptional, + PluginEnabler pluginEnabler, + PluginPrefs pluginPrefs, + List<String> privilegedPlugins) { mContext = context; - mFactory = factory; - mLooper = initializer.getBgLooper(); + mInstanceManagerFactory = instanceManagerFactory; mIsDebuggable = debuggable; - mWhitelistedPlugins.addAll(Arrays.asList(initializer.getWhitelistedPlugins(mContext))); - mPluginPrefs = new PluginPrefs(mContext); - mPluginEnabler = initializer.getPluginEnabler(mContext); - mPluginInitializer = initializer; + mPrivilegedPlugins.addAll(privilegedPlugins); + mPluginPrefs = pluginPrefs; + mPluginEnabler = pluginEnabler; PluginExceptionHandler uncaughtExceptionHandler = new PluginExceptionHandler( - defaultHandler); + defaultHandlerOptional); Thread.setUncaughtExceptionPreHandler(uncaughtExceptionHandler); - - new Handler(mLooper).post(new Runnable() { - @Override - public void run() { - initializer.onPluginManagerInit(); - } - }); } public boolean isDebuggable() { return mIsDebuggable; } - public String[] getWhitelistedPlugins() { - return mWhitelistedPlugins.toArray(new String[0]); - } - - public PluginEnabler getPluginEnabler() { - return mPluginEnabler; - } - - // TODO(mankoff): This appears to be only called from tests. Remove? - public <T extends Plugin> T getOneShotPlugin(Class<T> cls) { - ProvidesInterface info = cls.getDeclaredAnnotation(ProvidesInterface.class); - if (info == null) { - throw new RuntimeException(cls + " doesn't provide an interface"); - } - if (TextUtils.isEmpty(info.action())) { - throw new RuntimeException(cls + " doesn't provide an action"); - } - return getOneShotPlugin(info.action(), cls); - } - - public <T extends Plugin> T getOneShotPlugin(String action, Class<?> cls) { - if (Looper.myLooper() != Looper.getMainLooper()) { - throw new RuntimeException("Must be called from UI thread"); - } - // Passing null causes compiler to complain about incompatible (generic) types. - PluginListener<Plugin> dummy = null; - PluginInstanceManager<T> p = mFactory.createPluginInstanceManager(mContext, action, dummy, - false, mLooper, cls, this); - mPluginPrefs.addAction(action); - PluginInfo<T> info = p.getPlugin(); - if (info != null) { - mOneShotPackages.add(info.mPackage); - mHasOneShot = true; - startListening(); - return info.mPlugin; - } - return null; + public String[] getPrivilegedPlugins() { + return mPrivilegedPlugins.toArray(new String[0]); } public <T extends Plugin> void addPluginListener(PluginListener<T> listener, Class<?> cls) { @@ -167,10 +100,10 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage } public <T extends Plugin> void addPluginListener(String action, PluginListener<T> listener, - Class cls, boolean allowMultiple) { + Class<?> cls, boolean allowMultiple) { mPluginPrefs.addAction(action); - PluginInstanceManager p = mFactory.createPluginInstanceManager(mContext, action, listener, - allowMultiple, mLooper, cls, this); + PluginInstanceManager<T> p = mInstanceManagerFactory.create(action, listener, allowMultiple, + new VersionInfo().addClass(cls), isDebuggable()); p.loadAll(); synchronized (this) { mPluginMap.put(listener, p); @@ -208,8 +141,7 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage } private void stopListening() { - // Never stop listening if a one-shot is present. - if (!mListening || mHasOneShot) return; + if (!mListening) return; mListening = false; mContext.unregisterReceiver(this); } @@ -218,7 +150,7 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage public void onReceive(Context context, Intent intent) { if (Intent.ACTION_USER_UNLOCKED.equals(intent.getAction())) { synchronized (this) { - for (PluginInstanceManager manager : mPluginMap.values()) { + for (PluginInstanceManager<?> manager : mPluginMap.values()) { manager.loadAll(); } } @@ -226,46 +158,17 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage Uri uri = intent.getData(); ComponentName component = ComponentName.unflattenFromString( uri.toString().substring(10)); - if (isPluginWhitelisted(component)) { - // Don't disable whitelisted plugins as they are a part of the OS. + if (isPluginPrivileged(component)) { + // Don't disable privileged plugins as they are a part of the OS. return; } - getPluginEnabler().setDisabled(component, PluginEnabler.DISABLED_INVALID_VERSION); + mPluginEnabler.setDisabled(component, PluginEnabler.DISABLED_INVALID_VERSION); mContext.getSystemService(NotificationManager.class).cancel(component.getClassName(), SystemMessage.NOTE_PLUGIN); } else { Uri data = intent.getData(); String pkg = data.getEncodedSchemeSpecificPart(); ComponentName componentName = ComponentName.unflattenFromString(pkg); - if (mOneShotPackages.contains(pkg)) { - int icon = Resources.getSystem().getIdentifier( - "stat_sys_warning", "drawable", "android"); - int color = Resources.getSystem().getIdentifier( - "system_notification_accent_color", "color", "android"); - String label = pkg; - try { - PackageManager pm = mContext.getPackageManager(); - label = pm.getApplicationInfo(pkg, 0).loadLabel(pm).toString(); - } catch (NameNotFoundException e) { - } - // Localization not required as this will never ever appear in a user build. - final Notification.Builder nb = - new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID) - .setSmallIcon(icon) - .setWhen(0) - .setShowWhen(false) - .setPriority(Notification.PRIORITY_MAX) - .setVisibility(Notification.VISIBILITY_PUBLIC) - .setColor(mContext.getColor(color)) - .setContentTitle("Plugin \"" + label + "\" has updated") - .setContentText("Restart SysUI for changes to take effect."); - Intent i = new Intent("com.android.systemui.action.RESTART").setData( - Uri.parse("package://" + pkg)); - PendingIntent pi = PendingIntent.getBroadcast(mContext, 0, i, PendingIntent.FLAG_MUTABLE_UNAUDITED); - nb.addAction(new Action.Builder(null, "Restart SysUI", pi).build()); - mContext.getSystemService(NotificationManager.class) - .notify(SystemMessage.NOTE_PLUGIN, nb.build()); - } if (clearClassLoader(pkg)) { if (Build.IS_ENG) { Toast.makeText(mContext, "Reloading " + pkg, Toast.LENGTH_LONG).show(); @@ -276,22 +179,22 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage if (Intent.ACTION_PACKAGE_REPLACED.equals(intent.getAction()) && componentName != null) { @PluginEnabler.DisableReason int disableReason = - getPluginEnabler().getDisableReason(componentName); + mPluginEnabler.getDisableReason(componentName); if (disableReason == PluginEnabler.DISABLED_FROM_EXPLICIT_CRASH || disableReason == PluginEnabler.DISABLED_FROM_SYSTEM_CRASH || disableReason == PluginEnabler.DISABLED_INVALID_VERSION) { Log.i(TAG, "Re-enabling previously disabled plugin that has been " + "updated: " + componentName.flattenToShortString()); - getPluginEnabler().setEnabled(componentName); + mPluginEnabler.setEnabled(componentName); } } synchronized (this) { if (!Intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction())) { - for (PluginInstanceManager manager : mPluginMap.values()) { + for (PluginInstanceManager<?> manager : mPluginMap.values()) { manager.onPackageChange(pkg); } } else { - for (PluginInstanceManager manager : mPluginMap.values()) { + for (PluginInstanceManager<?> manager : mPluginMap.values()) { manager.onPackageRemoved(pkg); } } @@ -299,41 +202,10 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage } } - /** Returns class loader specific for the given plugin. */ - public ClassLoader getClassLoader(ApplicationInfo appInfo) { - if (!mIsDebuggable && !isPluginPackageWhitelisted(appInfo.packageName)) { - Log.w(TAG, "Cannot get class loader for non-whitelisted plugin. Src:" - + appInfo.sourceDir + ", pkg: " + appInfo.packageName); - return null; - } - if (mClassLoaders.containsKey(appInfo.packageName)) { - return mClassLoaders.get(appInfo.packageName); - } - - List<String> zipPaths = new ArrayList<>(); - List<String> libPaths = new ArrayList<>(); - LoadedApk.makePaths(null, true, appInfo, zipPaths, libPaths); - ClassLoader classLoader = new PathClassLoader( - TextUtils.join(File.pathSeparator, zipPaths), - TextUtils.join(File.pathSeparator, libPaths), - getParentClassLoader()); - mClassLoaders.put(appInfo.packageName, classLoader); - return classLoader; - } - private boolean clearClassLoader(String pkg) { return mClassLoaders.remove(pkg) != null; } - ClassLoader getParentClassLoader() { - if (mParentClassLoader == null) { - // Lazily load this so it doesn't have any effect on devices without plugins. - mParentClassLoader = new ClassLoaderFilter(getClass().getClassLoader(), - "com.android.systemui.plugin"); - } - return mParentClassLoader; - } - public <T> boolean dependsOn(Plugin p, Class<T> cls) { synchronized (this) { for (int i = 0; i < mPluginMap.size(); i++) { @@ -345,46 +217,18 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage return false; } - public void handleWtfs() { - mPluginInitializer.handleWtfs(); - } - public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { synchronized (this) { pw.println(String.format(" plugin map (%d):", mPluginMap.size())); - for (PluginListener listener : mPluginMap.keySet()) { + for (PluginListener<?> listener : mPluginMap.keySet()) { pw.println(String.format(" %s -> %s", listener, mPluginMap.get(listener))); } } } - @VisibleForTesting - public static class PluginInstanceManagerFactory { - public <T extends Plugin> PluginInstanceManager createPluginInstanceManager(Context context, - String action, PluginListener<T> listener, boolean allowMultiple, Looper looper, - Class<?> cls, PluginManagerImpl manager) { - return new PluginInstanceManager(context, action, listener, allowMultiple, looper, - new VersionInfo().addClass(cls), manager); - } - } - - private boolean isPluginPackageWhitelisted(String packageName) { - for (String componentNameOrPackage : mWhitelistedPlugins) { - ComponentName componentName = ComponentName.unflattenFromString(componentNameOrPackage); - if (componentName != null) { - if (componentName.getPackageName().equals(packageName)) { - return true; - } - } else if (componentNameOrPackage.equals(packageName)) { - return true; - } - } - return false; - } - - private boolean isPluginWhitelisted(ComponentName pluginName) { - for (String componentNameOrPackage : mWhitelistedPlugins) { + private boolean isPluginPrivileged(ComponentName pluginName) { + for (String componentNameOrPackage : mPrivilegedPlugins) { ComponentName componentName = ComponentName.unflattenFromString(componentNameOrPackage); if (componentName != null) { if (componentName.equals(pluginName)) { @@ -399,7 +243,7 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage // This allows plugins to include any libraries or copied code they want by only including // classes from the plugin library. - private static class ClassLoaderFilter extends ClassLoader { + static class ClassLoaderFilter extends ClassLoader { private final String mPackage; private final ClassLoader mBase; @@ -417,16 +261,20 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage } private class PluginExceptionHandler implements UncaughtExceptionHandler { - private final UncaughtExceptionHandler mHandler; + private final Optional<UncaughtExceptionHandler> mExceptionHandlerOptional; - private PluginExceptionHandler(UncaughtExceptionHandler handler) { - mHandler = handler; + private PluginExceptionHandler( + Optional<UncaughtExceptionHandler> exceptionHandlerOptional) { + mExceptionHandlerOptional = exceptionHandlerOptional; } @Override public void uncaughtException(Thread thread, Throwable throwable) { if (SystemProperties.getBoolean("plugin.debugging", false)) { - mHandler.uncaughtException(thread, throwable); + Throwable finalThrowable = throwable; + mExceptionHandlerOptional.ifPresent( + handler -> handler.uncaughtException(thread, finalThrowable)); + return; } // Search for and disable plugins that may have been involved in this crash. @@ -436,7 +284,7 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage // disable all the plugins, so we can be sure that SysUI is running as // best as possible. synchronized (this) { - for (PluginInstanceManager manager : mPluginMap.values()) { + for (PluginInstanceManager<?> manager : mPluginMap.values()) { disabledAny |= manager.disableAll(); } } @@ -446,7 +294,9 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage } // Run the normal exception handler so we can crash and cleanup our state. - mHandler.uncaughtException(thread, throwable); + Throwable finalThrowable = throwable; + mExceptionHandlerOptional.ifPresent( + handler -> handler.uncaughtException(thread, finalThrowable)); } private boolean checkStack(Throwable throwable) { @@ -454,7 +304,7 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage boolean disabledAny = false; synchronized (this) { for (StackTraceElement element : throwable.getStackTrace()) { - for (PluginInstanceManager manager : mPluginMap.values()) { + for (PluginInstanceManager<?> manager : mPluginMap.values()) { disabledAny |= manager.checkAndDisable(element.getClassName()); } } diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java index 4663a9afcd3d..b827356e952c 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java @@ -116,6 +116,8 @@ public class QuickStepContract { public static final int SYSUI_STATE_IME_SWITCHER_SHOWING = 1 << 20; // Device dozing/AOD state public static final int SYSUI_STATE_DEVICE_DOZING = 1 << 21; + // The home feature is disabled (either by SUW/SysUI/device policy) + public static final int SYSUI_STATE_BACK_DISABLED = 1 << 22; @Retention(RetentionPolicy.SOURCE) @IntDef({SYSUI_STATE_SCREEN_PINNING, @@ -139,7 +141,8 @@ public class QuickStepContract { SYSUI_STATE_IME_SHOWING, SYSUI_STATE_MAGNIFICATION_OVERLAP, SYSUI_STATE_IME_SWITCHER_SHOWING, - SYSUI_STATE_DEVICE_DOZING + SYSUI_STATE_DEVICE_DOZING, + SYSUI_STATE_BACK_DISABLED }) public @interface SystemUiStateFlags {} @@ -170,6 +173,7 @@ public class QuickStepContract { str.add((flags & SYSUI_STATE_MAGNIFICATION_OVERLAP) != 0 ? "magnification_overlap" : ""); str.add((flags & SYSUI_STATE_IME_SWITCHER_SHOWING) != 0 ? "ime_switcher_showing" : ""); str.add((flags & SYSUI_STATE_DEVICE_DOZING) != 0 ? "device_dozing" : ""); + str.add((flags & SYSUI_STATE_BACK_DISABLED) != 0 ? "back_disabled" : ""); return str.toString(); } diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java index 68905f795563..0a1465708edb 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java @@ -74,6 +74,10 @@ public class RemoteAnimationTargetCompat { private final SurfaceControl mStartLeash; + // Fields used only to unrap into RemoteAnimationTarget + private final WindowConfiguration windowConfiguration; + private final Rect startBounds; + public RemoteAnimationTargetCompat(RemoteAnimationTarget app) { taskId = app.taskId; mode = app.mode; @@ -94,6 +98,8 @@ public class RemoteAnimationTargetCompat { mStartLeash = app.startLeash; windowType = app.windowType; + windowConfiguration = app.windowConfiguration; + startBounds = app.startBounds; } private static int newModeToLegacyMode(int newMode) { @@ -109,6 +115,14 @@ public class RemoteAnimationTargetCompat { } } + public RemoteAnimationTarget unwrap() { + return new RemoteAnimationTarget( + taskId, mode, leash.getSurfaceControl(), isTranslucent, clipRect, contentInsets, + prefixOrderIndex, position, localBounds, screenSpaceBounds, windowConfiguration, + isNotInRecents, mStartLeash, startBounds, taskInfo, allowEnterPip, windowType + ); + } + /** * Almost a copy of Transitions#setupStartState. @@ -220,6 +234,10 @@ public class RemoteAnimationTargetCompat { mStartLeash = null; rotationChange = change.getEndRotation() - change.getStartRotation(); windowType = INVALID_WINDOW_TYPE; + + // TODO this probably isn't right but it's unused for now /shrug + windowConfiguration = new WindowConfiguration(); + startBounds = change.getStartAbsBounds(); } public static RemoteAnimationTargetCompat[] wrap(RemoteAnimationTarget[] apps) { diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java index 2519284c5f43..aac52357d008 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java @@ -19,6 +19,7 @@ package com.android.systemui.shared.system; import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.view.WindowManager.TRANSIT_CLOSE; +import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY; import static android.view.WindowManager.TRANSIT_OPEN; import static android.view.WindowManager.TRANSIT_TO_BACK; import static android.view.WindowManager.TRANSIT_TO_FRONT; @@ -172,6 +173,8 @@ public class RemoteTransitionCompat implements Parcelable { if (mFilter == null) { mFilter = new TransitionFilter(); } + // No need to handle the transition that also dismisses keyguard. + mFilter.mNotFlags = TRANSIT_FLAG_KEYGUARD_GOING_AWAY; mFilter.mRequirements = new TransitionFilter.Requirement[]{new TransitionFilter.Requirement(), new TransitionFilter.Requirement()}; diff --git a/packages/SystemUI/shared/src/com/android/unfold/UnfoldTransitionFactory.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt index 7594f5071899..9b5eae8f6c26 100644 --- a/packages/SystemUI/shared/src/com/android/unfold/UnfoldTransitionFactory.kt +++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt @@ -15,21 +15,21 @@ */ @file:JvmName("UnfoldTransitionFactory") -package com.android.unfold +package com.android.systemui.unfold import android.content.Context import android.hardware.SensorManager import android.hardware.devicestate.DeviceStateManager import android.os.Handler -import com.android.unfold.updates.screen.ScreenStatusProvider -import com.android.unfold.config.ANIMATION_MODE_HINGE_ANGLE -import com.android.unfold.config.ResourceUnfoldTransitionConfig -import com.android.unfold.config.UnfoldTransitionConfig -import com.android.unfold.progress.FixedTimingTransitionProgressProvider -import com.android.unfold.progress.PhysicsBasedUnfoldTransitionProgressProvider -import com.android.unfold.updates.DeviceFoldStateProvider -import com.android.unfold.updates.hinge.EmptyHingeAngleProvider -import com.android.unfold.updates.hinge.RotationSensorHingeAngleProvider +import com.android.systemui.unfold.updates.screen.ScreenStatusProvider +import com.android.systemui.unfold.config.ANIMATION_MODE_HINGE_ANGLE +import com.android.systemui.unfold.config.ResourceUnfoldTransitionConfig +import com.android.systemui.unfold.config.UnfoldTransitionConfig +import com.android.systemui.unfold.progress.FixedTimingTransitionProgressProvider +import com.android.systemui.unfold.progress.PhysicsBasedUnfoldTransitionProgressProvider +import com.android.systemui.unfold.updates.DeviceFoldStateProvider +import com.android.systemui.unfold.updates.hinge.EmptyHingeAngleProvider +import com.android.systemui.unfold.updates.hinge.RotationSensorHingeAngleProvider import java.lang.IllegalStateException import java.util.concurrent.Executor diff --git a/packages/SystemUI/shared/src/com/android/unfold/UnfoldTransitionProgressProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionProgressProvider.kt index 4a6a9ac266f2..e17f43e64b21 100644 --- a/packages/SystemUI/shared/src/com/android/unfold/UnfoldTransitionProgressProvider.kt +++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionProgressProvider.kt @@ -13,10 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.unfold +package com.android.systemui.unfold import android.annotation.FloatRange -import com.android.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener +import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener import com.android.systemui.statusbar.policy.CallbackController /** diff --git a/packages/SystemUI/shared/src/com/android/unfold/config/ResourceUnfoldTransitionConfig.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/config/ResourceUnfoldTransitionConfig.kt index bde87a57b3aa..fa6b5de0b1ee 100644 --- a/packages/SystemUI/shared/src/com/android/unfold/config/ResourceUnfoldTransitionConfig.kt +++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/config/ResourceUnfoldTransitionConfig.kt @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.unfold.config +package com.android.systemui.unfold.config import android.content.Context import android.os.SystemProperties diff --git a/packages/SystemUI/shared/src/com/android/unfold/config/UnfoldTransitionConfig.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/config/UnfoldTransitionConfig.kt index f000c69ac809..75d9dc32339f 100644 --- a/packages/SystemUI/shared/src/com/android/unfold/config/UnfoldTransitionConfig.kt +++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/config/UnfoldTransitionConfig.kt @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.unfold.config +package com.android.systemui.unfold.config import android.annotation.IntDef diff --git a/packages/SystemUI/shared/src/com/android/unfold/progress/FixedTimingTransitionProgressProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/progress/FixedTimingTransitionProgressProvider.kt index acfe073211d7..732882e99038 100644 --- a/packages/SystemUI/shared/src/com/android/unfold/progress/FixedTimingTransitionProgressProvider.kt +++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/progress/FixedTimingTransitionProgressProvider.kt @@ -13,17 +13,17 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.unfold.progress +package com.android.systemui.unfold.progress import android.animation.Animator import android.animation.ObjectAnimator import android.util.FloatProperty -import com.android.unfold.UnfoldTransitionProgressProvider -import com.android.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener -import com.android.unfold.updates.FOLD_UPDATE_FINISH_CLOSED -import com.android.unfold.updates.FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE -import com.android.unfold.updates.FoldStateProvider -import com.android.unfold.updates.FoldStateProvider.FoldUpdate +import com.android.systemui.unfold.UnfoldTransitionProgressProvider +import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener +import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_CLOSED +import com.android.systemui.unfold.updates.FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE +import com.android.systemui.unfold.updates.FoldStateProvider +import com.android.systemui.unfold.updates.FoldStateProvider.FoldUpdate /** * Emits animation progress with fixed timing after unfolding diff --git a/packages/SystemUI/shared/src/com/android/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt index d9d037fd99c1..b111892ceb6e 100644 --- a/packages/SystemUI/shared/src/com/android/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt +++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt @@ -13,21 +13,21 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.unfold.progress +package com.android.systemui.unfold.progress import android.os.Handler import androidx.dynamicanimation.animation.DynamicAnimation import androidx.dynamicanimation.animation.FloatPropertyCompat import androidx.dynamicanimation.animation.SpringAnimation import androidx.dynamicanimation.animation.SpringForce -import com.android.unfold.UnfoldTransitionProgressProvider -import com.android.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener -import com.android.unfold.updates.FOLD_UPDATE_FINISH_CLOSED -import com.android.unfold.updates.FOLD_UPDATE_FINISH_FULL_OPEN -import com.android.unfold.updates.FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE -import com.android.unfold.updates.FoldStateProvider -import com.android.unfold.updates.FoldStateProvider.FoldUpdate -import com.android.unfold.updates.FoldStateProvider.FoldUpdatesListener +import com.android.systemui.unfold.UnfoldTransitionProgressProvider +import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener +import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_CLOSED +import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_FULL_OPEN +import com.android.systemui.unfold.updates.FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE +import com.android.systemui.unfold.updates.FoldStateProvider +import com.android.systemui.unfold.updates.FoldStateProvider.FoldUpdate +import com.android.systemui.unfold.updates.FoldStateProvider.FoldUpdatesListener /** * Maps fold updates to unfold transition progress using DynamicAnimation. diff --git a/packages/SystemUI/shared/src/com/android/unfold/updates/DeviceFoldStateProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt index 3a21b809ec30..949652bc9137 100644 --- a/packages/SystemUI/shared/src/com/android/unfold/updates/DeviceFoldStateProvider.kt +++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt @@ -13,16 +13,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.unfold.updates +package com.android.systemui.unfold.updates import android.content.Context import android.hardware.devicestate.DeviceStateManager import androidx.core.util.Consumer -import com.android.unfold.updates.screen.ScreenStatusProvider -import com.android.unfold.updates.FoldStateProvider.FoldUpdate -import com.android.unfold.updates.FoldStateProvider.FoldUpdatesListener -import com.android.unfold.updates.hinge.FULLY_OPEN_DEGREES -import com.android.unfold.updates.hinge.HingeAngleProvider +import com.android.systemui.unfold.updates.screen.ScreenStatusProvider +import com.android.systemui.unfold.updates.FoldStateProvider.FoldUpdate +import com.android.systemui.unfold.updates.FoldStateProvider.FoldUpdatesListener +import com.android.systemui.unfold.updates.hinge.FULLY_OPEN_DEGREES +import com.android.systemui.unfold.updates.hinge.HingeAngleProvider import java.util.concurrent.Executor internal class DeviceFoldStateProvider( diff --git a/packages/SystemUI/shared/src/com/android/unfold/updates/FoldStateProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/FoldStateProvider.kt index 2c3a6ec2a0ed..4c6d241b1456 100644 --- a/packages/SystemUI/shared/src/com/android/unfold/updates/FoldStateProvider.kt +++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/FoldStateProvider.kt @@ -13,11 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.unfold.updates +package com.android.systemui.unfold.updates import android.annotation.FloatRange import android.annotation.IntDef -import com.android.unfold.updates.FoldStateProvider.FoldUpdatesListener +import com.android.systemui.unfold.updates.FoldStateProvider.FoldUpdatesListener import com.android.systemui.statusbar.policy.CallbackController /** diff --git a/packages/SystemUI/shared/src/com/android/unfold/updates/hinge/EmptyHingeAngleProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/EmptyHingeAngleProvider.kt index 905b08625bee..9b58b1fcad46 100644 --- a/packages/SystemUI/shared/src/com/android/unfold/updates/hinge/EmptyHingeAngleProvider.kt +++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/EmptyHingeAngleProvider.kt @@ -1,4 +1,4 @@ -package com.android.unfold.updates.hinge +package com.android.systemui.unfold.updates.hinge import androidx.core.util.Consumer diff --git a/packages/SystemUI/shared/src/com/android/unfold/updates/hinge/HingeAngleProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/HingeAngleProvider.kt index 4196f60e2ca8..854991383a8c 100644 --- a/packages/SystemUI/shared/src/com/android/unfold/updates/hinge/HingeAngleProvider.kt +++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/HingeAngleProvider.kt @@ -1,4 +1,4 @@ -package com.android.unfold.updates.hinge +package com.android.systemui.unfold.updates.hinge import androidx.core.util.Consumer import com.android.systemui.statusbar.policy.CallbackController diff --git a/packages/SystemUI/shared/src/com/android/unfold/updates/hinge/RotationSensorHingeAngleProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/RotationSensorHingeAngleProvider.kt index 011582efc67c..8b6eecfdfde4 100644 --- a/packages/SystemUI/shared/src/com/android/unfold/updates/hinge/RotationSensorHingeAngleProvider.kt +++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/RotationSensorHingeAngleProvider.kt @@ -1,4 +1,4 @@ -package com.android.unfold.updates.hinge +package com.android.systemui.unfold.updates.hinge import android.hardware.Sensor import android.hardware.SensorEvent diff --git a/packages/SystemUI/shared/src/com/android/unfold/updates/screen/ScreenStatusProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/screen/ScreenStatusProvider.kt index a65e888b3e2e..1eec8033ac5d 100644 --- a/packages/SystemUI/shared/src/com/android/unfold/updates/screen/ScreenStatusProvider.kt +++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/screen/ScreenStatusProvider.kt @@ -13,9 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.unfold.updates.screen +package com.android.systemui.unfold.updates.screen -import com.android.unfold.updates.screen.ScreenStatusProvider.ScreenListener +import com.android.systemui.unfold.updates.screen.ScreenStatusProvider.ScreenListener import com.android.systemui.statusbar.policy.CallbackController interface ScreenStatusProvider : CallbackController<ScreenListener> { diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java index 27b6182d1aa2..0b78ddb2229c 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java @@ -98,6 +98,8 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS private final KeyguardUnlockAnimationController mKeyguardUnlockAnimationController; private SmartspaceTransitionController mSmartspaceTransitionController; + private boolean mOnlyClock = false; + @Inject public KeyguardClockSwitchController( KeyguardClockSwitch keyguardClockSwitch, @@ -130,6 +132,13 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS } /** + * Mostly used for alternate displays, limit the information shown + */ + public void setOnlyClock(boolean onlyClock) { + mOnlyClock = onlyClock; + } + + /** * Attach the controller to the view it relates to. */ @Override @@ -169,6 +178,16 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS } mColorExtractor.addOnColorsChangedListener(mColorsListener); mView.updateColors(getGradientColors()); + + if (mOnlyClock) { + View ksa = mView.findViewById(R.id.keyguard_status_area); + ksa.setVisibility(View.GONE); + + View nic = mView.findViewById( + R.id.left_aligned_notification_icon_container); + nic.setVisibility(View.GONE); + return; + } updateAodIcons(); if (mSmartspaceController.isSmartspaceEnabled()) { diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java index 76a7473e25e8..cac90ea60e97 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java @@ -332,6 +332,7 @@ public class KeyguardDisplayManager { .build(findViewById(R.id.clock)) .getKeyguardClockSwitchController(); + mKeyguardClockSwitchController.setOnlyClock(true); mKeyguardClockSwitchController.init(); } diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconView.java b/packages/SystemUI/src/com/android/keyguard/LockIconView.java index 622419a86bfc..dd3bb8990599 100644 --- a/packages/SystemUI/src/com/android/keyguard/LockIconView.java +++ b/packages/SystemUI/src/com/android/keyguard/LockIconView.java @@ -22,12 +22,12 @@ import android.graphics.PointF; import android.graphics.RectF; import android.graphics.drawable.Drawable; import android.util.AttributeSet; -import android.view.Gravity; import android.view.View; import android.widget.FrameLayout; import android.widget.ImageView; import androidx.annotation.NonNull; +import androidx.annotation.VisibleForTesting; import com.android.settingslib.Utils; import com.android.systemui.Dumpable; @@ -80,7 +80,11 @@ public class LockIconView extends FrameLayout implements Dumpable { mLockIcon.setImageDrawable(drawable); } - void setCenterLocation(@NonNull PointF center, int radius) { + /** + * Set the location of the lock icon. + */ + @VisibleForTesting + public void setCenterLocation(@NonNull PointF center, int radius) { mLockIconCenter = center; mRadius = radius; @@ -91,13 +95,11 @@ public class LockIconView extends FrameLayout implements Dumpable { mLockIconCenter.x + mRadius, mLockIconCenter.y + mRadius); - setX(mSensorRect.left); - setY(mSensorRect.top); - - final FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams( - (int) (mSensorRect.right - mSensorRect.left), - (int) (mSensorRect.bottom - mSensorRect.top)); - lp.gravity = Gravity.CENTER; + final FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams(); + lp.width = (int) (mSensorRect.right - mSensorRect.left); + lp.height = (int) (mSensorRect.bottom - mSensorRect.top); + lp.topMargin = (int) mSensorRect.top; + lp.setMarginStart((int) mSensorRect.left); setLayoutParams(lp); } @@ -114,5 +116,6 @@ public class LockIconView extends FrameLayout implements Dumpable { public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) { pw.println("Center in px (x, y)= (" + mLockIconCenter.x + ", " + mLockIconCenter.y + ")"); pw.println("Radius in pixels: " + mRadius); + pw.println("topLeft= (" + getX() + ", " + getY() + ")"); } } diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java index aa8cbd710e90..47f0714af164 100644 --- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java @@ -68,7 +68,8 @@ import javax.inject.Inject; /** * Controls when to show the LockIcon affordance (lock/unlocked icon or circle) on lock screen. * - * This view will only be shown if the user has UDFPS or FaceAuth enrolled + * For devices with UDFPS, the lock icon will show at the sensor location. Else, the lock + * icon will show a set distance from the bottom of the device. */ @StatusBarComponent.StatusBarScope public class LockIconViewController extends ViewController<LockIconView> implements Dumpable { @@ -172,15 +173,14 @@ public class LockIconViewController extends ViewController<LockIconView> impleme @Override protected void onInit() { + mAuthController.addCallback(mAuthControllerCallback); + mUdfpsSupported = mAuthController.getUdfpsSensorLocation() != null; + mView.setAccessibilityDelegate(mAccessibilityDelegate); } @Override protected void onViewAttached() { - // we check this here instead of onInit since the FingerprintManager + FaceManager may not - // have started up yet onInit - mUdfpsSupported = mAuthController.getUdfpsSensorLocation() != null; - updateConfiguration(); updateKeyguardShowing(); mUserUnlockedWithBiometric = false; @@ -584,4 +584,12 @@ public class LockIconViewController extends ViewController<LockIconView> impleme public void setAlpha(float alpha) { mView.setAlpha(alpha); } + + private final AuthController.Callback mAuthControllerCallback = new AuthController.Callback() { + @Override + public void onAllAuthenticatorsRegistered() { + mUdfpsSupported = mAuthController.getUdfpsSensorLocation() != null; + updateConfiguration(); + } + }; } diff --git a/packages/SystemUI/src/com/android/keyguard/NumPadButton.java b/packages/SystemUI/src/com/android/keyguard/NumPadButton.java index 99f9558fe5d0..c659bf786a45 100644 --- a/packages/SystemUI/src/com/android/keyguard/NumPadButton.java +++ b/packages/SystemUI/src/com/android/keyguard/NumPadButton.java @@ -26,7 +26,7 @@ import android.view.MotionEvent; import androidx.annotation.Nullable; -import com.android.settingslib.Utils; +import com.android.systemui.R; /** * Similar to the {@link NumPadKey}, but displays an image. @@ -92,8 +92,7 @@ public class NumPadButton extends AlphaOptimizedImageButton { public void reloadColors() { if (mAnimator != null) mAnimator.reloadColors(getContext()); - int textColor = Utils.getColorAttrDefaultColor(getContext(), - android.R.attr.colorBackground); - ((VectorDrawable) getDrawable()).setTintList(ColorStateList.valueOf(textColor)); + int imageColor = getContext().getColor(R.color.keyguard_keypad_image_color); + ((VectorDrawable) getDrawable()).setTintList(ColorStateList.valueOf(imageColor)); } } diff --git a/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java b/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java index 06fbe842eb85..cc166c210078 100644 --- a/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java +++ b/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java @@ -92,9 +92,11 @@ public class ActivityStarterDelegate implements ActivityStarter { @Override public void startActivity(Intent intent, boolean dismissShade, - @Nullable ActivityLaunchAnimator.Controller animationController) { + @Nullable ActivityLaunchAnimator.Controller animationController, + boolean showOverLockscreenWhenLocked) { mActualStarterOptionalLazy.get().ifPresent( - starter -> starter.startActivity(intent, dismissShade, animationController)); + starter -> starter.startActivity(intent, dismissShade, animationController, + showOverLockscreenWhenLocked)); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java index e51de66dc2b8..e996e71b6715 100644 --- a/packages/SystemUI/src/com/android/systemui/Dependency.java +++ b/packages/SystemUI/src/com/android/systemui/Dependency.java @@ -114,7 +114,6 @@ import com.android.systemui.statusbar.policy.FlashlightController; import com.android.systemui.statusbar.policy.HotspotController; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.LocationController; -import com.android.systemui.statusbar.policy.NetworkController; import com.android.systemui.statusbar.policy.NextAlarmController; import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler; import com.android.systemui.statusbar.policy.RotationLockController; @@ -244,7 +243,6 @@ public class Dependency { @Inject Lazy<BluetoothController> mBluetoothController; @Inject Lazy<LocationController> mLocationController; @Inject Lazy<RotationLockController> mRotationLockController; - @Inject Lazy<NetworkController> mNetworkController; @Inject Lazy<ZenModeController> mZenModeController; @Inject Lazy<HotspotController> mHotspotController; @Inject Lazy<CastController> mCastController; @@ -389,8 +387,6 @@ public class Dependency { mProviders.put(RotationLockController.class, mRotationLockController::get); - mProviders.put(NetworkController.class, mNetworkController::get); - mProviders.put(ZenModeController.class, mZenModeController::get); mProviders.put(HotspotController.class, mHotspotController::get); diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java index b126cddf8eec..3555e8d8e193 100644 --- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java +++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java @@ -452,6 +452,12 @@ public class SwipeHelper implements Gefingerpoken { private boolean mCancelled; @Override + public void onAnimationStart(Animator animation) { + super.onAnimationStart(animation); + mCallback.onBeginDrag(animView); + } + + @Override public void onAnimationCancel(Animator animation) { mCancelled = true; } diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistOrbController.java b/packages/SystemUI/src/com/android/systemui/assist/AssistOrbController.java index 81a13a236685..408201558a9b 100644 --- a/packages/SystemUI/src/com/android/systemui/assist/AssistOrbController.java +++ b/packages/SystemUI/src/com/android/systemui/assist/AssistOrbController.java @@ -57,7 +57,9 @@ public class AssistOrbController { public void run() { mView.removeCallbacks(this); mView.show(false /* show */, true /* animate */, () -> { - mWindowManager.removeView(mView); + if (mView.isAttachedToWindow()) { + mWindowManager.removeView(mView); + } }); } }; diff --git a/packages/SystemUI/src/com/android/systemui/backup/BackupHelper.kt b/packages/SystemUI/src/com/android/systemui/backup/BackupHelper.kt index c9e67715decb..5616a00592f2 100644 --- a/packages/SystemUI/src/com/android/systemui/backup/BackupHelper.kt +++ b/packages/SystemUI/src/com/android/systemui/backup/BackupHelper.kt @@ -40,10 +40,10 @@ import com.android.systemui.people.widget.PeopleBackupHelper * After restoring is done, a [ACTION_RESTORE_FINISHED] intent will be send to SystemUI user 0, * indicating that restoring is finished for a given user. */ -class BackupHelper : BackupAgentHelper() { +open class BackupHelper : BackupAgentHelper() { companion object { - private const val TAG = "BackupHelper" + const val TAG = "BackupHelper" internal const val CONTROLS = ControlsFavoritePersistenceWrapper.FILE_NAME private const val NO_OVERWRITE_FILES_BACKUP_KEY = "systemui.files_no_overwrite" private const val PEOPLE_TILES_BACKUP_KEY = "systemui.people.shared_preferences" diff --git a/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterView.java index b9440c5e3975..66085ac0a915 100644 --- a/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterView.java +++ b/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterView.java @@ -41,24 +41,22 @@ import android.widget.LinearLayout; import android.widget.TextView; import androidx.annotation.StyleRes; +import androidx.annotation.VisibleForTesting; import com.android.settingslib.graph.ThemedBatteryDrawable; -import com.android.systemui.Dependency; import com.android.systemui.DualToneHandler; import com.android.systemui.R; import com.android.systemui.animation.Interpolators; import com.android.systemui.plugins.DarkIconDispatcher; import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver; import com.android.systemui.statusbar.policy.BatteryController; -import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback; import java.io.FileDescriptor; import java.io.PrintWriter; import java.lang.annotation.Retention; import java.text.NumberFormat; -public class BatteryMeterView extends LinearLayout implements - BatteryStateChangeCallback, DarkReceiver { +public class BatteryMeterView extends LinearLayout implements DarkReceiver { @Retention(SOURCE) @IntDef({MODE_DEFAULT, MODE_ON, MODE_OFF, MODE_ESTIMATE}) @@ -72,7 +70,6 @@ public class BatteryMeterView extends LinearLayout implements private final ImageView mBatteryIconView; private TextView mBatteryPercentView; - private BatteryController mBatteryController; private final @StyleRes int mPercentageStyleId; private int mTextColor; private int mLevel; @@ -90,6 +87,8 @@ public class BatteryMeterView extends LinearLayout implements private int mNonAdaptedForegroundColor; private int mNonAdaptedBackgroundColor; + private BatteryEstimateFetcher mBatteryEstimateFetcher; + public BatteryMeterView(Context context, AttributeSet attrs) { this(context, attrs, 0); } @@ -178,22 +177,7 @@ public class BatteryMeterView extends LinearLayout implements return false; } - @Override - public void onAttachedToWindow() { - super.onAttachedToWindow(); - mBatteryController = Dependency.get(BatteryController.class); - mBatteryController.addCallback(this); - updateShowPercent(); - } - - @Override - public void onDetachedFromWindow() { - super.onDetachedFromWindow(); - mBatteryController.removeCallback(this); - } - - @Override - public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) { + void onBatteryLevelChanged(int level, boolean pluggedIn) { mDrawable.setCharging(pluggedIn); mDrawable.setBatteryLevel(level); mCharging = pluggedIn; @@ -201,8 +185,7 @@ public class BatteryMeterView extends LinearLayout implements updatePercentText(); } - @Override - public void onPowerSaveChanged(boolean isPowerSave) { + void onPowerSaveChanged(boolean isPowerSave) { mDrawable.setPowerSaveEnabled(isPowerSave); } @@ -222,19 +205,28 @@ public class BatteryMeterView extends LinearLayout implements updateShowPercent(); } + /** + * Sets the fetcher that should be used to get the estimated time remaining for the user's + * battery. + */ + void setBatteryEstimateFetcher(BatteryEstimateFetcher fetcher) { + mBatteryEstimateFetcher = fetcher; + } + void updatePercentText() { if (mBatteryStateUnknown) { setContentDescription(getContext().getString(R.string.accessibility_battery_unknown)); return; } - if (mBatteryController == null) { + if (mBatteryEstimateFetcher == null) { return; } if (mBatteryPercentView != null) { if (mShowPercentMode == MODE_ESTIMATE && !mCharging) { - mBatteryController.getEstimatedTimeRemainingString((String estimate) -> { + mBatteryEstimateFetcher.fetchBatteryTimeRemainingEstimate( + (String estimate) -> { if (mBatteryPercentView == null) { return; } @@ -310,8 +302,7 @@ public class BatteryMeterView extends LinearLayout implements return mUnknownStateDrawable; } - @Override - public void onBatteryUnknownStateChanged(boolean isUnknown) { + void onBatteryUnknownStateChanged(boolean isUnknown) { if (mBatteryStateUnknown == isUnknown) { return; } @@ -390,5 +381,16 @@ public class BatteryMeterView extends LinearLayout implements pw.println(" mLevel: " + mLevel); pw.println(" mMode: " + mShowPercentMode); } + + @VisibleForTesting + CharSequence getBatteryPercentViewText() { + return mBatteryPercentView.getText(); + } + + /** An interface that will fetch the estimated time remaining for the user's battery. */ + public interface BatteryEstimateFetcher { + void fetchBatteryTimeRemainingEstimate( + BatteryController.EstimateFetchCompletion completion); + } } diff --git a/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterViewController.java b/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterViewController.java index 4664423c4422..ae9a32309d45 100644 --- a/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterViewController.java +++ b/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterViewController.java @@ -31,6 +31,7 @@ import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.settings.CurrentUserTracker; import com.android.systemui.statusbar.phone.StatusBarIconController; +import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.tuner.TunerService; import com.android.systemui.util.ViewController; @@ -42,6 +43,7 @@ public class BatteryMeterViewController extends ViewController<BatteryMeterView> private final ConfigurationController mConfigurationController; private final TunerService mTunerService; private final ContentResolver mContentResolver; + private final BatteryController mBatteryController; private final String mSlotBattery; private final SettingObserver mSettingObserver; @@ -66,6 +68,24 @@ public class BatteryMeterViewController extends ViewController<BatteryMeterView> } }; + private final BatteryController.BatteryStateChangeCallback mBatteryStateChangeCallback = + new BatteryController.BatteryStateChangeCallback() { + @Override + public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) { + mView.onBatteryLevelChanged(level, pluggedIn); + } + + @Override + public void onPowerSaveChanged(boolean isPowerSave) { + mView.onPowerSaveChanged(isPowerSave); + } + + @Override + public void onBatteryUnknownStateChanged(boolean isUnknown) { + mView.onBatteryUnknownStateChanged(isUnknown); + } + }; + // Some places may need to show the battery conditionally, and not obey the tuner private boolean mIgnoreTunerUpdates; private boolean mIsSubscribedForTunerUpdates; @@ -77,11 +97,15 @@ public class BatteryMeterViewController extends ViewController<BatteryMeterView> TunerService tunerService, BroadcastDispatcher broadcastDispatcher, @Main Handler mainHandler, - ContentResolver contentResolver) { + ContentResolver contentResolver, + BatteryController batteryController) { super(view); mConfigurationController = configurationController; mTunerService = tunerService; mContentResolver = contentResolver; + mBatteryController = batteryController; + + mView.setBatteryEstimateFetcher(mBatteryController::getEstimatedTimeRemainingString); mSlotBattery = getResources().getString(com.android.internal.R.string.status_bar_battery); mSettingObserver = new SettingObserver(mainHandler); @@ -99,22 +123,21 @@ public class BatteryMeterViewController extends ViewController<BatteryMeterView> protected void onViewAttached() { mConfigurationController.addCallback(mConfigurationListener); subscribeForTunerUpdates(); + mBatteryController.addCallback(mBatteryStateChangeCallback); registerShowBatteryPercentObserver(ActivityManager.getCurrentUser()); registerGlobalBatteryUpdateObserver(); mCurrentUserTracker.startTracking(); - } - @Override - protected void onViewDetached() { - destroy(); + mView.updateShowPercent(); } @Override - public void destroy() { - super.destroy(); + protected void onViewDetached() { mConfigurationController.removeCallback(mConfigurationListener); unsubscribeFromTunerUpdates(); + mBatteryController.removeCallback(mBatteryStateChangeCallback); + mCurrentUserTracker.stopTracking(); mContentResolver.unregisterContentObserver(mSettingObserver); } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java index 0ce1846e7745..ab5f2b8289be 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java @@ -786,7 +786,11 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, .build(sensorIds, credentialAllowed, mFpProps, mFaceProps); } - interface Callback { + /** + * AuthController callback used to receive signal for when biometric authenticators are + * registered. + */ + public interface Callback { /** * Called when authenticators are registered. If authenticators are already * registered before this call, this callback will never be triggered. diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt index 66b45e5900bf..45ca7088cf4c 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt @@ -24,6 +24,7 @@ import androidx.annotation.VisibleForTesting import com.android.keyguard.KeyguardUpdateMonitor import com.android.keyguard.KeyguardUpdateMonitorCallback import com.android.settingslib.Utils +import com.android.systemui.R import com.android.systemui.statusbar.CircleReveal import com.android.systemui.statusbar.LightRevealEffect import com.android.systemui.statusbar.NotificationShadeWindowController @@ -59,6 +60,11 @@ class AuthRippleController @Inject constructor( private var faceSensorLocation: PointF? = null private var circleReveal: LightRevealEffect? = null + override fun onInit() { + mView.setAlphaInDuration(sysuiContext.resources.getInteger( + R.integer.auth_ripple_alpha_in_duration).toLong()) + } + @VisibleForTesting public override fun onViewAttached() { updateRippleColor() diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt index 95ea81003ecb..95d0afa10e8f 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt @@ -38,6 +38,7 @@ private const val RIPPLE_SPARKLE_STRENGTH: Float = 0.4f * launcher. */ class AuthRippleView(context: Context?, attrs: AttributeSet?) : View(context, attrs) { + private var alphaInDuration: Long = 0 private var rippleInProgress: Boolean = false private val rippleShader = RippleShader() private val ripplePaint = Paint() @@ -66,47 +67,37 @@ class AuthRippleView(context: Context?, attrs: AttributeSet?) : View(context, at .toFloat() } + fun setAlphaInDuration(duration: Long) { + alphaInDuration = duration + } + fun startRipple(onAnimationEnd: Runnable?, lightReveal: LightRevealScrim?) { if (rippleInProgress) { return // Ignore if ripple effect is already playing } val rippleAnimator = ValueAnimator.ofFloat(0f, 1f).apply { - interpolator = PathInterpolator(0.4f, 0f, 0f, 1f) + interpolator = PathInterpolator(0f, 0f, .2f, 1f) duration = RIPPLE_ANIMATION_DURATION addUpdateListener { animator -> val now = animator.currentPlayTime rippleShader.progress = animator.animatedValue as Float rippleShader.time = now.toFloat() - lightReveal?.revealAmount = animator.animatedValue as Float invalidate() } } - val revealAnimator = ValueAnimator.ofFloat(0f, 1f).apply { + val revealAnimator = ValueAnimator.ofFloat(.1f, 1f).apply { interpolator = rippleAnimator.interpolator - startDelay = 10 duration = rippleAnimator.duration addUpdateListener { animator -> lightReveal?.revealAmount = animator.animatedValue as Float } } - val alphaInAnimator = ValueAnimator.ofInt(0, 127).apply { - duration = 167 - addUpdateListener { animator -> - rippleShader.color = ColorUtils.setAlphaComponent( - rippleShader.color, - animator.animatedValue as Int - ) - invalidate() - } - } - - val alphaOutAnimator = ValueAnimator.ofInt(127, 0).apply { - startDelay = 417 - duration = 1116 + val alphaInAnimator = ValueAnimator.ofInt(0, 255).apply { + duration = alphaInDuration addUpdateListener { animator -> rippleShader.color = ColorUtils.setAlphaComponent( rippleShader.color, @@ -120,8 +111,7 @@ class AuthRippleView(context: Context?, attrs: AttributeSet?) : View(context, at playTogether( rippleAnimator, revealAnimator, - alphaInAnimator, - alphaOutAnimator + alphaInAnimator ) addListener(object : AnimatorListenerAdapter() { override fun onAnimationStart(animation: Animator?) { @@ -148,7 +138,7 @@ class AuthRippleView(context: Context?, attrs: AttributeSet?) : View(context, at // the active effect area. Values here should be kept in sync with the // animation implementation in the ripple shader. val maskRadius = (1 - (1 - rippleShader.progress) * (1 - rippleShader.progress) * - (1 - rippleShader.progress)) * radius * 1.5f + (1 - rippleShader.progress)) * radius * 2f canvas?.drawCircle(origin.x, origin.y, maskRadius, ripplePaint) } } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java index 69f9004a0b47..b5378cd22904 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java @@ -43,7 +43,6 @@ import android.os.Looper; import android.os.PowerManager; import android.os.Process; import android.os.RemoteException; -import android.os.SystemClock; import android.os.Trace; import android.os.VibrationEffect; import android.os.Vibrator; @@ -76,6 +75,7 @@ import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.concurrency.DelayableExecutor; import com.android.systemui.util.concurrency.Execution; +import com.android.systemui.util.time.SystemClock; import java.util.Optional; @@ -125,6 +125,7 @@ public class UdfpsController implements DozeReceiver { @Nullable private final UdfpsHbmProvider mHbmProvider; @NonNull private final KeyguardBypassController mKeyguardBypassController; @NonNull private final ConfigurationController mConfigurationController; + @NonNull private final SystemClock mSystemClock; @VisibleForTesting @NonNull final BiometricOrientationEventListener mOrientationListener; // Currently the UdfpsController supports a single UDFPS sensor. If devices have multiple // sensors, this, in addition to a lot of the code here, will be updated. @@ -449,19 +450,19 @@ public class UdfpsController implements DozeReceiver { final String touchInfo = String.format( "minor: %.1f, major: %.1f, v: %.1f, exceedsVelocityThreshold: %b", minor, major, v, exceedsVelocityThreshold); - final long sinceLastLog = SystemClock.elapsedRealtime() - mTouchLogTime; + final long sinceLastLog = mSystemClock.elapsedRealtime() - mTouchLogTime; if (!isIlluminationRequested && !mGoodCaptureReceived && !exceedsVelocityThreshold) { onFingerDown((int) event.getRawX(), (int) event.getRawY(), minor, major); Log.v(TAG, "onTouch | finger down: " + touchInfo); - mTouchLogTime = SystemClock.elapsedRealtime(); - mPowerManager.userActivity(SystemClock.uptimeMillis(), + mTouchLogTime = mSystemClock.elapsedRealtime(); + mPowerManager.userActivity(mSystemClock.uptimeMillis(), PowerManager.USER_ACTIVITY_EVENT_TOUCH, 0); handled = true; } else if (sinceLastLog >= MIN_TOUCH_LOG_INTERVAL) { Log.v(TAG, "onTouch | finger move: " + touchInfo); - mTouchLogTime = SystemClock.elapsedRealtime(); + mTouchLogTime = mSystemClock.elapsedRealtime(); } } else { Log.v(TAG, "onTouch | finger outside"); @@ -525,7 +526,8 @@ public class UdfpsController implements DozeReceiver { @NonNull KeyguardBypassController keyguardBypassController, @NonNull DisplayManager displayManager, @Main Handler mainHandler, - @NonNull ConfigurationController configurationController) { + @NonNull ConfigurationController configurationController, + @NonNull SystemClock systemClock) { mContext = context; mExecution = execution; // TODO (b/185124905): inject main handler and vibrator once done prototyping @@ -561,6 +563,7 @@ public class UdfpsController implements DozeReceiver { mainHandler); mKeyguardBypassController = keyguardBypassController; mConfigurationController = configurationController; + mSystemClock = systemClock; mSensorProps = findFirstUdfps(); // At least one UDFPS sensor exists @@ -781,6 +784,7 @@ public class UdfpsController implements DozeReceiver { mKeyguardViewMediator, mLockscreenShadeTransitionController, mConfigurationController, + mSystemClock, this ); case IUdfpsOverlayController.REASON_AUTH_BP: diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java index 79a4a0bd51ab..b81b54720fbd 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java @@ -35,6 +35,7 @@ import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.util.concurrency.DelayableExecutor; +import com.android.systemui.util.time.SystemClock; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -51,6 +52,7 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud @NonNull private final KeyguardViewMediator mKeyguardViewMediator; @NonNull private final LockscreenShadeTransitionController mLockScreenShadeTransitionController; @NonNull private final ConfigurationController mConfigurationController; + @NonNull private final SystemClock mSystemClock; @NonNull private final UdfpsController mUdfpsController; private boolean mShowingUdfpsBouncer; @@ -60,6 +62,7 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud private int mStatusBarState; private float mTransitionToFullShadeProgress; private float mLastDozeAmount; + private long mLastUdfpsBouncerShowTime = -1; /** * hidden amount of pin/pattern/password bouncer @@ -80,6 +83,7 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud @NonNull KeyguardViewMediator keyguardViewMediator, @NonNull LockscreenShadeTransitionController transitionController, @NonNull ConfigurationController configurationController, + @NonNull SystemClock systemClock, @NonNull UdfpsController udfpsController) { super(view, statusBarStateController, statusBarOptional, dumpManager); mKeyguardViewManager = statusBarKeyguardViewManager; @@ -88,6 +92,7 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud mKeyguardViewMediator = keyguardViewMediator; mLockScreenShadeTransitionController = transitionController; mConfigurationController = configurationController; + mSystemClock = systemClock; mUdfpsController = udfpsController; } @@ -156,6 +161,9 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud } mShowingUdfpsBouncer = show; + if (mShowingUdfpsBouncer) { + mLastUdfpsBouncerShowTime = mSystemClock.uptimeMillis(); + } updatePauseAuth(); if (mShowingUdfpsBouncer) { if (mStatusBarState == StatusBarState.SHADE_LOCKED) { @@ -219,16 +227,25 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud * If we were previously showing the udfps bouncer, hide it and instead show the regular * (pin/pattern/password) bouncer. * - * Does nothing if we weren't previously showing the udfps bouncer. + * Does nothing if we weren't previously showing the UDFPS bouncer. */ private void maybeShowInputBouncer() { - if (mShowingUdfpsBouncer) { + if (mShowingUdfpsBouncer && hasUdfpsBouncerShownWithMinTime()) { mKeyguardViewManager.showBouncer(true); mKeyguardViewManager.resetAlternateAuth(false); } } /** + * Whether the udfps bouncer has shown for at least 200ms before allowing touches outside + * of the udfps icon area to dismiss the udfps bouncer and show the pin/pattern/password + * bouncer. + */ + private boolean hasUdfpsBouncerShownWithMinTime() { + return (mSystemClock.uptimeMillis() - mLastUdfpsBouncerShowTime) > 200; + } + + /** * Set the progress we're currently transitioning to the full shade. 0.0f means we're not * transitioning yet, while 1.0f means we've fully dragged down. */ diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java index 2d200e3f5456..c093219de59b 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java @@ -68,21 +68,15 @@ import com.android.systemui.navigationbar.NavigationBarController; import com.android.systemui.navigationbar.NavigationBarOverlayController; import com.android.systemui.navigationbar.NavigationModeController; import com.android.systemui.navigationbar.TaskbarDelegate; -import com.android.systemui.plugins.PluginInitializerImpl; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.qs.ReduceBrightColorsController; import com.android.systemui.recents.OverviewProxyService; import com.android.systemui.recents.Recents; import com.android.systemui.settings.UserTracker; -import com.android.systemui.shared.plugins.PluginManager; -import com.android.systemui.shared.plugins.PluginManagerImpl; import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.DevicePolicyManagerWrapper; import com.android.systemui.shared.system.TaskStackChangeListeners; import com.android.systemui.shared.system.WindowManagerWrapper; -import com.android.unfold.UnfoldTransitionFactory; -import com.android.unfold.UnfoldTransitionProgressProvider; -import com.android.unfold.config.UnfoldTransitionConfig; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.NotificationRemoteInputManager; import com.android.systemui.statusbar.NotificationShadeDepthController; @@ -98,6 +92,9 @@ import com.android.systemui.statusbar.policy.NetworkController; import com.android.systemui.theme.ThemeOverlayApplier; import com.android.systemui.util.leak.LeakDetector; import com.android.systemui.util.settings.SecureSettings; +import com.android.systemui.unfold.UnfoldTransitionFactory; +import com.android.systemui.unfold.UnfoldTransitionProgressProvider; +import com.android.systemui.unfold.config.UnfoldTransitionConfig; import com.android.wm.shell.legacysplitscreen.LegacySplitScreen; import com.android.wm.shell.pip.Pip; @@ -196,13 +193,6 @@ public class DependencyProvider { } /** */ - @Provides - @SysUISingleton - public PluginManager providePluginManager(Context context) { - return new PluginManagerImpl(context, new PluginInitializerImpl()); - } - - /** */ @SysUISingleton @Provides static ThemeOverlayApplier provideThemeOverlayManager(Context context, diff --git a/packages/SystemUI/src/com/android/systemui/dagger/GlobalModule.java b/packages/SystemUI/src/com/android/systemui/dagger/GlobalModule.java index a89c7acea984..648f345205db 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/GlobalModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/GlobalModule.java @@ -23,6 +23,7 @@ import android.util.DisplayMetrics; import com.android.internal.logging.UiEventLogger; import com.android.internal.logging.UiEventLoggerImpl; import com.android.systemui.dagger.qualifiers.TestHarness; +import com.android.systemui.plugins.PluginsModule; import com.android.systemui.util.concurrency.GlobalConcurrencyModule; import javax.inject.Singleton; @@ -47,7 +48,9 @@ import dagger.Provides; */ @Module(includes = { FrameworkServicesModule.class, - GlobalConcurrencyModule.class}) + GlobalConcurrencyModule.class, + PluginsModule.class, +}) public class GlobalModule { /** */ diff --git a/packages/SystemUI/src/com/android/systemui/dagger/GlobalRootComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/GlobalRootComponent.java index d648c949ffc5..a3a45fe7ae40 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/GlobalRootComponent.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/GlobalRootComponent.java @@ -18,8 +18,6 @@ package com.android.systemui.dagger; import android.content.Context; -import com.android.systemui.util.concurrency.ThreadFactory; - import javax.inject.Singleton; import dagger.BindsInstance; @@ -55,9 +53,4 @@ public interface GlobalRootComponent { * Builder for a SysUIComponent. */ SysUIComponent.Builder getSysUIComponent(); - - /** - * Build a {@link ThreadFactory}. - */ - ThreadFactory createThreadFactory(); } diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java index 470d2f364c1c..98d2739836a9 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java @@ -31,6 +31,7 @@ import android.os.UserHandle; import android.provider.Settings; import android.view.Display; +import com.android.systemui.dock.DockManager; import com.android.systemui.doze.dagger.BrightnessSensor; import com.android.systemui.doze.dagger.DozeScope; import com.android.systemui.doze.dagger.WrappedService; @@ -63,6 +64,7 @@ public class DozeScreenBrightness extends BroadcastReceiver implements DozeMachi private final Optional<Sensor> mLightSensorOptional; private final WakefulnessLifecycle mWakefulnessLifecycle; private final DozeParameters mDozeParameters; + private final DockManager mDockManager; private final int[] mSensorToBrightness; private final int[] mSensorToScrimOpacity; private final int mScreenBrightnessDim; @@ -87,7 +89,8 @@ public class DozeScreenBrightness extends BroadcastReceiver implements DozeMachi @BrightnessSensor Optional<Sensor> lightSensorOptional, DozeHost host, Handler handler, AlwaysOnDisplayPolicy alwaysOnDisplayPolicy, WakefulnessLifecycle wakefulnessLifecycle, - DozeParameters dozeParameters) { + DozeParameters dozeParameters, + DockManager dockManager) { mContext = context; mDozeService = service; mSensorManager = sensorManager; @@ -96,6 +99,7 @@ public class DozeScreenBrightness extends BroadcastReceiver implements DozeMachi mDozeParameters = dozeParameters; mDozeHost = host; mHandler = handler; + mDockManager = dockManager; mDefaultDozeBrightness = alwaysOnDisplayPolicy.defaultDozeBrightness; mScreenBrightnessDim = alwaysOnDisplayPolicy.dimBrightness; @@ -122,13 +126,20 @@ public class DozeScreenBrightness extends BroadcastReceiver implements DozeMachi @Override public void onScreenState(int state) { - if (state == Display.STATE_DOZE || state == Display.STATE_DOZE_SUSPEND) { + boolean isDockedScreenOn = state == Display.STATE_ON && mDockManager.isDocked(); + if (state == Display.STATE_DOZE || state == Display.STATE_DOZE_SUSPEND + || (isDockedScreenOn && shouldRegisterLightSensorWhenScreenOnDocked())) { setLightSensorEnabled(true); } else { setLightSensorEnabled(false); } } + private boolean shouldRegisterLightSensorWhenScreenOnDocked() { + return !mDozeParameters.brightnessUsesProx() + || !mDozeParameters.getSelectivelyRegisterSensorsUsingProx(); + } + private void onDestroy() { setLightSensorEnabled(false); } diff --git a/packages/SystemUI/src/com/android/systemui/dump/DumpManager.kt b/packages/SystemUI/src/com/android/systemui/dump/DumpManager.kt index bfa478088cad..5b327bd5e4c1 100644 --- a/packages/SystemUI/src/com/android/systemui/dump/DumpManager.kt +++ b/packages/SystemUI/src/com/android/systemui/dump/DumpManager.kt @@ -34,7 +34,7 @@ import javax.inject.Inject * See [DumpHandler] for more information on how and when this information is dumped. */ @SysUISingleton -class DumpManager @Inject constructor() { +open class DumpManager @Inject constructor() { private val dumpables: MutableMap<String, RegisteredDumpable<Dumpable>> = ArrayMap() private val buffers: MutableMap<String, RegisteredDumpable<LogBuffer>> = ArrayMap() diff --git a/packages/SystemUI/src/com/android/systemui/flags/SystemPropertiesHelper.kt b/packages/SystemUI/src/com/android/systemui/flags/SystemPropertiesHelper.kt index 28f63b07e584..6561bd5a5323 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/SystemPropertiesHelper.kt +++ b/packages/SystemUI/src/com/android/systemui/flags/SystemPropertiesHelper.kt @@ -25,8 +25,12 @@ import javax.inject.Inject * Proxy to make {@link SystemProperties} easily testable. */ @SysUISingleton -class SystemPropertiesHelper @Inject constructor() { +open class SystemPropertiesHelper @Inject constructor() { fun getBoolean(name: String, default: Boolean): Boolean { return SystemProperties.getBoolean(name, default) } + + fun set(name: String, value: Int) { + SystemProperties.set(name, value.toString()) + } }
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java index 04a0226aa0b4..df6aa348ed84 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java @@ -62,6 +62,7 @@ import com.android.internal.logging.UiEventLogger; import com.android.internal.statusbar.IStatusBarService; import com.android.internal.view.RotationPolicy; import com.android.internal.widget.LockPatternUtils; +import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.animation.Interpolators; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.colorextraction.SysuiColorExtractor; @@ -173,7 +174,8 @@ public class GlobalActionsDialog extends GlobalActionsDialogLite SysUiState sysUiState, @Main Handler handler, PackageManager packageManager, - Optional<StatusBar> statusBarOptional) { + Optional<StatusBar> statusBarOptional, + KeyguardUpdateMonitor keyguardUpdateMonitor) { super(context, windowManagerFuncs, @@ -204,7 +206,8 @@ public class GlobalActionsDialog extends GlobalActionsDialogLite sysUiState, handler, packageManager, - statusBarOptional); + statusBarOptional, + keyguardUpdateMonitor); mLockPatternUtils = lockPatternUtils; mKeyguardStateController = keyguardStateController; @@ -266,7 +269,7 @@ public class GlobalActionsDialog extends GlobalActionsDialogLite this::getWalletViewController, mSysuiColorExtractor, mStatusBarService, mNotificationShadeWindowController, mSysUiState, this::onRotate, isKeyguardShowing(), mPowerAdapter, getEventLogger(), - getStatusBar()); + getStatusBar(), getKeyguardUpdateMonitor(), mLockPatternUtils); if (shouldShowLockMessage(dialog)) { dialog.showLockMessage(); @@ -334,11 +337,13 @@ public class GlobalActionsDialog extends GlobalActionsDialogLite NotificationShadeWindowController notificationShadeWindowController, SysUiState sysuiState, Runnable onRotateCallback, boolean keyguardShowing, MyPowerOptionsAdapter powerAdapter, UiEventLogger uiEventLogger, - Optional<StatusBar> statusBarOptional) { + Optional<StatusBar> statusBarOptional, KeyguardUpdateMonitor keyguardUpdateMonitor, + LockPatternUtils lockPatternUtils) { super(context, com.android.systemui.R.style.Theme_SystemUI_Dialog_GlobalActions, adapter, overflowAdapter, sysuiColorExtractor, statusBarService, notificationShadeWindowController, sysuiState, onRotateCallback, - keyguardShowing, powerAdapter, uiEventLogger, statusBarOptional); + keyguardShowing, powerAdapter, uiEventLogger, statusBarOptional, + keyguardUpdateMonitor, lockPatternUtils); mWalletFactory = walletFactory; // Update window attributes diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java index 6cd5f91653fc..b06b024a63a4 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java @@ -84,6 +84,7 @@ import android.view.Window; import android.view.WindowInsets; import android.view.WindowManager; import android.view.accessibility.AccessibilityEvent; +import android.view.accessibility.AccessibilityManager; import android.widget.BaseAdapter; import android.widget.ImageView; import android.widget.ImageView.ScaleType; @@ -108,6 +109,7 @@ import com.android.internal.statusbar.IStatusBarService; import com.android.internal.util.EmergencyAffordanceManager; import com.android.internal.util.ScreenshotHelper; import com.android.internal.widget.LockPatternUtils; +import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.MultiListLayout; import com.android.systemui.MultiListLayout.MultiListAdapter; import com.android.systemui.animation.Interpolators; @@ -171,6 +173,11 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene static final String GLOBAL_ACTION_KEY_EMERGENCY = "emergency"; static final String GLOBAL_ACTION_KEY_SCREENSHOT = "screenshot"; + // See NotificationManagerService#scheduleDurationReachedLocked + private static final long TOAST_FADE_TIME = 333; + // See NotificationManagerService.LONG_DELAY + private static final int TOAST_VISIBLE_TIME = 3500; + private final Context mContext; private final GlobalActionsManager mWindowManagerFuncs; private final AudioManager mAudioManager; @@ -231,6 +238,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene protected Handler mMainHandler; private int mSmallestScreenWidthDp; private final Optional<StatusBar> mStatusBarOptional; + private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; @VisibleForTesting public enum GlobalActionsEvent implements UiEventLogger.UiEventEnum { @@ -337,7 +345,8 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene SysUiState sysUiState, @Main Handler handler, PackageManager packageManager, - Optional<StatusBar> statusBarOptional) { + Optional<StatusBar> statusBarOptional, + KeyguardUpdateMonitor keyguardUpdateMonitor) { mContext = context; mWindowManagerFuncs = windowManagerFuncs; mAudioManager = audioManager; @@ -367,6 +376,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene mMainHandler = handler; mSmallestScreenWidthDp = resources.getConfiguration().smallestScreenWidthDp; mStatusBarOptional = statusBarOptional; + mKeyguardUpdateMonitor = keyguardUpdateMonitor; // receive broadcasts IntentFilter filter = new IntentFilter(); @@ -420,6 +430,10 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene return mStatusBarOptional; } + protected KeyguardUpdateMonitor getKeyguardUpdateMonitor() { + return mKeyguardUpdateMonitor; + } + /** * Show the global actions dialog (creating if necessary) * @@ -651,7 +665,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene mAdapter, mOverflowAdapter, mSysuiColorExtractor, mStatusBarService, mNotificationShadeWindowController, mSysUiState, this::onRotate, mKeyguardShowing, mPowerAdapter, mUiEventLogger, - mStatusBarOptional); + mStatusBarOptional, mKeyguardUpdateMonitor, mLockPatternUtils); dialog.setOnDismissListener(this); dialog.setOnShowListener(this); @@ -2119,6 +2133,8 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene private UiEventLogger mUiEventLogger; private GestureDetector mGestureDetector; private Optional<StatusBar> mStatusBarOptional; + private KeyguardUpdateMonitor mKeyguardUpdateMonitor; + private LockPatternUtils mLockPatternUtils; protected ViewGroup mContainer; @@ -2172,7 +2188,8 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene NotificationShadeWindowController notificationShadeWindowController, SysUiState sysuiState, Runnable onRotateCallback, boolean keyguardShowing, MyPowerOptionsAdapter powerAdapter, UiEventLogger uiEventLogger, - Optional<StatusBar> statusBarOptional) { + Optional<StatusBar> statusBarOptional, + KeyguardUpdateMonitor keyguardUpdateMonitor, LockPatternUtils lockPatternUtils) { super(context, themeRes); mContext = context; mAdapter = adapter; @@ -2186,6 +2203,8 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene mKeyguardShowing = keyguardShowing; mUiEventLogger = uiEventLogger; mStatusBarOptional = statusBarOptional; + mKeyguardUpdateMonitor = keyguardUpdateMonitor; + mLockPatternUtils = lockPatternUtils; mGestureDetector = new GestureDetector(mContext, mGestureListener); @@ -2304,6 +2323,14 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene mBackgroundDrawable = new ScrimDrawable(); mScrimAlpha = 1.0f; } + + // If user entered from the lock screen and smart lock was enabled, disable it + int user = KeyguardUpdateMonitor.getCurrentUser(); + boolean userHasTrust = mKeyguardUpdateMonitor.getUserHasTrust(user); + if (mKeyguardShowing && userHasTrust) { + mLockPatternUtils.requireCredentialEntry(KeyguardUpdateMonitor.getCurrentUser()); + showSmartLockDisabledMessage(); + } } protected void fixNavBarClipping() { @@ -2315,6 +2342,37 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene contentParent.setClipToPadding(false); } + private void showSmartLockDisabledMessage() { + // Since power menu is the top window, make a Toast-like view that will show up + View message = LayoutInflater.from(mContext) + .inflate(com.android.systemui.R.layout.global_actions_toast, mContainer, false); + + // Set up animation + AccessibilityManager mAccessibilityManager = + (AccessibilityManager) getContext().getSystemService( + Context.ACCESSIBILITY_SERVICE); + final int visibleTime = mAccessibilityManager.getRecommendedTimeoutMillis( + TOAST_VISIBLE_TIME, AccessibilityManager.FLAG_CONTENT_TEXT); + message.setVisibility(View.VISIBLE); + message.setAlpha(0f); + mContainer.addView(message); + + // Fade in + message.animate() + .alpha(1f) + .setDuration(TOAST_FADE_TIME) + .setListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + // Then fade out + message.animate() + .alpha(0f) + .setDuration(TOAST_FADE_TIME) + .setStartDelay(visibleTime); + } + }); + } + @Override protected void onStart() { super.onStart(); diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java index a641ad4b338b..c4508e043c7d 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java @@ -97,7 +97,7 @@ public class GlobalActionsImpl implements GlobalActions, CommandQueue.Callbacks int backgroundAlpha = (int) (ScrimController.BUSY_SCRIM_ALPHA * 255); background.setAlpha(backgroundAlpha); mBlurUtils.applyBlur(d.getWindow().getDecorView().getViewRootImpl(), - mBlurUtils.blurRadiusOfRatio(1), backgroundAlpha == 255); + (int) mBlurUtils.blurRadiusOfRatio(1), backgroundAlpha == 255); } else { float backgroundAlpha = mContext.getResources().getFloat( com.android.systemui.R.dimen.shutdown_scrim_behind_alpha); diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java index 0b8c63580234..35773955529f 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java @@ -21,6 +21,7 @@ import static android.view.Display.DEFAULT_DISPLAY; import static android.view.RemoteAnimationTarget.MODE_CLOSING; import static android.view.RemoteAnimationTarget.MODE_OPENING; import static android.view.WindowManager.TRANSIT_CLOSE; +import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY; import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_LOCKED; import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY; import static android.view.WindowManager.TRANSIT_KEYGUARD_OCCLUDE; @@ -33,6 +34,7 @@ import static android.view.WindowManager.TRANSIT_OLD_NONE; import static android.view.WindowManager.TRANSIT_OPEN; import static android.view.WindowManager.TRANSIT_TO_BACK; import static android.view.WindowManager.TRANSIT_TO_FRONT; +import static android.view.WindowManager.TransitionFlags; import static android.view.WindowManager.TransitionOldType; import static android.view.WindowManager.TransitionType; import static android.window.TransitionInfo.FLAG_OCCLUDES_KEYGUARD; @@ -170,8 +172,9 @@ public class KeyguardService extends Service { } private static @TransitionOldType int getTransitionOldType(@TransitionType int type, - RemoteAnimationTarget[] apps) { - if (type == TRANSIT_KEYGUARD_GOING_AWAY) { + @TransitionFlags int flags, RemoteAnimationTarget[] apps) { + if (type == TRANSIT_KEYGUARD_GOING_AWAY + || (flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY) != 0) { return apps.length == 0 ? TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER : TRANSIT_OLD_KEYGUARD_GOING_AWAY; } else if (type == TRANSIT_KEYGUARD_OCCLUDE) { @@ -200,7 +203,7 @@ public class KeyguardService extends Service { t.setAlpha(change.getLeash(), 1.0f); } t.apply(); - runner.onAnimationStart(getTransitionOldType(info.getType(), apps), + runner.onAnimationStart(getTransitionOldType(info.getType(), info.getFlags(), apps), apps, wallpapers, nonApps, new IRemoteAnimationFinishedCallback.Stub() { @Override @@ -232,7 +235,7 @@ public class KeyguardService extends Service { if (sEnableRemoteKeyguardGoingAwayAnimation) { Slog.d(TAG, "KeyguardService registerRemote: TRANSIT_KEYGUARD_GOING_AWAY"); TransitionFilter f = new TransitionFilter(); - f.mTypeSet = new int[]{TRANSIT_KEYGUARD_GOING_AWAY}; + f.mFlags = TRANSIT_FLAG_KEYGUARD_GOING_AWAY; shellTransitions.registerRemote(f, wrap(mExitAnimationRunner)); } if (sEnableRemoteKeyguardOccludeAnimation) { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/LifecycleScreenStatusProvider.kt b/packages/SystemUI/src/com/android/systemui/keyguard/LifecycleScreenStatusProvider.kt index d8905a076e01..f25ec5591e3a 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/LifecycleScreenStatusProvider.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/LifecycleScreenStatusProvider.kt @@ -16,8 +16,8 @@ package com.android.systemui.keyguard import com.android.systemui.dagger.SysUISingleton -import com.android.unfold.updates.screen.ScreenStatusProvider -import com.android.unfold.updates.screen.ScreenStatusProvider.ScreenListener +import com.android.systemui.unfold.updates.screen.ScreenStatusProvider +import com.android.systemui.unfold.updates.screen.ScreenStatusProvider.ScreenListener import javax.inject.Inject @SysUISingleton diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java index 1ca2217638f7..2e7ab6e48c62 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java @@ -191,8 +191,8 @@ public class NavigationBarController implements Callbacks, mNavMode = mNavigationModeController.addListener(this); mNavigationModeController.addListener(this); mTaskbarDelegate = taskbarDelegate; - mTaskbarDelegate.setOverviewProxyService(overviewProxyService, - navigationBarA11yHelper, mSysUiFlagsContainer); + mTaskbarDelegate.setOverviewProxyService(commandQueue, overviewProxyService, + navigationBarA11yHelper, navigationModeController, sysUiFlagsContainer); mIsTablet = isTablet(mContext); mUserTracker = userTracker; } @@ -251,17 +251,14 @@ public class NavigationBarController implements Callbacks, /** @return {@code true} if taskbar is enabled, false otherwise */ private boolean initializeTaskbarIfNecessary() { - boolean isShowingTaskbar = mIsTablet && mNavMode == NAV_BAR_MODE_3BUTTON; - if (isShowingTaskbar) { - // Remove navigation bar when taskbar is showing, currently only for 3 button mode + if (mIsTablet) { + // Remove navigation bar when taskbar is showing removeNavigationBar(mContext.getDisplayId()); - mCommandQueue.addCallback(mTaskbarDelegate); mTaskbarDelegate.init(mContext.getDisplayId()); } else { - mCommandQueue.removeCallback(mTaskbarDelegate); mTaskbarDelegate.destroy(); } - return isShowingTaskbar; + return mIsTablet; } @Override @@ -332,7 +329,7 @@ public class NavigationBarController implements Callbacks, return; } - if (mIsTablet && mNavMode == NAV_BAR_MODE_3BUTTON) { + if (mIsTablet) { return; } diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java index 2d36de34ad04..93388f93991f 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java @@ -79,7 +79,7 @@ import com.android.systemui.navigationbar.buttons.NearestTouchFrame; import com.android.systemui.navigationbar.buttons.RotationContextButton; import com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler; import com.android.systemui.navigationbar.gestural.FloatingRotationButton; -import com.android.systemui.navigationbar.gestural.RegionSamplingHelper; +import com.android.systemui.shared.navigationbar.RegionSamplingHelper; import com.android.systemui.recents.OverviewProxyService; import com.android.systemui.recents.Recents; import com.android.systemui.shared.system.ActivityManagerWrapper; diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java index fe24ecd62a91..e0caf125084a 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java @@ -21,15 +21,22 @@ import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SHOWN; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_CLICKABLE; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE; +import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BACK_DISABLED; +import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_HOME_DISABLED; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SHOWING; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SWITCHER_SHOWING; +import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED; +import android.content.Context; import android.inputmethodservice.InputMethodService; import android.os.IBinder; import android.view.InsetsVisibilities; +import android.view.View; import com.android.internal.view.AppearanceRegion; +import com.android.systemui.Dependency; import com.android.systemui.model.SysUiState; +import com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler; import com.android.systemui.recents.OverviewProxyService; import com.android.systemui.shared.recents.utilities.Utilities; import com.android.systemui.statusbar.CommandQueue; @@ -38,33 +45,53 @@ import javax.inject.Inject; import javax.inject.Singleton; @Singleton -public class TaskbarDelegate implements CommandQueue.Callbacks { +public class TaskbarDelegate implements CommandQueue.Callbacks, + OverviewProxyService.OverviewProxyListener, NavigationModeController.ModeChangedListener { + private final EdgeBackGestureHandler mEdgeBackGestureHandler; + + private CommandQueue mCommandQueue; private OverviewProxyService mOverviewProxyService; private NavigationBarA11yHelper mNavigationBarA11yHelper; + private NavigationModeController mNavigationModeController; private SysUiState mSysUiState; private int mDisplayId; private int mNavigationIconHints; private final NavigationBarA11yHelper.NavA11yEventListener mNavA11yEventListener = this::updateSysuiFlags; + private int mDisabledFlags; + @Inject - public TaskbarDelegate() { /* no-op */ } + public TaskbarDelegate(Context context) { + mEdgeBackGestureHandler = Dependency.get(EdgeBackGestureHandler.Factory.class) + .create(context); + } - public void setOverviewProxyService(OverviewProxyService overviewProxyService, + public void setOverviewProxyService(CommandQueue commandQueue, + OverviewProxyService overviewProxyService, NavigationBarA11yHelper navigationBarA11yHelper, + NavigationModeController navigationModeController, SysUiState sysUiState) { // TODO: adding this in the ctor results in a dagger dependency cycle :( + mCommandQueue = commandQueue; mOverviewProxyService = overviewProxyService; mNavigationBarA11yHelper = navigationBarA11yHelper; + mNavigationModeController = navigationModeController; mSysUiState = sysUiState; } public void destroy() { + mCommandQueue.removeCallback(this); + mOverviewProxyService.removeCallback(this); + mNavigationModeController.removeListener(this); mNavigationBarA11yHelper.removeA11yEventListener(mNavA11yEventListener); } public void init(int displayId) { mDisplayId = displayId; + mCommandQueue.addCallback(this); + mOverviewProxyService.addCallback(this); + mNavigationModeController.addListener(this); mNavigationBarA11yHelper.registerA11yEventListener(mNavA11yEventListener); // Set initial state for any listeners updateSysuiFlags(); @@ -81,6 +108,12 @@ public class TaskbarDelegate implements CommandQueue.Callbacks { (mNavigationIconHints & NAVIGATION_HINT_BACK_ALT) != 0) .setFlag(SYSUI_STATE_IME_SWITCHER_SHOWING, (mNavigationIconHints & NAVIGATION_HINT_IME_SHOWN) != 0) + .setFlag(SYSUI_STATE_OVERVIEW_DISABLED, + (mDisabledFlags & View.STATUS_BAR_DISABLE_RECENT) != 0) + .setFlag(SYSUI_STATE_HOME_DISABLED, + (mDisabledFlags & View.STATUS_BAR_DISABLE_HOME) != 0) + .setFlag(SYSUI_STATE_BACK_DISABLED, + (mDisabledFlags & View.STATUS_BAR_DISABLE_BACK) != 0) .commitUpdate(mDisplayId); } @@ -103,6 +136,8 @@ public class TaskbarDelegate implements CommandQueue.Callbacks { @Override public void disable(int displayId, int state1, int state2, boolean animate) { + mDisabledFlags = state1; + updateSysuiFlags(); mOverviewProxyService.disable(displayId, state1, state2, animate); } @@ -112,4 +147,18 @@ public class TaskbarDelegate implements CommandQueue.Callbacks { InsetsVisibilities requestedVisibilities, String packageName) { mOverviewProxyService.onSystemBarAttributesChanged(displayId, behavior); } + + @Override + public void onTaskbarStatusUpdated(boolean visible, boolean stashed) { + if (visible) { + mEdgeBackGestureHandler.onNavBarAttached(); + } else { + mEdgeBackGestureHandler.onNavBarDetached(); + } + } + + @Override + public void onNavigationModeChanged(int mode) { + mEdgeBackGestureHandler.onNavigationModeChanged(mode); + } } diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationBarEdgePanel.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationBarEdgePanel.java index ea04cefb8f70..8d1dfc842fba 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationBarEdgePanel.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationBarEdgePanel.java @@ -55,6 +55,7 @@ import com.android.systemui.Dependency; import com.android.systemui.R; import com.android.systemui.animation.Interpolators; import com.android.systemui.plugins.NavigationEdgeBackPlugin; +import com.android.systemui.shared.navigationbar.RegionSamplingHelper; import com.android.systemui.statusbar.VibratorHelper; import java.io.PrintWriter; diff --git a/packages/SystemUI/src/com/android/systemui/plugins/PluginDependencyProvider.java b/packages/SystemUI/src/com/android/systemui/plugins/PluginDependencyProvider.java index ad1e21d7cc45..0b565ea25911 100644 --- a/packages/SystemUI/src/com/android/systemui/plugins/PluginDependencyProvider.java +++ b/packages/SystemUI/src/com/android/systemui/plugins/PluginDependencyProvider.java @@ -17,25 +17,27 @@ package com.android.systemui.plugins; import android.util.ArrayMap; import com.android.systemui.Dependency; -import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.plugins.PluginDependency.DependencyProvider; import com.android.systemui.shared.plugins.PluginManager; import javax.inject.Inject; +import javax.inject.Singleton; + +import dagger.Lazy; /** */ -@SysUISingleton +@Singleton public class PluginDependencyProvider extends DependencyProvider { private final ArrayMap<Class<?>, Object> mDependencies = new ArrayMap<>(); - private final PluginManager mManager; + private final Lazy<PluginManager> mManagerLazy; /** */ @Inject - public PluginDependencyProvider(PluginManager manager) { - mManager = manager; + public PluginDependencyProvider(Lazy<PluginManager> managerLazy) { + mManagerLazy = managerLazy; PluginDependency.sProvider = this; } @@ -51,7 +53,7 @@ public class PluginDependencyProvider extends DependencyProvider { @Override <T> T get(Plugin p, Class<T> cls) { - if (!mManager.dependsOn(p, cls)) { + if (!mManagerLazy.get().dependsOn(p, cls)) { throw new IllegalArgumentException(p.getClass() + " does not depend on " + cls); } synchronized (mDependencies) { diff --git a/packages/SystemUI/src/com/android/systemui/plugins/PluginEnablerImpl.java b/packages/SystemUI/src/com/android/systemui/plugins/PluginEnablerImpl.java index 63374150adaa..40f59744e038 100644 --- a/packages/SystemUI/src/com/android/systemui/plugins/PluginEnablerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/plugins/PluginEnablerImpl.java @@ -22,16 +22,22 @@ import android.content.pm.PackageManager; import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.shared.plugins.PluginEnabler; +import javax.inject.Inject; +import javax.inject.Singleton; + +/** */ +@Singleton public class PluginEnablerImpl implements PluginEnabler { private static final String CRASH_DISABLED_PLUGINS_PREF_FILE = "auto_disabled_plugins_prefs"; - private PackageManager mPm; + private final PackageManager mPm; private final SharedPreferences mAutoDisabledPrefs; public PluginEnablerImpl(Context context) { this(context, context.getPackageManager()); } + @Inject @VisibleForTesting public PluginEnablerImpl(Context context, PackageManager pm) { mAutoDisabledPrefs = context.getSharedPreferences( CRASH_DISABLED_PLUGINS_PREF_FILE, Context.MODE_PRIVATE); diff --git a/packages/SystemUI/src/com/android/systemui/plugins/PluginInitializerImpl.java b/packages/SystemUI/src/com/android/systemui/plugins/PluginInitializerImpl.java index 7f01d6f1ffa3..654d000ca09e 100644 --- a/packages/SystemUI/src/com/android/systemui/plugins/PluginInitializerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/plugins/PluginInitializerImpl.java @@ -15,16 +15,17 @@ package com.android.systemui.plugins; import android.content.Context; -import android.os.Build; -import android.os.Looper; import android.util.Log; -import com.android.systemui.Dependency; import com.android.systemui.R; -import com.android.systemui.shared.plugins.PluginEnabler; import com.android.systemui.shared.plugins.PluginInitializer; import com.android.systemui.shared.plugins.PluginManagerImpl; +import javax.inject.Inject; +import javax.inject.Singleton; + +/** */ +@Singleton public class PluginInitializerImpl implements PluginInitializer { /** @@ -33,44 +34,24 @@ public class PluginInitializerImpl implements PluginInitializer { private static final boolean WTFS_SHOULD_CRASH = false; private boolean mWtfsSet; - @Override - public Looper getBgLooper() { - return Dependency.get(Dependency.BG_LOOPER); - } - - @Override - public void onPluginManagerInit() { - // Plugin dependencies that don't have another good home can go here, but - // dependencies that have better places to init can happen elsewhere. - Dependency.get(PluginDependencyProvider.class) - .allowPluginDependency(ActivityStarter.class); + @Inject + public PluginInitializerImpl(PluginDependencyProvider dependencyProvider) { + dependencyProvider.allowPluginDependency(ActivityStarter.class); } @Override - public String[] getWhitelistedPlugins(Context context) { + public String[] getPrivilegedPlugins(Context context) { return context.getResources().getStringArray(R.array.config_pluginWhitelist); } - public PluginEnabler getPluginEnabler(Context context) { - return new PluginEnablerImpl(context); - } @Override public void handleWtfs() { if (WTFS_SHOULD_CRASH && !mWtfsSet) { mWtfsSet = true; - Log.setWtfHandler(new Log.TerribleFailureHandler() { - @Override - public void onTerribleFailure(String tag, Log.TerribleFailure what, - boolean system) { - throw new PluginManagerImpl.CrashWhilePluginActiveException(what); - } + Log.setWtfHandler((tag, what, system) -> { + throw new PluginManagerImpl.CrashWhilePluginActiveException(what); }); } } - - @Override - public boolean isDebuggable() { - return Build.IS_DEBUGGABLE; - } } diff --git a/packages/SystemUI/src/com/android/systemui/plugins/PluginsModule.java b/packages/SystemUI/src/com/android/systemui/plugins/PluginsModule.java new file mode 100644 index 000000000000..1ea9b3c08b1e --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/plugins/PluginsModule.java @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.plugins; + +import static com.android.systemui.util.concurrency.GlobalConcurrencyModule.PRE_HANDLER; + +import android.app.NotificationManager; +import android.content.Context; +import android.content.pm.PackageManager; +import android.os.Build; + +import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.shared.plugins.PluginEnabler; +import com.android.systemui.shared.plugins.PluginInitializer; +import com.android.systemui.shared.plugins.PluginInstanceManager; +import com.android.systemui.shared.plugins.PluginManager; +import com.android.systemui.shared.plugins.PluginManagerImpl; +import com.android.systemui.shared.plugins.PluginPrefs; +import com.android.systemui.util.concurrency.GlobalConcurrencyModule; +import com.android.systemui.util.concurrency.ThreadFactory; + +import java.util.Arrays; +import java.util.List; +import java.util.Optional; +import java.util.concurrent.Executor; + +import javax.inject.Named; +import javax.inject.Singleton; + +import dagger.Binds; +import dagger.Module; +import dagger.Provides; + +/** + * Dagger Module for code related to plugins. + * + * Covers code both in com.android.systemui.plugins and code in + * com.android.systemui.shared.plugins. + */ +@Module(includes = {GlobalConcurrencyModule.class}) +public abstract class PluginsModule { + public static final String PLUGIN_THREAD = "plugin_thread"; + public static final String PLUGIN_DEBUG = "plugin_debug"; + public static final String PLUGIN_PRIVILEGED = "plugin_privileged"; + + @Provides + @Named(PLUGIN_DEBUG) + static boolean providesPluginDebug() { + return Build.IS_DEBUGGABLE; + } + + @Binds + abstract PluginEnabler bindsPluginEnablerImpl(PluginEnablerImpl impl); + + @Binds + abstract PluginInitializer bindsPluginInitializerImpl(PluginInitializerImpl impl); + + @Provides + @Singleton + static PluginInstanceManager.Factory providePluginInstanceManagerFactory(Context context, + PackageManager packageManager, @Main Executor mainExecutor, + @Named(PLUGIN_THREAD) Executor pluginExecutor, PluginInitializer initializer, + NotificationManager notificationManager, PluginEnabler pluginEnabler, + @Named(PLUGIN_PRIVILEGED) List<String> privilegedPlugins) { + return new PluginInstanceManager.Factory( + context, packageManager, mainExecutor, pluginExecutor, initializer, + notificationManager, pluginEnabler, privilegedPlugins); + } + + @Provides + @Singleton + @Named(PLUGIN_THREAD) + static Executor providesPluginExecutor(ThreadFactory threadFactory) { + return threadFactory.buildExecutorOnNewThread("plugin"); + } + + @Provides + static PluginManager providesPluginManager( + Context context, + PluginInstanceManager.Factory instanceManagerFactory, + @Named(PLUGIN_DEBUG) boolean debug, + @Named(PRE_HANDLER) + Optional<Thread.UncaughtExceptionHandler> uncaughtExceptionHandlerOptional, + PluginEnabler pluginEnabler, + PluginPrefs pluginPrefs, + @Named(PLUGIN_PRIVILEGED) List<String> privilegedPlugins) { + return new PluginManagerImpl(context, instanceManagerFactory, debug, + uncaughtExceptionHandlerOptional, pluginEnabler, pluginPrefs, + privilegedPlugins); + } + + @Provides + static PluginPrefs providesPluginPrefs(Context context) { + return new PluginPrefs(context); + } + + @Provides + @Named(PLUGIN_PRIVILEGED) + static List<String> providesPrivilegedPlugins(PluginInitializer initializer, Context context) { + return Arrays.asList(initializer.getPrivilegedPlugins(context)); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.java b/packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.java deleted file mode 100644 index 38b20ee45946..000000000000 --- a/packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.java +++ /dev/null @@ -1,156 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file - * except in compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the - * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -package com.android.systemui.qs; - -import static com.android.systemui.statusbar.phone.AutoTileManager.HOTSPOT; -import static com.android.systemui.statusbar.phone.AutoTileManager.INVERSION; -import static com.android.systemui.statusbar.phone.AutoTileManager.NIGHT; -import static com.android.systemui.statusbar.phone.AutoTileManager.SAVER; -import static com.android.systemui.statusbar.phone.AutoTileManager.WORK; - -import android.content.Context; -import android.database.ContentObserver; -import android.os.Handler; -import android.os.UserHandle; -import android.provider.Settings.Secure; -import android.text.TextUtils; -import android.util.ArraySet; - -import com.android.internal.annotations.VisibleForTesting; -import com.android.systemui.Prefs; -import com.android.systemui.Prefs.Key; -import com.android.systemui.util.UserAwareController; - -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; - -import javax.inject.Inject; - -public class AutoAddTracker implements UserAwareController { - - private static final String[][] CONVERT_PREFS = { - {Key.QS_HOTSPOT_ADDED, HOTSPOT}, - {Key.QS_DATA_SAVER_ADDED, SAVER}, - {Key.QS_INVERT_COLORS_ADDED, INVERSION}, - {Key.QS_WORK_ADDED, WORK}, - {Key.QS_NIGHTDISPLAY_ADDED, NIGHT}, - }; - - private final ArraySet<String> mAutoAdded; - private final Context mContext; - private int mUserId; - - public AutoAddTracker(Context context, int userId) { - mContext = context; - mUserId = userId; - mAutoAdded = new ArraySet<>(getAdded()); - } - - /** - * Init method must be called after construction to start listening - */ - public void initialize() { - // TODO: remove migration code and shared preferences keys after P release - if (mUserId == UserHandle.USER_SYSTEM) { - for (String[] convertPref : CONVERT_PREFS) { - if (Prefs.getBoolean(mContext, convertPref[0], false)) { - setTileAdded(convertPref[1]); - Prefs.remove(mContext, convertPref[0]); - } - } - } - mContext.getContentResolver().registerContentObserver( - Secure.getUriFor(Secure.QS_AUTO_ADDED_TILES), false, mObserver, - UserHandle.USER_ALL); - } - - @Override - public void changeUser(UserHandle newUser) { - if (newUser.getIdentifier() == mUserId) { - return; - } - mUserId = newUser.getIdentifier(); - mAutoAdded.clear(); - mAutoAdded.addAll(getAdded()); - } - - @Override - public int getCurrentUserId() { - return mUserId; - } - - public boolean isAdded(String tile) { - return mAutoAdded.contains(tile); - } - - public void setTileAdded(String tile) { - if (mAutoAdded.add(tile)) { - saveTiles(); - } - } - - public void setTileRemoved(String tile) { - if (mAutoAdded.remove(tile)) { - saveTiles(); - } - } - - public void destroy() { - mContext.getContentResolver().unregisterContentObserver(mObserver); - } - - private void saveTiles() { - Secure.putStringForUser(mContext.getContentResolver(), Secure.QS_AUTO_ADDED_TILES, - TextUtils.join(",", mAutoAdded), mUserId); - } - - private Collection<String> getAdded() { - String current = Secure.getStringForUser(mContext.getContentResolver(), - Secure.QS_AUTO_ADDED_TILES, mUserId); - if (current == null) { - return Collections.emptyList(); - } - return Arrays.asList(current.split(",")); - } - - @VisibleForTesting - protected final ContentObserver mObserver = new ContentObserver(new Handler()) { - @Override - public void onChange(boolean selfChange) { - mAutoAdded.clear(); - mAutoAdded.addAll(getAdded()); - } - }; - - public static class Builder { - private final Context mContext; - private int mUserId; - - @Inject - public Builder(Context context) { - mContext = context; - } - - public Builder setUserId(int userId) { - mUserId = userId; - return this; - } - - public AutoAddTracker build() { - return new AutoAddTracker(mContext, mUserId); - } - } -} diff --git a/packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.kt b/packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.kt new file mode 100644 index 000000000000..7ffa9d931ff0 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.kt @@ -0,0 +1,285 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs + +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.content.IntentFilter +import android.database.ContentObserver +import android.net.Uri +import android.os.Handler +import android.os.UserHandle +import android.provider.Settings +import android.text.TextUtils +import android.util.ArraySet +import android.util.Log +import androidx.annotation.GuardedBy +import androidx.annotation.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.dagger.qualifiers.Main +import com.android.systemui.dump.DumpManager +import com.android.systemui.util.UserAwareController +import com.android.systemui.util.settings.SecureSettings +import java.io.FileDescriptor +import java.io.PrintWriter +import java.util.concurrent.Executor +import javax.inject.Inject + +private const val TAG = "AutoAddTracker" + +/** + * Class to track tiles that have been auto-added + * + * The list is backed by [Settings.Secure.QS_AUTO_ADDED_TILES]. + * + * It also handles restore gracefully. + */ +class AutoAddTracker @VisibleForTesting constructor( + private val secureSettings: SecureSettings, + private val broadcastDispatcher: BroadcastDispatcher, + private val qsHost: QSHost, + private val dumpManager: DumpManager, + private val mainHandler: Handler?, + private val backgroundExecutor: Executor, + private var userId: Int +) : UserAwareController, Dumpable { + + companion object { + private val FILTER = IntentFilter(Intent.ACTION_SETTING_RESTORED) + } + + @GuardedBy("autoAdded") + private val autoAdded = ArraySet<String>() + private var restoredTiles: Set<String>? = null + + override val currentUserId: Int + get() = userId + + private val contentObserver = object : ContentObserver(mainHandler) { + override fun onChange( + selfChange: Boolean, + uris: Collection<Uri>, + flags: Int, + _userId: Int + ) { + if (_userId != userId) { + // Ignore changes outside of our user. We'll load the correct value on user change + return + } + loadTiles() + } + } + + private val restoreReceiver = object : BroadcastReceiver() { + override fun onReceive(context: Context, intent: Intent) { + if (intent.action != Intent.ACTION_SETTING_RESTORED) return + processRestoreIntent(intent) + } + } + + private fun processRestoreIntent(intent: Intent) { + when (intent.getStringExtra(Intent.EXTRA_SETTING_NAME)) { + Settings.Secure.QS_TILES -> { + restoredTiles = intent.getStringExtra(Intent.EXTRA_SETTING_NEW_VALUE) + ?.split(",") + ?.toSet() + ?: run { + Log.w(TAG, "Null restored tiles for user $userId") + emptySet() + } + } + Settings.Secure.QS_AUTO_ADDED_TILES -> { + restoredTiles?.let { tiles -> + val restoredAutoAdded = intent + .getStringExtra(Intent.EXTRA_SETTING_NEW_VALUE) + ?.split(",") + ?: emptyList() + val autoAddedBeforeRestore = intent + .getStringExtra(Intent.EXTRA_SETTING_PREVIOUS_VALUE) + ?.split(",") + ?: emptyList() + + val tilesToRemove = restoredAutoAdded.filter { it !in tiles } + if (tilesToRemove.isNotEmpty()) { + qsHost.removeTiles(tilesToRemove) + } + val tiles = synchronized(autoAdded) { + autoAdded.clear() + autoAdded.addAll(restoredAutoAdded + autoAddedBeforeRestore) + getTilesFromListLocked() + } + saveTiles(tiles) + } ?: run { + Log.w(TAG, "${Settings.Secure.QS_AUTO_ADDED_TILES} restored before " + + "${Settings.Secure.QS_TILES} for user $userId") + } + } + else -> {} // Do nothing for other Settings + } + } + + /** + * Init method must be called after construction to start listening + */ + fun initialize() { + dumpManager.registerDumpable(TAG, this) + loadTiles() + secureSettings.registerContentObserverForUser( + secureSettings.getUriFor(Settings.Secure.QS_AUTO_ADDED_TILES), + contentObserver, + UserHandle.USER_ALL + ) + registerBroadcastReceiver() + } + + /** + * Unregister listeners, receivers and observers + */ + fun destroy() { + dumpManager.unregisterDumpable(TAG) + secureSettings.unregisterContentObserver(contentObserver) + unregisterBroadcastReceiver() + } + + private fun registerBroadcastReceiver() { + broadcastDispatcher.registerReceiver( + restoreReceiver, + FILTER, + backgroundExecutor, + UserHandle.of(userId) + ) + } + + private fun unregisterBroadcastReceiver() { + broadcastDispatcher.unregisterReceiver(restoreReceiver) + } + + override fun changeUser(newUser: UserHandle) { + if (newUser.identifier == userId) return + unregisterBroadcastReceiver() + userId = newUser.identifier + restoredTiles = null + loadTiles() + registerBroadcastReceiver() + } + + /** + * Returns `true` if the tile has been auto-added before + */ + fun isAdded(tile: String): Boolean { + return synchronized(autoAdded) { + tile in autoAdded + } + } + + /** + * Sets a tile as auto-added. + * + * From here on, [isAdded] will return true for that tile. + */ + fun setTileAdded(tile: String) { + val tiles = synchronized(autoAdded) { + if (autoAdded.add(tile)) { + getTilesFromListLocked() + } else { + null + } + } + tiles?.let { saveTiles(it) } + } + + /** + * Removes a tile from the list of auto-added. + * + * This allows for this tile to be auto-added again in the future. + */ + fun setTileRemoved(tile: String) { + val tiles = synchronized(autoAdded) { + if (autoAdded.remove(tile)) { + getTilesFromListLocked() + } else { + null + } + } + tiles?.let { saveTiles(it) } + } + + private fun getTilesFromListLocked(): String { + return TextUtils.join(",", autoAdded) + } + + private fun saveTiles(tiles: String) { + secureSettings.putStringForUser( + Settings.Secure.QS_AUTO_ADDED_TILES, + tiles, + /* tag */ null, + /* makeDefault */ false, + userId, + /* overrideableByRestore */ true + ) + } + + private fun loadTiles() { + synchronized(autoAdded) { + autoAdded.clear() + autoAdded.addAll(getAdded()) + } + } + + private fun getAdded(): Collection<String> { + val current = secureSettings.getStringForUser(Settings.Secure.QS_AUTO_ADDED_TILES, userId) + return current?.split(",") ?: emptySet() + } + + override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) { + pw.println("Current user: $userId") + pw.println("Added tiles: $autoAdded") + } + + @SysUISingleton + class Builder @Inject constructor( + private val secureSettings: SecureSettings, + private val broadcastDispatcher: BroadcastDispatcher, + private val qsHost: QSHost, + private val dumpManager: DumpManager, + @Main private val handler: Handler, + @Background private val executor: Executor + ) { + private var userId: Int = 0 + + fun setUserId(_userId: Int): Builder { + userId = _userId + return this + } + + fun build(): AutoAddTracker { + return AutoAddTracker( + secureSettings, + broadcastDispatcher, + qsHost, + dumpManager, + handler, + executor, + userId + ) + } + } +}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSHost.java index 000fd1c4bd2e..9f585bdfaeb0 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSHost.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSHost.java @@ -37,6 +37,7 @@ public interface QSHost { void removeCallback(Callback callback); TileServices getTileServices(); void removeTile(String tileSpec); + void removeTiles(Collection<String> specs); void unmarkTileAsAutoAdded(String tileSpec); int indexOf(String tileSpec); diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java index ae3dde10756f..9ceeb754f9b7 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java @@ -348,6 +348,17 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D changeTileSpecs(tileSpecs-> tileSpecs.remove(spec)); } + /** + * Remove many tiles at once. + * + * It will only save to settings once (as opposed to {@link QSTileHost#removeTile} called + * multiple times). + */ + @Override + public void removeTiles(Collection<String> specs) { + changeTileSpecs(tileSpecs -> tileSpecs.removeAll(specs)); + } + @Override public void unmarkTileAsAutoAdded(String spec) { if (mAutoTiles != null) mAutoTiles.unmarkTileAsAutoAdded(spec); @@ -369,6 +380,7 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D * @param requestPosition -1 for end, 0 for beginning, or X for insertion at position X */ public void addTile(String spec, int requestPosition) { + if (spec.equals("work")) Log.wtfStack(TAG, "Adding work tile"); changeTileSpecs(tileSpecs -> { if (tileSpecs.contains(spec)) return false; @@ -383,6 +395,7 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D } void saveTilesToSettings(List<String> tileSpecs) { + if (tileSpecs.contains("work")) Log.wtfStack(TAG, "Saving work tile"); mSecureSettings.putStringForUser(TILES_SETTING, TextUtils.join(",", tileSpecs), null /* tag */, false /* default */, mCurrentUser, true /* overrideable by restore */); diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java index 9e6a8b86ebba..a85800bfa76e 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java @@ -42,6 +42,7 @@ import com.android.systemui.statusbar.phone.StatusBarIconController.TintedIconMa import com.android.systemui.statusbar.phone.StatusBarWindowView; import com.android.systemui.statusbar.phone.StatusIconContainer; import com.android.systemui.statusbar.policy.Clock; +import com.android.systemui.statusbar.policy.VariableDateView; import java.util.List; @@ -61,11 +62,15 @@ public class QuickStatusBarHeader extends FrameLayout { protected QuickQSPanel mHeaderQsPanel; private View mDatePrivacyView; + private View mDateView; + // DateView next to clock. Visible on QQS + private VariableDateView mClockDateView; private View mSecurityHeaderView; private View mStatusIconsView; private View mContainer; private View mQSCarriers; + private ViewGroup mClockContainer; private Clock mClockView; private Space mDatePrivacySeparator; private View mClockIconsSeparator; @@ -120,12 +125,15 @@ public class QuickStatusBarHeader extends FrameLayout { mContainer = findViewById(R.id.qs_container); mIconContainer = findViewById(R.id.statusIcons); mPrivacyChip = findViewById(R.id.privacy_chip); + mDateView = findViewById(R.id.date); + mClockDateView = findViewById(R.id.date_clock); mSecurityHeaderView = findViewById(R.id.header_text_container); mClockIconsSeparator = findViewById(R.id.separator); mRightLayout = findViewById(R.id.rightLayout); mDateContainer = findViewById(R.id.date_container); mPrivacyContainer = findViewById(R.id.privacy_container); + mClockContainer = findViewById(R.id.clock_container); mClockView = findViewById(R.id.clock); mDatePrivacySeparator = findViewById(R.id.space); // Tint for the battery icons are handled in setupHost() @@ -172,7 +180,7 @@ public class QuickStatusBarHeader extends FrameLayout { super.onMeasure(widthMeasureSpec, heightMeasureSpec); if (mDatePrivacyView.getMeasuredHeight() != mTopViewMeasureHeight) { mTopViewMeasureHeight = mDatePrivacyView.getMeasuredHeight(); - updateAnimators(); + post(this::updateAnimators); } } @@ -280,7 +288,8 @@ public class QuickStatusBarHeader extends FrameLayout { TouchAnimator.Builder builder = new TouchAnimator.Builder() .addFloat(mSecurityHeaderView, "alpha", 0, 1) // These views appear on expanding down - .addFloat(mClockView, "alpha", 0, 1) + .addFloat(mDateView, "alpha", 0, 0, 1) + .addFloat(mClockDateView, "alpha", 1, 0, 0) .addFloat(mQSCarriers, "alpha", 0, 1) .setListener(new TouchAnimator.ListenerAdapter() { @Override @@ -289,10 +298,14 @@ public class QuickStatusBarHeader extends FrameLayout { if (!mIsSingleCarrier) { mIconContainer.addIgnoredSlots(mRssiIgnoredSlots); } + // Make it gone so there's enough room for carrier names + mClockDateView.setVisibility(View.GONE); } @Override public void onAnimationStarted() { + mClockDateView.setVisibility(View.VISIBLE); + mClockDateView.setFreezeSwitching(true); setSeparatorVisibility(false); if (!mIsSingleCarrier) { mIconContainer.addIgnoredSlots(mRssiIgnoredSlots); @@ -302,6 +315,7 @@ public class QuickStatusBarHeader extends FrameLayout { @Override public void onAnimationAtStart() { super.onAnimationAtStart(); + mClockDateView.setFreezeSwitching(false); setSeparatorVisibility(mShowClockIconsSeparator); // In QQS we never ignore RSSI. mIconContainer.removeIgnoredSlots(mRssiIgnoredSlots); @@ -434,10 +448,11 @@ public class QuickStatusBarHeader extends FrameLayout { mClockIconsSeparator.setVisibility(visible ? View.VISIBLE : View.GONE); mQSCarriers.setVisibility(visible ? View.GONE : View.VISIBLE); - LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) mClockView.getLayoutParams(); + LinearLayout.LayoutParams lp = + (LinearLayout.LayoutParams) mClockContainer.getLayoutParams(); lp.width = visible ? 0 : WRAP_CONTENT; lp.weight = visible ? 1f : 0f; - mClockView.setLayoutParams(lp); + mClockContainer.setLayoutParams(lp); lp = (LinearLayout.LayoutParams) mRightLayout.getLayoutParams(); lp.width = visible ? 0 : WRAP_CONTENT; diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java index df601003f9da..38428c53fead 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java @@ -42,6 +42,7 @@ import com.android.systemui.qs.dagger.QSScope; import com.android.systemui.statusbar.phone.StatusBarIconController; import com.android.systemui.statusbar.phone.StatusIconContainer; import com.android.systemui.statusbar.policy.Clock; +import com.android.systemui.statusbar.policy.VariableDateViewController; import com.android.systemui.util.ViewController; import java.util.List; @@ -73,6 +74,9 @@ class QuickStatusBarHeaderController extends ViewController<QuickStatusBarHeader private final BatteryMeterViewController mBatteryMeterViewController; private final FeatureFlags mFeatureFlags; + private final VariableDateViewController mVariableDateViewControllerDateView; + private final VariableDateViewController mVariableDateViewControllerClockDateView; + private boolean mListening; private boolean mMicCameraIndicatorsEnabled; private boolean mLocationIndicatorsEnabled; @@ -137,7 +141,8 @@ class QuickStatusBarHeaderController extends ViewController<QuickStatusBarHeader PrivacyDialogController privacyDialogController, QSExpansionPathInterpolator qsExpansionPathInterpolator, BatteryMeterViewController batteryMeterViewController, - FeatureFlags featureFlags) { + FeatureFlags featureFlags, + VariableDateViewController.Factory variableDateViewControllerFactory) { super(view); mPrivacyItemController = privacyItemController; mActivityStarter = activityStarter; @@ -158,6 +163,12 @@ class QuickStatusBarHeaderController extends ViewController<QuickStatusBarHeader mPrivacyChip = mView.findViewById(R.id.privacy_chip); mClockView = mView.findViewById(R.id.clock); mIconContainer = mView.findViewById(R.id.statusIcons); + mVariableDateViewControllerDateView = variableDateViewControllerFactory.create( + mView.requireViewById(R.id.date) + ); + mVariableDateViewControllerClockDateView = variableDateViewControllerFactory.create( + mView.requireViewById(R.id.date_clock) + ); mIconManager = new StatusBarIconController.TintedIconManager(mIconContainer, featureFlags); mDemoModeReceiver = new ClockDemoModeReceiver(mClockView); @@ -190,6 +201,8 @@ class QuickStatusBarHeaderController extends ViewController<QuickStatusBarHeader // Ignore privacy icons because they show in the space above QQS updatePrivacyIconSlots(); + mIconContainer.addIgnoredSlot( + getResources().getString(com.android.internal.R.string.status_bar_managed_profile)); mIconContainer.setShouldRestrictIcons(false); mStatusBarIconController.addIconGroup(mIconManager); @@ -215,6 +228,9 @@ class QuickStatusBarHeaderController extends ViewController<QuickStatusBarHeader mView.onAttach(mIconManager, mQSExpansionPathInterpolator, rssiIgnoredSlots); mDemoModeController.addCallback(mDemoModeReceiver); + + mVariableDateViewControllerDateView.init(); + mVariableDateViewControllerClockDateView.init(); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java index 4b13015361cc..04f089d31664 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java @@ -150,11 +150,7 @@ public class CastTile extends QSTileImpl<BooleanState> { } List<CastDevice> activeDevices = getActiveDevices(); - // We want to pop up the media route selection dialog if we either have no active devices - // (neither routes nor projection), or if we have an active route. In other cases, we assume - // that a projection is active. This is messy, but this tile never correctly handled the - // case where multiple devices were active :-/. - if (activeDevices.isEmpty() || (activeDevices.get(0).tag instanceof RouteInfo)) { + if (willPopDetail()) { mActivityStarter.postQSRunnableDismissingKeyguard(() -> { showDetail(true); }); @@ -163,6 +159,15 @@ public class CastTile extends QSTileImpl<BooleanState> { } } + // We want to pop up the media route selection dialog if we either have no active devices + // (neither routes nor projection), or if we have an active route. In other cases, we assume + // that a projection is active. This is messy, but this tile never correctly handled the + // case where multiple devices were active :-/. + private boolean willPopDetail() { + List<CastDevice> activeDevices = getActiveDevices(); + return activeDevices.isEmpty() || (activeDevices.get(0).tag instanceof RouteInfo); + } + private List<CastDevice> getActiveDevices() { ArrayList<CastDevice> activeDevices = new ArrayList<>(); for (CastDevice device : mController.getCastDevices()) { @@ -234,10 +239,12 @@ public class CastTile extends QSTileImpl<BooleanState> { state.contentDescription = state.contentDescription + "," + mContext.getString(R.string.accessibility_quick_settings_open_details); state.expandedAccessibilityClassName = Button.class.getName(); + state.forceExpandIcon = willPopDetail(); } else { state.state = Tile.STATE_UNAVAILABLE; String noWifi = mContext.getString(R.string.quick_settings_cast_no_wifi); state.secondaryLabel = noWifi; + state.forceExpandIcon = false; } state.stateDescription = state.stateDescription + ", " + state.secondaryLabel; mDetailAdapter.updateItems(devices); diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java index 7cb1421e3f0f..41a3fb0211a7 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java @@ -429,7 +429,7 @@ public class InternetTile extends QSTileImpl<SignalState> { state.icon = ResourceIcon.get(cb.mWifiSignalIconId); } } else if (cb.mNoDefaultNetwork) { - if (cb.mNoNetworksAvailable) { + if (cb.mNoNetworksAvailable || !cb.mEnabled) { state.icon = ResourceIcon.get(R.drawable.ic_qs_no_internet_unavailable); state.secondaryLabel = r.getString(R.string.quick_settings_networks_unavailable); } else { @@ -489,7 +489,7 @@ public class InternetTile extends QSTileImpl<SignalState> { state.icon = ResourceIcon.get(R.drawable.ic_qs_no_internet_unavailable); state.secondaryLabel = r.getString(R.string.status_bar_airplane); } else if (cb.mNoDefaultNetwork) { - if (cb.mNoNetworksAvailable) { + if (cb.mNoNetworksAvailable || !mSignalCallback.mWifiInfo.mEnabled) { state.icon = ResourceIcon.get(R.drawable.ic_qs_no_internet_unavailable); state.secondaryLabel = r.getString(R.string.quick_settings_networks_unavailable); } else { diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java index 82b6c0c1805d..d9919bdac889 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java @@ -131,22 +131,16 @@ public class QuickAccessWalletTile extends QSTileImpl<QSTile.State> { Intent intent = new Intent(mContext, WalletActivity.class) .setAction(Intent.ACTION_VIEW) .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK); - if (mKeyguardStateController.isUnlocked()) { - mActivityStarter.startActivity(intent, true /* dismissShade */, - animationController); - } else { - mHost.collapsePanels(); - // Do not use ActivityStarter here because the WalletActivity is required to be - // started without prompting keyguard when the device is locked. - mContext.startActivity(intent); - } + mActivityStarter.startActivity(intent, true /* dismissShade */, + animationController, true /* showOverLockscreenWhenLocked */); } else { - if (mController.getWalletClient().createWalletIntent() == null) { + Intent intent = mController.getWalletClient().createWalletIntent(); + if (intent == null) { Log.w(TAG, "Could not get intent of the wallet app."); return; } mActivityStarter.postStartActivityDismissingKeyguard( - mController.getWalletClient().createWalletIntent(), + intent, /* delay= */ 0, animationController); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java new file mode 100644 index 000000000000..24c2fb1eb3f9 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java @@ -0,0 +1,851 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.tiles.dialog; + +import static com.android.settingslib.mobile.MobileMappings.getIconKey; +import static com.android.settingslib.mobile.MobileMappings.mapIconSets; + +import android.annotation.ColorInt; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.graphics.Color; +import android.graphics.drawable.ColorDrawable; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.LayerDrawable; +import android.net.ConnectivityManager; +import android.net.Network; +import android.net.NetworkCapabilities; +import android.net.NetworkRequest; +import android.net.wifi.ScanResult; +import android.net.wifi.WifiManager; +import android.os.Handler; +import android.provider.Settings; +import android.telephony.ServiceState; +import android.telephony.SignalStrength; +import android.telephony.SubscriptionInfo; +import android.telephony.SubscriptionManager; +import android.telephony.TelephonyCallback; +import android.telephony.TelephonyDisplayInfo; +import android.telephony.TelephonyManager; +import android.text.TextUtils; +import android.util.Log; +import android.view.Gravity; +import android.widget.Toast; + +import com.android.internal.logging.UiEventLogger; +import com.android.keyguard.KeyguardUpdateMonitor; +import com.android.keyguard.KeyguardUpdateMonitorCallback; +import com.android.settingslib.DeviceInfoUtils; +import com.android.settingslib.Utils; +import com.android.settingslib.graph.SignalDrawable; +import com.android.settingslib.mobile.MobileMappings; +import com.android.settingslib.net.SignalStrengthUtil; +import com.android.systemui.R; +import com.android.systemui.broadcast.BroadcastDispatcher; +import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.plugins.ActivityStarter; +import com.android.systemui.statusbar.policy.NetworkController; +import com.android.systemui.statusbar.policy.NetworkController.AccessPointController; +import com.android.systemui.util.settings.GlobalSettings; +import com.android.wifitrackerlib.MergedCarrierEntry; +import com.android.wifitrackerlib.WifiEntry; + +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.Executor; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Supplier; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import javax.inject.Inject; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.VisibleForTesting; + +public class InternetDialogController implements WifiEntry.DisconnectCallback, + NetworkController.AccessPointController.AccessPointCallback { + + private static final String TAG = "InternetDialogController"; + private static final String ACTION_NETWORK_PROVIDER_SETTINGS = + "android.settings.NETWORK_PROVIDER_SETTINGS"; + private static final String EXTRA_CHOSEN_WIFI_ENTRY_KEY = "key_chosen_wifientry_key"; + public static final Drawable EMPTY_DRAWABLE = new ColorDrawable(Color.TRANSPARENT); + public static final int NO_CELL_DATA_TYPE_ICON = 0; + private static final int SUBTITLE_TEXT_WIFI_IS_OFF = R.string.wifi_is_off; + private static final int SUBTITLE_TEXT_TAP_A_NETWORK_TO_CONNECT = + R.string.tap_a_network_to_connect; + private static final int SUBTITLE_TEXT_SEARCHING_FOR_NETWORKS = + R.string.wifi_empty_list_wifi_on; + private static final int SUBTITLE_TEXT_NON_CARRIER_NETWORK_UNAVAILABLE = + R.string.non_carrier_network_unavailable; + private static final int SUBTITLE_TEXT_ALL_CARRIER_NETWORK_UNAVAILABLE = + R.string.all_network_unavailable; + private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); + + private WifiManager mWifiManager; + private Context mContext; + private ActivityStarter mActivityStarter; + private SubscriptionManager mSubscriptionManager; + private TelephonyManager mTelephonyManager; + private ConnectivityManager mConnectivityManager; + private TelephonyDisplayInfo mTelephonyDisplayInfo = + new TelephonyDisplayInfo(TelephonyManager.NETWORK_TYPE_UNKNOWN, + TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE); + private Handler mHandler; + private MobileMappings.Config mConfig = null; + private Executor mExecutor; + private AccessPointController mAccessPointController; + private IntentFilter mWifiStateFilter; + private InternetDialogCallback mCallback; + private List<WifiEntry> mWifiEntry; + private WifiEntry mConnectedEntry; + private UiEventLogger mUiEventLogger; + private BroadcastDispatcher mBroadcastDispatcher; + private KeyguardUpdateMonitor mKeyguardUpdateMonitor; + private GlobalSettings mGlobalSettings; + private int mDefaultDataSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; + + @VisibleForTesting + protected SubscriptionManager.OnSubscriptionsChangedListener mOnSubscriptionsChangedListener; + @VisibleForTesting + protected InternetTelephonyCallback mInternetTelephonyCallback; + + private final KeyguardUpdateMonitorCallback mKeyguardUpdateCallback = + new KeyguardUpdateMonitorCallback() { + @Override + public void onRefreshCarrierInfo() { + mCallback.onRefreshCarrierInfo(); + } + + @Override + public void onSimStateChanged(int subId, int slotId, int simState) { + mCallback.onSimStateChanged(); + } + }; + + protected List<SubscriptionInfo> getSubscriptionInfo() { + return mKeyguardUpdateMonitor.getFilteredSubscriptionInfo(false); + } + + @Inject + public InternetDialogController(@NonNull Context context, UiEventLogger uiEventLogger, + ActivityStarter starter, AccessPointController accessPointController, + SubscriptionManager subscriptionManager, TelephonyManager telephonyManager, + @Nullable WifiManager wifiManager, ConnectivityManager connectivityManager, + @Main Handler handler, @Main Executor mainExecutor, + BroadcastDispatcher broadcastDispatcher, KeyguardUpdateMonitor keyguardUpdateMonitor, + GlobalSettings globalSettings) { + if (DEBUG) { + Log.d(TAG, "Init InternetDialogController"); + } + mHandler = handler; + mExecutor = mainExecutor; + mContext = context; + mGlobalSettings = globalSettings; + mWifiManager = wifiManager; + mTelephonyManager = telephonyManager; + mConnectivityManager = connectivityManager; + mSubscriptionManager = subscriptionManager; + mBroadcastDispatcher = broadcastDispatcher; + mKeyguardUpdateMonitor = keyguardUpdateMonitor; + mWifiStateFilter = new IntentFilter(WifiManager.NETWORK_STATE_CHANGED_ACTION); + mWifiStateFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION); + mUiEventLogger = uiEventLogger; + mActivityStarter = starter; + mAccessPointController = accessPointController; + mConfig = MobileMappings.Config.readConfig(mContext); + } + + void onStart(@NonNull InternetDialogCallback callback) { + if (DEBUG) { + Log.d(TAG, "onStart"); + } + mCallback = callback; + mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateCallback); + mAccessPointController.addAccessPointCallback(this); + mBroadcastDispatcher.registerReceiver(mWifiStateReceiver, mWifiStateFilter, mExecutor); + // Listen the subscription changes + mOnSubscriptionsChangedListener = new InternetOnSubscriptionChangedListener(); + mSubscriptionManager.addOnSubscriptionsChangedListener(mExecutor, + mOnSubscriptionsChangedListener); + mDefaultDataSubId = getDefaultDataSubscriptionId(); + mTelephonyManager = mTelephonyManager.createForSubscriptionId(mDefaultDataSubId); + mInternetTelephonyCallback = new InternetTelephonyCallback(); + mTelephonyManager.registerTelephonyCallback(mExecutor, mInternetTelephonyCallback); + // Listen the connectivity changes + mConnectivityManager.registerNetworkCallback(new NetworkRequest.Builder() + .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) + .build(), new DataConnectivityListener(), mHandler); + scanWifiAccessPoints(); + } + + void onStop() { + if (DEBUG) { + Log.d(TAG, "onStop"); + } + mBroadcastDispatcher.unregisterReceiver(mWifiStateReceiver); + mTelephonyManager.unregisterTelephonyCallback(mInternetTelephonyCallback); + mSubscriptionManager.removeOnSubscriptionsChangedListener( + mOnSubscriptionsChangedListener); + mAccessPointController.removeAccessPointCallback(this); + mKeyguardUpdateMonitor.removeCallback(mKeyguardUpdateCallback); + } + + @VisibleForTesting + boolean isAirplaneModeEnabled() { + return mGlobalSettings.getInt(Settings.Global.AIRPLANE_MODE_ON, 0) != 0; + } + + @VisibleForTesting + protected int getDefaultDataSubscriptionId() { + return mSubscriptionManager.getDefaultDataSubscriptionId(); + } + + @VisibleForTesting + protected Intent getSettingsIntent() { + return new Intent(ACTION_NETWORK_PROVIDER_SETTINGS).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + } + + CharSequence getDialogTitleText() { + if (isAirplaneModeEnabled()) { + return mContext.getText(R.string.airplane_mode); + } + return mContext.getText(R.string.quick_settings_internet_label); + } + + CharSequence getSubtitleText(boolean isProgressBarVisible) { + if (isAirplaneModeEnabled()) { + return null; + } + + if (!mWifiManager.isWifiEnabled()) { + // When the airplane mode is off and Wi-Fi is disabled. + // Sub-Title: Wi-Fi is off + if (DEBUG) { + Log.d(TAG, "Airplane mode off + Wi-Fi off."); + } + return mContext.getText(SUBTITLE_TEXT_WIFI_IS_OFF); + } + + if (isProgressBarVisible) { + // When the Wi-Fi scan result callback is received + // Sub-Title: Searching for networks... + return mContext.getText(SUBTITLE_TEXT_SEARCHING_FOR_NETWORKS); + } + + final List<ScanResult> wifiList = mWifiManager.getScanResults(); + if (wifiList != null && wifiList.size() != 0) { + return mContext.getText(SUBTITLE_TEXT_TAP_A_NETWORK_TO_CONNECT); + } + + // Sub-Title: + // show non_carrier_network_unavailable + // - while Wi-Fi on + no Wi-Fi item + // - while Wi-Fi on + no Wi-Fi item + mobile data off + // show all_network_unavailable: + // - while Wi-Fi on + no Wi-Fi item + no carrier item + // - while Wi-Fi on + no Wi-Fi item + service is out of service + // - while Wi-Fi on + no Wi-Fi item + mobile data on + no carrier data. + if (DEBUG) { + Log.d(TAG, "No Wi-Fi item."); + } + if (!hasCarrier() || (!isVoiceStateInService() && !isDataStateInService())) { + if (DEBUG) { + Log.d(TAG, "No carrier or service is out of service."); + } + return mContext.getText(SUBTITLE_TEXT_ALL_CARRIER_NETWORK_UNAVAILABLE); + } + + if (!isMobileDataEnabled()) { + if (DEBUG) { + Log.d(TAG, "Mobile data off"); + } + return mContext.getText(SUBTITLE_TEXT_NON_CARRIER_NETWORK_UNAVAILABLE); + } + + if (!activeNetworkIsCellular()) { + if (DEBUG) { + Log.d(TAG, "No carrier data."); + } + return mContext.getText(SUBTITLE_TEXT_ALL_CARRIER_NETWORK_UNAVAILABLE); + } + + return mContext.getText(SUBTITLE_TEXT_NON_CARRIER_NETWORK_UNAVAILABLE); + } + + Drawable getWifiConnectedDrawable(WifiEntry wifiEntry) throws Throwable { + final @ColorInt int tint; + tint = Utils.getColorAttrDefaultColor(mContext, + com.android.internal.R.attr.colorAccentPrimaryVariant); + final Drawable drawable = mContext.getDrawable( + com.android.settingslib.Utils.getWifiIconResource(wifiEntry.getLevel())); + drawable.setTint(tint); + + return drawable; + } + + Drawable getSignalStrengthDrawable() { + Drawable drawable = mContext.getDrawable( + R.drawable.ic_signal_strength_zero_bar_no_internet); + try { + if (mTelephonyManager == null) { + if (DEBUG) { + Log.d(TAG, "TelephonyManager is null"); + } + return drawable; + } + + if (isDataStateInService() || isVoiceStateInService()) { + AtomicReference<Drawable> shared = new AtomicReference<>(); + shared.set(getSignalStrengthDrawableWithLevel()); + drawable = shared.get(); + } + + drawable.setTint( + Utils.getColorAttrDefaultColor(mContext, android.R.attr.colorControlNormal)); + if (activeNetworkIsCellular()) { + drawable.setTint(Utils.getColorAttrDefaultColor(mContext, + com.android.internal.R.attr.colorAccentPrimaryVariant)); + } + + } catch (Throwable e) { + e.printStackTrace(); + } + return drawable; + } + + /** + * To get the signal bar icon with level. + * + * @return The Drawable which is a signal bar icon with level. + */ + Drawable getSignalStrengthDrawableWithLevel() { + final SignalStrength strength = mTelephonyManager.getSignalStrength(); + int level = (strength == null) ? 0 : strength.getLevel(); + int numLevels = SignalStrength.NUM_SIGNAL_STRENGTH_BINS; + if (mSubscriptionManager != null && shouldInflateSignalStrength(mDefaultDataSubId)) { + level += 1; + numLevels += 1; + } + return getSignalStrengthIcon(mContext, level, numLevels, NO_CELL_DATA_TYPE_ICON, false); + } + + Drawable getSignalStrengthIcon(Context context, int level, int numLevels, + int iconType, boolean cutOut) { + Log.d(TAG, "getSignalStrengthIcon"); + final SignalDrawable signalDrawable = new SignalDrawable(context); + signalDrawable.setLevel( + SignalDrawable.getState(level, numLevels, cutOut)); + + // Make the network type drawable + final Drawable networkDrawable = + iconType == NO_CELL_DATA_TYPE_ICON + ? EMPTY_DRAWABLE + : context.getResources().getDrawable(iconType, context.getTheme()); + + // Overlay the two drawables + final Drawable[] layers = {networkDrawable, signalDrawable}; + final int iconSize = + context.getResources().getDimensionPixelSize(R.dimen.signal_strength_icon_size); + + final LayerDrawable icons = new LayerDrawable(layers); + // Set the network type icon at the top left + icons.setLayerGravity(0 /* index of networkDrawable */, Gravity.TOP | Gravity.LEFT); + // Set the signal strength icon at the bottom right + icons.setLayerGravity(1 /* index of SignalDrawable */, Gravity.BOTTOM | Gravity.RIGHT); + icons.setLayerSize(1 /* index of SignalDrawable */, iconSize, iconSize); + icons.setTintList(Utils.getColorAttr(context, android.R.attr.colorControlNormal)); + return icons; + } + + private boolean shouldInflateSignalStrength(int subId) { + return SignalStrengthUtil.shouldInflateSignalStrength(mContext, subId); + } + + private CharSequence getUniqueSubscriptionDisplayName(int subscriptionId, Context context) { + final Map<Integer, CharSequence> displayNames = getUniqueSubscriptionDisplayNames(context); + return displayNames.getOrDefault(subscriptionId, ""); + } + + private Map<Integer, CharSequence> getUniqueSubscriptionDisplayNames(Context context) { + class DisplayInfo { + public SubscriptionInfo subscriptionInfo; + public CharSequence originalName; + public CharSequence uniqueName; + } + + // Map of SubscriptionId to DisplayName + final Supplier<Stream<DisplayInfo>> originalInfos = + () -> getSubscriptionInfo() + .stream() + .filter(i -> { + // Filter out null values. + return (i != null && i.getDisplayName() != null); + }) + .map(i -> { + DisplayInfo info = new DisplayInfo(); + info.subscriptionInfo = i; + info.originalName = i.getDisplayName().toString().trim(); + return info; + }); + + // A Unique set of display names + Set<CharSequence> uniqueNames = new HashSet<>(); + // Return the set of duplicate names + final Set<CharSequence> duplicateOriginalNames = originalInfos.get() + .filter(info -> !uniqueNames.add(info.originalName)) + .map(info -> info.originalName) + .collect(Collectors.toSet()); + + // If a display name is duplicate, append the final 4 digits of the phone number. + // Creates a mapping of Subscription id to original display name + phone number display name + final Supplier<Stream<DisplayInfo>> uniqueInfos = () -> originalInfos.get().map(info -> { + if (duplicateOriginalNames.contains(info.originalName)) { + // This may return null, if the user cannot view the phone number itself. + final String phoneNumber = DeviceInfoUtils.getBidiFormattedPhoneNumber(context, + info.subscriptionInfo); + String lastFourDigits = ""; + if (phoneNumber != null) { + lastFourDigits = (phoneNumber.length() > 4) + ? phoneNumber.substring(phoneNumber.length() - 4) : phoneNumber; + } + + if (TextUtils.isEmpty(lastFourDigits)) { + info.uniqueName = info.originalName; + } else { + info.uniqueName = info.originalName + " " + lastFourDigits; + } + + } else { + info.uniqueName = info.originalName; + } + return info; + }); + + // Check uniqueness a second time. + // We might not have had permission to view the phone numbers. + // There might also be multiple phone numbers whose last 4 digits the same. + uniqueNames.clear(); + final Set<CharSequence> duplicatePhoneNames = uniqueInfos.get() + .filter(info -> !uniqueNames.add(info.uniqueName)) + .map(info -> info.uniqueName) + .collect(Collectors.toSet()); + + return uniqueInfos.get().map(info -> { + if (duplicatePhoneNames.contains(info.uniqueName)) { + info.uniqueName = info.originalName + " " + + info.subscriptionInfo.getSubscriptionId(); + } + return info; + }).collect(Collectors.toMap( + info -> info.subscriptionInfo.getSubscriptionId(), + info -> info.uniqueName)); + } + + CharSequence getMobileNetworkTitle() { + return getUniqueSubscriptionDisplayName(mDefaultDataSubId, mContext); + } + + String getMobileNetworkSummary() { + String description = getNetworkTypeDescription(mContext, mConfig, + mTelephonyDisplayInfo, mDefaultDataSubId); + return getMobileSummary(mContext, mTelephonyManager, description); + } + + /** + * Get currently description of mobile network type. + */ + private String getNetworkTypeDescription(Context context, MobileMappings.Config config, + TelephonyDisplayInfo telephonyDisplayInfo, int subId) { + String iconKey = getIconKey(telephonyDisplayInfo); + + if (mapIconSets(config) == null || mapIconSets(config).get(iconKey) == null) { + if (DEBUG) { + Log.d(TAG, "The description of network type is empty."); + } + return ""; + } + + int resId = mapIconSets(config).get(iconKey).dataContentDescription; + return resId != 0 + ? SubscriptionManager.getResourcesForSubId(context, subId).getString(resId) : ""; + } + + private String getMobileSummary(Context context, TelephonyManager telephonyManager, + String networkTypeDescription) { + if (!isMobileDataEnabled()) { + return context.getString(R.string.mobile_data_off_summary); + } + if (!isDataStateInService()) { + return context.getString(R.string.mobile_data_no_connection); + } + String summary = networkTypeDescription; + if (activeNetworkIsCellular()) { + summary = context.getString(R.string.preference_summary_default_combination, + context.getString(R.string.mobile_data_connection_active), + networkTypeDescription); + } + return summary; + } + + String getConnectedWifiTitle() { + if (getConnectedWifiEntry() == null) { + if (DEBUG) { + Log.d(TAG, "connected entry is null"); + } + return ""; + } + return getConnectedWifiEntry().getTitle(); + } + + String getConnectedWifiSummary() { + if (getConnectedWifiEntry() == null) { + if (DEBUG) { + Log.d(TAG, "connected entry is null"); + } + return ""; + } + return getConnectedWifiEntry().getSummary(false); + } + + void launchNetworkSetting() { + mCallback.dismissDialog(); + mActivityStarter.postStartActivityDismissingKeyguard(getSettingsIntent(), 0); + } + + void connectCarrierNetwork() { + final MergedCarrierEntry mergedCarrierEntry = + mAccessPointController.getMergedCarrierEntry(); + if (mergedCarrierEntry != null && mergedCarrierEntry.canConnect()) { + mergedCarrierEntry.connect(null /* ConnectCallback */); + } + } + + List<WifiEntry> getWifiEntryList() { + return mWifiEntry; + } + + WifiEntry getConnectedWifiEntry() { + return mConnectedEntry; + } + + WifiManager getWifiManager() { + return mWifiManager; + } + + TelephonyManager getTelephonyManager() { + return mTelephonyManager; + } + + SubscriptionManager getSubscriptionManager() { + return mSubscriptionManager; + } + + /** + * @return whether there is the carrier item in the slice. + */ + boolean hasCarrier() { + if (mSubscriptionManager == null) { + if (DEBUG) { + Log.d(TAG, "SubscriptionManager is null, can not check carrier."); + } + return false; + } + + if (isAirplaneModeEnabled() || mTelephonyManager == null + || mSubscriptionManager.getActiveSubscriptionIdList().length <= 0) { + return false; + } + return true; + } + + /** + * Return {@code true} if mobile data is enabled + */ + boolean isMobileDataEnabled() { + if (mTelephonyManager == null || !mTelephonyManager.isDataEnabled()) { + return false; + } + return true; + } + + /** + * Set whether to enable data for {@code subId}, also whether to disable data for other + * subscription + */ + void setMobileDataEnabled(Context context, int subId, boolean enabled, + boolean disableOtherSubscriptions) { + if (mTelephonyManager == null) { + if (DEBUG) { + Log.d(TAG, "TelephonyManager is null, can not set mobile data."); + } + return; + } + + if (mSubscriptionManager == null) { + if (DEBUG) { + Log.d(TAG, "SubscriptionManager is null, can not set mobile data."); + } + return; + } + + mTelephonyManager.setDataEnabled(enabled); + if (disableOtherSubscriptions) { + final List<SubscriptionInfo> subInfoList = + mSubscriptionManager.getActiveSubscriptionInfoList(); + if (subInfoList != null) { + for (SubscriptionInfo subInfo : subInfoList) { + // We never disable mobile data for opportunistic subscriptions. + if (subInfo.getSubscriptionId() != subId && !subInfo.isOpportunistic()) { + context.getSystemService(TelephonyManager.class).createForSubscriptionId( + subInfo.getSubscriptionId()).setDataEnabled(false); + } + } + } + } + } + + boolean isDataStateInService() { + if (mTelephonyManager == null) { + if (DEBUG) { + Log.d(TAG, "TelephonyManager is null, can not detect mobile state."); + } + return false; + } + return mTelephonyManager.getDataState() == TelephonyManager.DATA_CONNECTED; + } + + boolean isVoiceStateInService() { + if (mTelephonyManager == null) { + if (DEBUG) { + Log.d(TAG, "TelephonyManager is null, can not detect voice state."); + } + return false; + } + + final ServiceState serviceState = mTelephonyManager.getServiceState(); + return serviceState != null + && serviceState.getState() == serviceState.STATE_IN_SERVICE; + } + + boolean activeNetworkIsCellular() { + if (mConnectivityManager == null) { + if (DEBUG) { + Log.d(TAG, "ConnectivityManager is null, can not check active network."); + } + return false; + } + + final Network activeNetwork = mConnectivityManager.getActiveNetwork(); + if (activeNetwork == null) { + return false; + } + final NetworkCapabilities networkCapabilities = + mConnectivityManager.getNetworkCapabilities(activeNetwork); + if (networkCapabilities == null) { + return false; + } + return networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR); + } + + boolean connect(WifiEntry ap) { + if (ap == null) { + if (DEBUG) { + Log.d(TAG, "No Wi-Fi ap to connect."); + } + return false; + } + + if (ap.getWifiConfiguration() != null) { + if (DEBUG) { + Log.d(TAG, "connect networkId=" + ap.getWifiConfiguration().networkId); + } + } else { + if (DEBUG) { + Log.d(TAG, "connect to unsaved network " + ap.getTitle()); + } + } + ap.connect(new WifiEntryConnectCallback(mActivityStarter, mContext, ap)); + return false; + } + + static class WifiEntryConnectCallback implements WifiEntry.ConnectCallback { + final ActivityStarter mActivityStarter; + final Context mContext; + final WifiEntry mWifiEntry; + + WifiEntryConnectCallback(ActivityStarter activityStarter, Context context, + WifiEntry connectWifiEntry) { + mActivityStarter = activityStarter; + mContext = context; + mWifiEntry = connectWifiEntry; + } + + @Override + public void onConnectResult(@ConnectStatus int status) { + if (DEBUG) { + Log.d(TAG, "onConnectResult " + status); + } + + if (status == WifiEntry.ConnectCallback.CONNECT_STATUS_FAILURE_NO_CONFIG) { + final Intent intent = new Intent("com.android.settings.WIFI_DIALOG") + .putExtra(EXTRA_CHOSEN_WIFI_ENTRY_KEY, mWifiEntry.getKey()); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + mActivityStarter.startActivity(intent, true); + } else if (status == CONNECT_STATUS_FAILURE_UNKNOWN) { + Toast.makeText(mContext, R.string.wifi_failed_connect_message, + Toast.LENGTH_SHORT).show(); + } else { + if (DEBUG) { + Log.d(TAG, "connect failure reason=" + status); + } + } + } + } + + void scanWifiAccessPoints() { + mAccessPointController.scanForAccessPoints(); + } + + @Override + public void onAccessPointsChanged(List<WifiEntry> accessPoints) { + if (accessPoints == null) { + return; + } + + boolean hasConnectedWifi = false; + mWifiEntry = accessPoints; + for (WifiEntry wifiEntry : accessPoints) { + if (wifiEntry.getConnectedState() == WifiEntry.CONNECTED_STATE_CONNECTED) { + mConnectedEntry = wifiEntry; + hasConnectedWifi = true; + break; + } + } + if (!hasConnectedWifi) { + mConnectedEntry = null; + } + + mCallback.onAccessPointsChanged(mWifiEntry, mConnectedEntry); + } + + @Override + public void onSettingsActivityTriggered(Intent settingsIntent) { + } + + @Override + public void onDisconnectResult(int status) { + } + + private class InternetTelephonyCallback extends TelephonyCallback implements + TelephonyCallback.DataConnectionStateListener, + TelephonyCallback.DisplayInfoListener, + TelephonyCallback.ServiceStateListener, + TelephonyCallback.SignalStrengthsListener { + + @Override + public void onServiceStateChanged(@NonNull ServiceState serviceState) { + mCallback.onServiceStateChanged(serviceState); + } + + @Override + public void onDataConnectionStateChanged(int state, int networkType) { + mCallback.onDataConnectionStateChanged(state, networkType); + } + + @Override + public void onSignalStrengthsChanged(@NonNull SignalStrength signalStrength) { + mCallback.onSignalStrengthsChanged(signalStrength); + } + + @Override + public void onDisplayInfoChanged(@NonNull TelephonyDisplayInfo telephonyDisplayInfo) { + mTelephonyDisplayInfo = telephonyDisplayInfo; + mCallback.onDisplayInfoChanged(telephonyDisplayInfo); + } + } + + private class InternetOnSubscriptionChangedListener + extends SubscriptionManager.OnSubscriptionsChangedListener { + InternetOnSubscriptionChangedListener() { + super(); + } + + @Override + public void onSubscriptionsChanged() { + mDefaultDataSubId = getDefaultDataSubscriptionId(); + if (SubscriptionManager.isUsableSubscriptionId(mDefaultDataSubId)) { + mTelephonyManager.unregisterTelephonyCallback(mInternetTelephonyCallback); + + mTelephonyManager = mTelephonyManager.createForSubscriptionId(mDefaultDataSubId); + mTelephonyManager.registerTelephonyCallback(mHandler::post, + mInternetTelephonyCallback); + mCallback.onSubscriptionsChanged(mDefaultDataSubId); + } + } + } + + private class DataConnectivityListener extends ConnectivityManager.NetworkCallback { + @Override + public void onCapabilitiesChanged(@NonNull Network network, + @NonNull NetworkCapabilities networkCapabilities) { + final Network activeNetwork = mConnectivityManager.getActiveNetwork(); + if (activeNetwork != null && activeNetwork.equals(network)) { + // update UI + mCallback.onCapabilitiesChanged(network, networkCapabilities); + } + } + } + + private final BroadcastReceiver mWifiStateReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + mCallback.onWifiStateReceived(context, intent); + } + }; + + interface InternetDialogCallback { + + void onRefreshCarrierInfo(); + + void onSimStateChanged(); + + void onCapabilitiesChanged(Network network, NetworkCapabilities networkCapabilities); + + void onSubscriptionsChanged(int defaultDataSubId); + + void onServiceStateChanged(ServiceState serviceState); + + void onDataConnectionStateChanged(int state, int networkType); + + void onSignalStrengthsChanged(SignalStrength signalStrength); + + void onDisplayInfoChanged(TelephonyDisplayInfo telephonyDisplayInfo); + + void dismissDialog(); + + void onAccessPointsChanged(List<WifiEntry> wifiEntryList, WifiEntry connectedEntry); + + void onWifiStateReceived(Context context, Intent intent); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogUtil.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogUtil.java new file mode 100644 index 000000000000..6aaba997faad --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogUtil.java @@ -0,0 +1,14 @@ +package com.android.systemui.qs.tiles.dialog; + +import android.content.Context; +import android.util.FeatureFlagUtils; + +public class InternetDialogUtil { + + public static boolean isProviderModelEnabled(Context context) { + if (context == null) { + return false; + } + return FeatureFlagUtils.isEnabled(context, FeatureFlagUtils.SETTINGS_PROVIDER_MODEL); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenInternalAudioRecorder.java b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenInternalAudioRecorder.java index 0aa9d4d662a5..5a6f2a2a912d 100644 --- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenInternalAudioRecorder.java +++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenInternalAudioRecorder.java @@ -27,6 +27,7 @@ import android.media.MediaMuxer; import android.media.MediaRecorder; import android.media.projection.MediaProjection; import android.util.Log; +import android.util.MathUtils; import java.io.IOException; import java.nio.ByteBuffer; @@ -128,30 +129,64 @@ public class ScreenInternalAudioRecorder { mThread = new Thread(() -> { short[] bufferInternal = null; short[] bufferMic = null; - byte[] buffer = null; + byte[] buffer = new byte[size]; if (mMic) { bufferInternal = new short[size / 2]; bufferMic = new short[size / 2]; - } else { - buffer = new byte[size]; } + int readBytes = 0; + int readShortsInternal = 0; + int offsetShortsInternal = 0; + int readShortsMic = 0; + int offsetShortsMic = 0; while (true) { - int readBytes = 0; - int readShortsInternal = 0; - int readShortsMic = 0; if (mMic) { - readShortsInternal = mAudioRecord.read(bufferInternal, 0, - bufferInternal.length); - readShortsMic = mAudioRecordMic.read(bufferMic, 0, bufferMic.length); + readShortsInternal = mAudioRecord.read(bufferInternal, offsetShortsInternal, + bufferInternal.length - offsetShortsInternal); + readShortsMic = mAudioRecordMic.read( + bufferMic, offsetShortsMic, bufferMic.length - offsetShortsMic); + + // if both error, end the recording + if (readShortsInternal < 0 && readShortsMic < 0) { + break; + } + + // if one has an errors, fill its buffer with zeros and assume it is mute + // with the same size as the other buffer + if (readShortsInternal < 0) { + readShortsInternal = readShortsMic; + offsetShortsInternal = offsetShortsMic; + java.util.Arrays.fill(bufferInternal, (short) 0); + } + + if (readShortsMic < 0) { + readShortsMic = readShortsInternal; + offsetShortsMic = offsetShortsInternal; + java.util.Arrays.fill(bufferMic, (short) 0); + } + + // Add offset (previous unmixed values) to the buffer + readShortsInternal += offsetShortsInternal; + readShortsMic += offsetShortsMic; + + int minShorts = Math.min(readShortsInternal, readShortsMic); + readBytes = minShorts * 2; // modify the volume - bufferMic = scaleValues(bufferMic, - readShortsMic, MIC_VOLUME_SCALE); - readBytes = Math.min(readShortsInternal, readShortsMic) * 2; - buffer = addAndConvertBuffers(bufferInternal, readShortsInternal, bufferMic, - readShortsMic); + // scale only mixed shorts + scaleValues(bufferMic, minShorts, MIC_VOLUME_SCALE); + // Mix the two buffers + addAndConvertBuffers(bufferInternal, bufferMic, buffer, minShorts); + + // shift unmixed shorts to the beginning of the buffer + shiftToStart(bufferInternal, minShorts, offsetShortsInternal); + shiftToStart(bufferMic, minShorts, offsetShortsMic); + + // reset the offset for the next loop + offsetShortsInternal = readShortsInternal - minShorts; + offsetShortsMic = readShortsMic - minShorts; } else { readBytes = mAudioRecord.read(buffer, 0, buffer.length); } @@ -169,40 +204,31 @@ public class ScreenInternalAudioRecorder { }); } - private short[] scaleValues(short[] buff, int len, float scale) { + /** + * moves all bits from start to end to the beginning of the array + */ + private void shiftToStart(short[] target, int start, int end) { + for (int i = 0; i < end - start; i++) { + target[i] = target[start + i]; + } + } + + private void scaleValues(short[] buff, int len, float scale) { for (int i = 0; i < len; i++) { - int oldValue = buff[i]; int newValue = (int) (buff[i] * scale); - if (newValue > Short.MAX_VALUE) { - newValue = Short.MAX_VALUE; - } else if (newValue < Short.MIN_VALUE) { - newValue = Short.MIN_VALUE; - } - buff[i] = (short) (newValue); + buff[i] = (short) MathUtils.constrain(newValue, Short.MIN_VALUE, Short.MAX_VALUE); } - return buff; } - private byte[] addAndConvertBuffers(short[] a1, int a1Limit, short[] a2, int a2Limit) { - int size = Math.max(a1Limit, a2Limit); - if (size < 0) return new byte[0]; - byte[] buff = new byte[size * 2]; - for (int i = 0; i < size; i++) { - int sum; - if (i > a1Limit) { - sum = a2[i]; - } else if (i > a2Limit) { - sum = a1[i]; - } else { - sum = (int) a1[i] + (int) a2[i]; - } - if (sum > Short.MAX_VALUE) sum = Short.MAX_VALUE; - if (sum < Short.MIN_VALUE) sum = Short.MIN_VALUE; + private void addAndConvertBuffers(short[] src1, short[] src2, byte[] dst, int sizeShorts) { + for (int i = 0; i < sizeShorts; i++) { + int sum; + sum = (short) MathUtils.constrain( + (int) src1[i] + (int) src2[i], Short.MIN_VALUE, Short.MAX_VALUE); int byteIndex = i * 2; - buff[byteIndex] = (byte) (sum & 0xff); - buff[byteIndex + 1] = (byte) ((sum >> 8) & 0xff); + dst[byteIndex] = (byte) (sum & 0xff); + dst[byteIndex + 1] = (byte) ((sum >> 8) & 0xff); } - return buff; } private void encode(byte[] buffer, int readBytes) { diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java b/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java index 0eaef72ae29b..31d51f1d1a60 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java @@ -154,6 +154,7 @@ public class LongScreenshotActivity extends Activity { @Override public void onStart() { super.onStart(); + mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_LONG_SCREENSHOT_ACTIVITY_STARTED); if (mPreview.getDrawable() != null) { // We already have an image, so no need to try to load again. @@ -245,6 +246,8 @@ public class LongScreenshotActivity extends Activity { } private void onCachedImageLoaded(ImageLoader.Result imageResult) { + mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_LONG_SCREENSHOT_ACTIVITY_CACHED_IMAGE_LOADED); + BitmapDrawable drawable = new BitmapDrawable(getResources(), imageResult.bitmap); mPreview.setImageDrawable(drawable); mPreview.setAlpha(1f); @@ -282,6 +285,8 @@ public class LongScreenshotActivity extends Activity { finish(); } if (isFinishing()) { + mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_LONG_SCREENSHOT_ACTIVITY_FINISHED); + if (mScrollCaptureResponse != null) { mScrollCaptureResponse.close(); } diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java index 16872b08b9c8..cab2168d44e4 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java @@ -660,7 +660,7 @@ public class ScreenshotController { + mLastScrollCaptureResponse.getWindowTitle() + "]"); final ScrollCaptureResponse response = mLastScrollCaptureResponse; - mScreenshotView.showScrollChip(/* onClick */ () -> { + mScreenshotView.showScrollChip(response.getPackageName(), /* onClick */ () -> { DisplayMetrics displayMetrics = new DisplayMetrics(); getDefaultDisplay().getRealMetrics(displayMetrics); Bitmap newScreenshot = captureScreenshot( diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotEvent.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotEvent.java index 5cf018813133..169b28c6b373 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotEvent.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotEvent.java @@ -71,7 +71,19 @@ public enum ScreenshotEvent implements UiEventLogger.UiEventEnum { @UiEvent(doc = "User has shared a long screenshot") SCREENSHOT_LONG_SCREENSHOT_SHARE(689), @UiEvent(doc = "User has sent a long screenshot to the editor") - SCREENSHOT_LONG_SCREENSHOT_EDIT(690); + SCREENSHOT_LONG_SCREENSHOT_EDIT(690), + @UiEvent(doc = "A long screenshot capture has started") + SCREENSHOT_LONG_SCREENSHOT_STARTED(880), + @UiEvent(doc = "The long screenshot capture failed") + SCREENSHOT_LONG_SCREENSHOT_FAILURE(881), + @UiEvent(doc = "The long screenshot capture completed successfully") + SCREENSHOT_LONG_SCREENSHOT_COMPLETED(882), + @UiEvent(doc = "Long screenshot editor activity started") + SCREENSHOT_LONG_SCREENSHOT_ACTIVITY_STARTED(889), + @UiEvent(doc = "Long screenshot editor activity loaded a previously saved screenshot") + SCREENSHOT_LONG_SCREENSHOT_ACTIVITY_CACHED_IMAGE_LOADED(890), + @UiEvent(doc = "Long screenshot editor activity finished") + SCREENSHOT_LONG_SCREENSHOT_ACTIVITY_FINISHED(891); private final int mId; diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java index e9e62f26a10e..e5e690bcb8b0 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java @@ -242,19 +242,21 @@ public class ScreenshotView extends FrameLayout implements /** * Called to display the scroll action chip when support is detected. * + * @param packageName the owning package of the window to be captured * @param onClick the action to take when the chip is clicked. */ - public void showScrollChip(Runnable onClick) { + public void showScrollChip(String packageName, Runnable onClick) { if (DEBUG_SCROLL) { Log.d(TAG, "Showing Scroll option"); } - mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_LONG_SCREENSHOT_IMPRESSION); + mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_LONG_SCREENSHOT_IMPRESSION, 0, packageName); mScrollChip.setVisibility(VISIBLE); mScrollChip.setOnClickListener((v) -> { if (DEBUG_INPUT) { Log.d(TAG, "scroll chip tapped"); } - mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_LONG_SCREENSHOT_REQUESTED); + mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_LONG_SCREENSHOT_REQUESTED, 0, + packageName); onClick.run(); }); } diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java index 6dc68746e3ec..ef7355a09fbf 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java @@ -28,6 +28,7 @@ import androidx.concurrent.futures.CallbackToFutureAdapter; import androidx.concurrent.futures.CallbackToFutureAdapter.Completer; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.logging.UiEventLogger; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.screenshot.ScrollCaptureClient.CaptureResult; import com.android.systemui.screenshot.ScrollCaptureClient.Session; @@ -61,6 +62,7 @@ public class ScrollCaptureController { private final Context mContext; private final Executor mBgExecutor; private final ImageTileSet mImageTileSet; + private final UiEventLogger mEventLogger; private final ScrollCaptureClient mClient; private Completer<LongScreenshot> mCaptureCompleter; @@ -69,6 +71,7 @@ public class ScrollCaptureController { private Session mSession; private ListenableFuture<CaptureResult> mTileFuture; private ListenableFuture<Void> mEndFuture; + private String mWindowOwner; static class LongScreenshot { private final ImageTileSet mImageTileSet; @@ -135,11 +138,12 @@ public class ScrollCaptureController { @Inject ScrollCaptureController(Context context, @Background Executor bgExecutor, - ScrollCaptureClient client, ImageTileSet imageTileSet) { + ScrollCaptureClient client, ImageTileSet imageTileSet, UiEventLogger logger) { mContext = context; mBgExecutor = bgExecutor; mClient = client; mImageTileSet = imageTileSet; + mEventLogger = logger; } @VisibleForTesting @@ -157,6 +161,7 @@ public class ScrollCaptureController { ListenableFuture<LongScreenshot> run(ScrollCaptureResponse response) { return CallbackToFutureAdapter.getFuture(completer -> { mCaptureCompleter = completer; + mWindowOwner = response.getPackageName(); mBgExecutor.execute(() -> { float maxPages = Settings.Secure.getFloat(mContext.getContentResolver(), SETTING_KEY_MAX_PAGES, MAX_PAGES_DEFAULT); @@ -173,11 +178,13 @@ public class ScrollCaptureController { if (LogConfig.DEBUG_SCROLL) { Log.d(TAG, "got session " + mSession); } + mEventLogger.log(ScreenshotEvent.SCREENSHOT_LONG_SCREENSHOT_STARTED, 0, mWindowOwner); requestNextTile(0); } catch (InterruptedException | ExecutionException e) { // Failure to start, propagate to caller Log.e(TAG, "session start failed!"); mCaptureCompleter.setException(e); + mEventLogger.log(ScreenshotEvent.SCREENSHOT_LONG_SCREENSHOT_FAILURE, 0, mWindowOwner); } } @@ -297,6 +304,11 @@ public class ScrollCaptureController { if (LogConfig.DEBUG_SCROLL) { Log.d(TAG, "finishCapture()"); } + if (mImageTileSet.getHeight() > 0) { + mEventLogger.log(ScreenshotEvent.SCREENSHOT_LONG_SCREENSHOT_COMPLETED, 0, mWindowOwner); + } else { + mEventLogger.log(ScreenshotEvent.SCREENSHOT_LONG_SCREENSHOT_FAILURE, 0, mWindowOwner); + } mEndFuture = mSession.end(); mEndFuture.addListener(() -> { if (LogConfig.DEBUG_SCROLL) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt b/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt index dce19cf86b35..cfbe3b29783a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt @@ -54,22 +54,22 @@ open class BlurUtils @Inject constructor( /** * Translates a ratio from 0 to 1 to a blur radius in pixels. */ - fun blurRadiusOfRatio(ratio: Float): Int { + fun blurRadiusOfRatio(ratio: Float): Float { if (ratio == 0f) { - return 0 + return 0f } - return MathUtils.lerp(minBlurRadius.toFloat(), maxBlurRadius.toFloat(), ratio).toInt() + return MathUtils.lerp(minBlurRadius.toFloat(), maxBlurRadius.toFloat(), ratio) } /** * Translates a blur radius in pixels to a ratio between 0 to 1. */ - fun ratioOfBlurRadius(blur: Int): Float { - if (blur == 0) { + fun ratioOfBlurRadius(blur: Float): Float { + if (blur == 0f) { return 0f } return MathUtils.map(minBlurRadius.toFloat(), maxBlurRadius.toFloat(), - 0f /* maxStart */, 1f /* maxStop */, blur.toFloat()) + 0f /* maxStart */, 1f /* maxStop */, blur) } /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java index 503b5c0ee4b0..1431c9e6f641 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java @@ -821,7 +821,7 @@ public class KeyguardIndicationController { } } - private void showTryFingerprintMsg() { + private void showTryFingerprintMsg(String a11yString) { if (mKeyguardUpdateMonitor.isUdfpsAvailable()) { // if udfps available, there will always be a tappable affordance to unlock // For example, the lock icon @@ -833,6 +833,11 @@ public class KeyguardIndicationController { } else { showTransientIndication(R.string.keyguard_try_fingerprint); } + + // Although we suppress face auth errors visually, we still announce them for a11y + if (!TextUtils.isEmpty(a11yString)) { + mLockScreenIndicationView.announceForAccessibility(a11yString); + } } public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { @@ -913,7 +918,7 @@ public class KeyguardIndicationController { } else if (mKeyguardUpdateMonitor.isScreenOn()) { if (biometricSourceType == BiometricSourceType.FACE && shouldSuppressFaceMsgAndShowTryFingerprintMsg()) { - showTryFingerprintMsg(); + showTryFingerprintMsg(helpString); return; } showTransientIndication(helpString, false /* isError */, showActionToUnlock); @@ -933,7 +938,7 @@ public class KeyguardIndicationController { && shouldSuppressFaceMsgAndShowTryFingerprintMsg() && !mStatusBarKeyguardViewManager.isBouncerShowing() && mKeyguardUpdateMonitor.isScreenOn()) { - showTryFingerprintMsg(); + showTryFingerprintMsg(errString); return; } if (msgId == FaceManager.FACE_ERROR_TIMEOUT) { @@ -942,7 +947,7 @@ public class KeyguardIndicationController { if (!mStatusBarKeyguardViewManager.isBouncerShowing() && mKeyguardUpdateMonitor.isUdfpsEnrolled() && mKeyguardUpdateMonitor.isFingerprintDetectionRunning()) { - showTryFingerprintMsg(); + showTryFingerprintMsg(errString); } else if (mStatusBarKeyguardViewManager.isShowingAlternateAuth()) { mStatusBarKeyguardViewManager.showBouncerMessage( mContext.getResources().getString(R.string.keyguard_unlock_press), diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt index 8780ad89e082..51dbd85775c6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt @@ -178,7 +178,10 @@ class PowerButtonReveal( */ class LightRevealScrim(context: Context?, attrs: AttributeSet?) : View(context, attrs) { - lateinit var revealAmountListener: Consumer<Float> + /** + * Listener that is called if the scrim's opaqueness changes + */ + lateinit var isScrimOpaqueChangedListener: Consumer<Boolean> /** * How much of the underlying views are revealed, in percent. 0 means they will be completely @@ -190,7 +193,7 @@ class LightRevealScrim(context: Context?, attrs: AttributeSet?) : View(context, field = value revealEffect.setRevealAmountOnScrim(value, this) - revealAmountListener.accept(value) + updateScrimOpaque() invalidate() } } @@ -230,6 +233,31 @@ class LightRevealScrim(context: Context?, attrs: AttributeSet?) : View(context, } /** + * Is the scrim currently fully opaque + */ + var isScrimOpaque = false + private set(value) { + if (field != value) { + field = value + isScrimOpaqueChangedListener.accept(field) + } + } + + private fun updateScrimOpaque() { + isScrimOpaque = revealAmount == 0.0f && alpha == 1.0f && visibility == VISIBLE + } + + override fun setAlpha(alpha: Float) { + super.setAlpha(alpha) + updateScrimOpaque() + } + + override fun setVisibility(visibility: Int) { + super.setVisibility(visibility) + updateScrimOpaque() + } + + /** * Paint used to draw a transparent-to-white radial gradient. This will be scaled and translated * via local matrix in [onDraw] so we never need to construct a new shader. */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt index 002c9c7d2544..b8334272c157 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt @@ -68,7 +68,7 @@ class NotificationShadeDepthController @Inject constructor( private const val VELOCITY_SCALE = 100f private const val MAX_VELOCITY = 3000f private const val MIN_VELOCITY = -MAX_VELOCITY - private const val INTERACTION_BLUR_FRACTION = 0.4f + private const val INTERACTION_BLUR_FRACTION = 0.8f private const val ANIMATION_BLUR_FRACTION = 1f - INTERACTION_BLUR_FRACTION private const val TAG = "DepthController" } @@ -92,8 +92,6 @@ class NotificationShadeDepthController @Inject constructor( // Only for dumpsys private var lastAppliedBlur = 0 - @VisibleForTesting - var shadeSpring = DepthAnimation() var shadeAnimation = DepthAnimation() @VisibleForTesting @@ -101,12 +99,16 @@ class NotificationShadeDepthController @Inject constructor( var brightnessMirrorVisible: Boolean = false set(value) { field = value - brightnessMirrorSpring.animateTo(if (value) blurUtils.blurRadiusOfRatio(1f) + brightnessMirrorSpring.animateTo(if (value) blurUtils.blurRadiusOfRatio(1f).toInt() else 0) } var qsPanelExpansion = 0f set(value) { + if (value.isNaN()) { + Log.w(TAG, "Invalid qs expansion") + return + } if (field == value) return field = value scheduleUpdate() @@ -134,15 +136,13 @@ class NotificationShadeDepthController @Inject constructor( field = value scheduleUpdate() - if (shadeSpring.radius == 0 && shadeAnimation.radius == 0) { + if (shadeExpansion == 0f && shadeAnimation.radius == 0f) { return } // Do not remove blurs when we're re-enabling them if (!value) { return } - shadeSpring.animateTo(0) - shadeSpring.finishIfRunning() shadeAnimation.animateTo(0) shadeAnimation.finishIfRunning() @@ -161,7 +161,7 @@ class NotificationShadeDepthController @Inject constructor( /** * Blur radius of the wake-up animation on this frame. */ - private var wakeAndUnlockBlurRadius = 0 + private var wakeAndUnlockBlurRadius = 0f set(value) { if (field == value) return field = value @@ -174,26 +174,30 @@ class NotificationShadeDepthController @Inject constructor( @VisibleForTesting val updateBlurCallback = Choreographer.FrameCallback { updateScheduled = false - val normalizedBlurRadius = MathUtils.constrain(shadeAnimation.radius, - blurUtils.minBlurRadius, blurUtils.maxBlurRadius) - var combinedBlur = (shadeSpring.radius * INTERACTION_BLUR_FRACTION + - normalizedBlurRadius * ANIMATION_BLUR_FRACTION).toInt() + val animationRadius = MathUtils.constrain(shadeAnimation.radius, + blurUtils.minBlurRadius.toFloat(), blurUtils.maxBlurRadius.toFloat()) + val expansionRadius = blurUtils.blurRadiusOfRatio( + Interpolators.getNotificationScrimAlpha( + if (shouldApplyShadeBlur()) shadeExpansion else 0f, false)) + var combinedBlur = (expansionRadius * INTERACTION_BLUR_FRACTION + + animationRadius * ANIMATION_BLUR_FRACTION) val qsExpandedRatio = qsPanelExpansion * shadeExpansion combinedBlur = max(combinedBlur, blurUtils.blurRadiusOfRatio(qsExpandedRatio)) combinedBlur = max(combinedBlur, blurUtils.blurRadiusOfRatio(transitionToFullShadeProgress)) - var shadeRadius = max(combinedBlur, wakeAndUnlockBlurRadius).toFloat() + var shadeRadius = max(combinedBlur, wakeAndUnlockBlurRadius) if (blursDisabledForAppLaunch) { shadeRadius = 0f } + var zoomOut = MathUtils.saturate(blurUtils.ratioOfBlurRadius(shadeRadius)) var blur = shadeRadius.toInt() // Make blur be 0 if it is necessary to stop blur effect. if (scrimsVisible) { blur = 0 + zoomOut = 0f } - val zoomOut = blurUtils.ratioOfBlurRadius(blur) if (!blurUtils.supportsBlursOnWindows()) { blur = 0 @@ -266,12 +270,11 @@ class NotificationShadeDepthController @Inject constructor( override fun onStateChanged(newState: Int) { updateShadeAnimationBlur( shadeExpansion, prevTracking, prevShadeVelocity, prevShadeDirection) - updateShadeBlur() + scheduleUpdate() } override fun onDozingChanged(isDozing: Boolean) { if (isDozing) { - shadeSpring.finishIfRunning() shadeAnimation.finishIfRunning() brightnessMirrorSpring.finishIfRunning() } @@ -336,7 +339,7 @@ class NotificationShadeDepthController @Inject constructor( prevTracking = tracking prevTimestamp = timestamp - updateShadeBlur() + scheduleUpdate() } private fun updateShadeAnimationBlur( @@ -399,15 +402,7 @@ class NotificationShadeDepthController @Inject constructor( } shadeAnimation.setStartVelocity(velocity) - shadeAnimation.animateTo(blurUtils.blurRadiusOfRatio(targetBlurNormalized)) - } - - private fun updateShadeBlur() { - var newBlur = 0 - if (shouldApplyShadeBlur()) { - newBlur = blurUtils.blurRadiusOfRatio(shadeExpansion) - } - shadeSpring.animateTo(newBlur) + shadeAnimation.animateTo(blurUtils.blurRadiusOfRatio(targetBlurNormalized).toInt()) } private fun scheduleUpdate(viewToBlur: View? = null) { @@ -433,7 +428,8 @@ class NotificationShadeDepthController @Inject constructor( IndentingPrintWriter(pw, " ").let { it.println("StatusBarWindowBlurController:") it.increaseIndent() - it.println("shadeRadius: ${shadeSpring.radius}") + it.println("shadeExpansion: $shadeExpansion") + it.println("shouldApplyShaeBlur: ${shouldApplyShadeBlur()}") it.println("shadeAnimation: ${shadeAnimation.radius}") it.println("brightnessMirrorRadius: ${brightnessMirrorSpring.radius}") it.println("wakeAndUnlockBlur: $wakeAndUnlockBlurRadius") @@ -452,7 +448,7 @@ class NotificationShadeDepthController @Inject constructor( /** * Blur radius visible on the UI, in pixels. */ - var radius = 0 + var radius = 0f /** * Depth ratio of the current blur radius. @@ -473,12 +469,12 @@ class NotificationShadeDepthController @Inject constructor( private var springAnimation = SpringAnimation(this, object : FloatPropertyCompat<DepthAnimation>("blurRadius") { override fun setValue(rect: DepthAnimation?, value: Float) { - radius = value.toInt() + radius = value scheduleUpdate(view) } override fun getValue(rect: DepthAnimation?): Float { - return radius.toFloat() + return radius } }) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowController.java index f0d779ce1e0f..6ea79af8b9ad 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowController.java @@ -182,10 +182,10 @@ public interface NotificationShadeWindowController extends RemoteInputController default void setFaceAuthDisplayBrightness(float brightness) {} /** - * How much {@link LightRevealScrim} obscures the UI. - * @param amount 0 when opaque, 1 when not transparent + * If {@link LightRevealScrim} obscures the UI. + * @param opaque if the scrim is opaque */ - default void setLightRevealScrimAmount(float amount) {} + default void setLightRevealScrimOpaque(boolean opaque) {} /** * Custom listener to pipe data back to plugins about whether or not the status bar would be diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameView.java b/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameView.java index cc7a4f836c63..4a6d7e184ec2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameView.java @@ -15,46 +15,18 @@ package com.android.systemui.statusbar; import android.content.Context; -import android.graphics.Rect; -import android.os.Bundle; -import android.telephony.ServiceState; -import android.telephony.SubscriptionInfo; -import android.telephony.TelephonyManager; import android.text.TextUtils; import android.util.AttributeSet; import android.widget.TextView; -import com.android.keyguard.KeyguardUpdateMonitor; -import com.android.keyguard.KeyguardUpdateMonitorCallback; import com.android.settingslib.WirelessUtils; -import com.android.systemui.Dependency; -import com.android.systemui.demomode.DemoModeCommandReceiver; -import com.android.systemui.plugins.DarkIconDispatcher; -import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver; -import com.android.systemui.statusbar.policy.NetworkController; -import com.android.systemui.statusbar.policy.NetworkController.IconState; -import com.android.systemui.statusbar.policy.NetworkController.SignalCallback; -import com.android.systemui.tuner.TunerService; -import com.android.systemui.tuner.TunerService.Tunable; import java.util.List; /** Shows the operator name */ -public class OperatorNameView extends TextView implements DemoModeCommandReceiver, DarkReceiver, - SignalCallback, Tunable { - - private static final String KEY_SHOW_OPERATOR_NAME = "show_operator_name"; - - private KeyguardUpdateMonitor mKeyguardUpdateMonitor; +public class OperatorNameView extends TextView { private boolean mDemoMode; - private final KeyguardUpdateMonitorCallback mCallback = new KeyguardUpdateMonitorCallback() { - @Override - public void onRefreshCarrierInfo() { - updateText(); - } - }; - public OperatorNameView(Context context) { this(context, null); } @@ -67,62 +39,14 @@ public class OperatorNameView extends TextView implements DemoModeCommandReceive super(context, attrs, defStyle); } - @Override - protected void onAttachedToWindow() { - super.onAttachedToWindow(); - mKeyguardUpdateMonitor = Dependency.get(KeyguardUpdateMonitor.class); - mKeyguardUpdateMonitor.registerCallback(mCallback); - Dependency.get(DarkIconDispatcher.class).addDarkReceiver(this); - Dependency.get(NetworkController.class).addCallback(this); - Dependency.get(TunerService.class).addTunable(this, KEY_SHOW_OPERATOR_NAME); - } - - @Override - protected void onDetachedFromWindow() { - super.onDetachedFromWindow(); - mKeyguardUpdateMonitor.removeCallback(mCallback); - Dependency.get(DarkIconDispatcher.class).removeDarkReceiver(this); - Dependency.get(NetworkController.class).removeCallback(this); - Dependency.get(TunerService.class).removeTunable(this); - } - - @Override - public void onDarkChanged(Rect area, float darkIntensity, int tint) { - setTextColor(DarkIconDispatcher.getTint(area, this, tint)); - } - - @Override - public void setIsAirplaneMode(IconState icon) { - update(); - } - - @Override - public void onTuningChanged(String key, String newValue) { - update(); - } - - @Override - public void dispatchDemoCommand(String command, Bundle args) { - setText(args.getString("name")); - } - - @Override - public void onDemoModeStarted() { - mDemoMode = true; - } - - @Override - public void onDemoModeFinished() { - mDemoMode = false; - update(); + void setDemoMode(boolean demoMode) { + mDemoMode = demoMode; } - private void update() { - boolean showOperatorName = Dependency.get(TunerService.class) - .getValue(KEY_SHOW_OPERATOR_NAME, 1) != 0; + void update(boolean showOperatorName, boolean hasMobile, + List<OperatorNameViewController.SubInfo> subs) { setVisibility(showOperatorName ? VISIBLE : GONE); - boolean hasMobile = mContext.getSystemService(TelephonyManager.class).isDataCapable(); boolean airplaneMode = WirelessUtils.isAirplaneModeOn(mContext); if (!hasMobile || airplaneMode) { setText(null); @@ -131,22 +55,19 @@ public class OperatorNameView extends TextView implements DemoModeCommandReceive } if (!mDemoMode) { - updateText(); + updateText(subs); } } - private void updateText() { + void updateText(List<OperatorNameViewController.SubInfo> subs) { CharSequence displayText = null; - List<SubscriptionInfo> subs = mKeyguardUpdateMonitor.getFilteredSubscriptionInfo(false); final int N = subs.size(); for (int i = 0; i < N; i++) { - int subId = subs.get(i).getSubscriptionId(); - int simState = mKeyguardUpdateMonitor.getSimState(subId); + OperatorNameViewController.SubInfo subInfo = subs.get(i); CharSequence carrierName = subs.get(i).getCarrierName(); - if (!TextUtils.isEmpty(carrierName) && simState == TelephonyManager.SIM_STATE_READY) { - ServiceState ss = mKeyguardUpdateMonitor.getServiceState(subId); - if (ss != null && ss.getState() == ServiceState.STATE_IN_SERVICE) { - displayText = carrierName; + if (!TextUtils.isEmpty(carrierName) && subInfo.simReady()) { + if (subInfo.stateInService()) { + displayText = subInfo.getCarrierName(); break; } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameViewController.java new file mode 100644 index 000000000000..e49f48f2cf49 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameViewController.java @@ -0,0 +1,201 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar; + +import android.os.Bundle; +import android.telephony.ServiceState; +import android.telephony.SubscriptionInfo; +import android.telephony.TelephonyManager; +import android.view.View; + +import com.android.keyguard.KeyguardUpdateMonitor; +import com.android.keyguard.KeyguardUpdateMonitorCallback; +import com.android.systemui.demomode.DemoModeCommandReceiver; +import com.android.systemui.plugins.DarkIconDispatcher; +import com.android.systemui.statusbar.policy.NetworkController; +import com.android.systemui.tuner.TunerService; +import com.android.systemui.util.ViewController; + +import java.util.ArrayList; +import java.util.List; + +import javax.inject.Inject; + +/** Controller for {@link OperatorNameView}. */ +public class OperatorNameViewController extends ViewController<OperatorNameView> { + private static final String KEY_SHOW_OPERATOR_NAME = "show_operator_name"; + + private final DarkIconDispatcher mDarkIconDispatcher; + private final NetworkController mNetworkController; + private final TunerService mTunerService; + private final TelephonyManager mTelephonyManager; + private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; + + private OperatorNameViewController(OperatorNameView view, + DarkIconDispatcher darkIconDispatcher, + NetworkController networkController, + TunerService tunerService, + TelephonyManager telephonyManager, + KeyguardUpdateMonitor keyguardUpdateMonitor) { + super(view); + mDarkIconDispatcher = darkIconDispatcher; + mNetworkController = networkController; + mTunerService = tunerService; + mTelephonyManager = telephonyManager; + mKeyguardUpdateMonitor = keyguardUpdateMonitor; + } + + @Override + protected void onViewAttached() { + mDarkIconDispatcher.addDarkReceiver(mDarkReceiver); + mNetworkController.addCallback(mSignalCallback); + mTunerService.addTunable(mTunable, KEY_SHOW_OPERATOR_NAME); + mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateMonitorCallback); + } + + @Override + protected void onViewDetached() { + mDarkIconDispatcher.removeDarkReceiver(mDarkReceiver); + mNetworkController.removeCallback(mSignalCallback); + mTunerService.removeTunable(mTunable); + mKeyguardUpdateMonitor.removeCallback(mKeyguardUpdateMonitorCallback); + } + + private void update() { + mView.update(mTunerService.getValue(KEY_SHOW_OPERATOR_NAME, 1) != 0, + mTelephonyManager.isDataCapable(), getSubInfos()); + } + + private List<SubInfo> getSubInfos() { + List<SubInfo> result = new ArrayList<>(); + List<SubscriptionInfo> subscritionInfos = + mKeyguardUpdateMonitor.getFilteredSubscriptionInfo(false); + + for (SubscriptionInfo subscriptionInfo : subscritionInfos) { + int subId = subscriptionInfo.getSubscriptionId(); + result.add(new SubInfo( + subscriptionInfo.getCarrierName(), + mKeyguardUpdateMonitor.getSimState(subId), + mKeyguardUpdateMonitor.getServiceState(subId))); + } + + return result; + } + + /** Factory for constructing an {@link OperatorNameViewController}. */ + public static class Factory { + private final DarkIconDispatcher mDarkIconDispatcher; + private final NetworkController mNetworkController; + private final TunerService mTunerService; + private final TelephonyManager mTelephonyManager; + private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; + + @Inject + public Factory(DarkIconDispatcher darkIconDispatcher, NetworkController networkController, + TunerService tunerService, TelephonyManager telephonyManager, + KeyguardUpdateMonitor keyguardUpdateMonitor) { + mDarkIconDispatcher = darkIconDispatcher; + mNetworkController = networkController; + mTunerService = tunerService; + mTelephonyManager = telephonyManager; + mKeyguardUpdateMonitor = keyguardUpdateMonitor; + } + + /** Create an {@link OperatorNameViewController}. */ + public OperatorNameViewController create(OperatorNameView view) { + return new OperatorNameViewController(view, mDarkIconDispatcher, mNetworkController, + mTunerService, mTelephonyManager, mKeyguardUpdateMonitor); + } + } + + /** + * Needed because of how {@link CollapsedStatusBarFragment} works. + * + * Ideally this can be done internally. + **/ + public View getView() { + return mView; + } + + private final DarkIconDispatcher.DarkReceiver mDarkReceiver = + (area, darkIntensity, tint) -> + mView.setTextColor(DarkIconDispatcher.getTint(area, mView, tint)); + + private final NetworkController.SignalCallback mSignalCallback = + new NetworkController.SignalCallback() { + @Override + public void setIsAirplaneMode(NetworkController.IconState icon) { + update(); + } + }; + + private final TunerService.Tunable mTunable = (key, newValue) -> update(); + + + private final KeyguardUpdateMonitorCallback mKeyguardUpdateMonitorCallback = + new KeyguardUpdateMonitorCallback() { + @Override + public void onRefreshCarrierInfo() { + mView.updateText(getSubInfos()); + } + }; + + // TODO: do we even register this anywhere? + private final DemoModeCommandReceiver mDemoModeCommandReceiver = new DemoModeCommandReceiver() { + @Override + public void onDemoModeStarted() { + mView.setDemoMode(true); + } + + @Override + public void onDemoModeFinished() { + mView.setDemoMode(false); + update(); + } + + @Override + public void dispatchDemoCommand(String command, Bundle args) { + mView.setText(args.getString("name")); + } + }; + + static class SubInfo { + private final CharSequence mCarrierName; + private final int mSimState; + private final ServiceState mServiceState; + + private SubInfo(CharSequence carrierName, + int simState, ServiceState serviceState) { + mCarrierName = carrierName; + mSimState = simState; + mServiceState = serviceState; + } + + boolean simReady() { + return mSimState == TelephonyManager.SIM_STATE_READY; + } + + CharSequence getCarrierName() { + return mCarrierName; + } + + boolean stateInService() { + return mServiceState != null + && mServiceState.getState() == ServiceState.STATE_IN_SERVICE; + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/charging/ChargingRippleView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/charging/ChargingRippleView.kt index 4a467ce3c987..d01fc93ee84c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/charging/ChargingRippleView.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/charging/ChargingRippleView.kt @@ -104,7 +104,7 @@ class ChargingRippleView(context: Context?, attrs: AttributeSet?) : View(context // the active effect area. Values here should be kept in sync with the // animation implementation in the ripple shader. val maskRadius = (1 - (1 - rippleShader.progress) * (1 - rippleShader.progress) * - (1 - rippleShader.progress)) * radius * 1.5f + (1 - rippleShader.progress)) * radius * 2 canvas?.drawCircle(origin.x, origin.y, maskRadius, ripplePaint) } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt index 29cfb07a14f9..1037e576f263 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt @@ -497,7 +497,12 @@ class PrivacyDotViewController @Inject constructor( } if (state.designatedCorner != currentViewState.designatedCorner) { + currentViewState.designatedCorner?.contentDescription = null + state.designatedCorner?.contentDescription = state.contentDescription + updateDesignatedCorner(state.designatedCorner, state.shouldShowDot()) + } else if (state.contentDescription != currentViewState.contentDescription) { + state.designatedCorner?.contentDescription = state.contentDescription } val shouldShow = state.shouldShowDot() @@ -514,9 +519,13 @@ class PrivacyDotViewController @Inject constructor( private val systemStatusAnimationCallback: SystemStatusAnimationCallback = object : SystemStatusAnimationCallback { - override fun onSystemStatusAnimationTransitionToPersistentDot(): Animator? { + override fun onSystemStatusAnimationTransitionToPersistentDot( + contentDescr: String? + ): Animator? { synchronized(lock) { - nextViewState = nextViewState.copy(systemPrivacyEventIsActive = true) + nextViewState = nextViewState.copy( + systemPrivacyEventIsActive = true, + contentDescription = contentDescr) } return null @@ -620,7 +629,9 @@ private data class ViewState( val rotation: Int = 0, val height: Int = 0, val cornerIndex: Int = -1, - val designatedCorner: View? = null + val designatedCorner: View? = null, + + val contentDescription: String? = null ) { fun shouldShowDot(): Boolean { return systemPrivacyEventIsActive && !shadeExpanded && !qsExpanded diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/StatusEvent.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/StatusEvent.kt index 539020d52db5..d4d84c138b20 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/events/StatusEvent.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/StatusEvent.kt @@ -34,6 +34,7 @@ interface StatusEvent { // Whether or not to show an animation for this event val showAnimation: Boolean val viewCreator: (context: Context) -> View + var contentDescription: String? // Update this event with values from another event. fun updateFromEvent(other: StatusEvent?) { @@ -50,6 +51,7 @@ class BatteryEvent : StatusEvent { override val priority = 50 override val forceVisible = false override val showAnimation = true + override var contentDescription: String? = "" override val viewCreator: (context: Context) -> View = { context -> val iv = ImageView(context) @@ -62,7 +64,9 @@ class BatteryEvent : StatusEvent { return javaClass.simpleName } } + class PrivacyEvent(override val showAnimation: Boolean = true) : StatusEvent { + override var contentDescription: String? = null override val priority = 100 override val forceVisible = true var privacyItems: List<PrivacyItem> = listOf() @@ -72,6 +76,7 @@ class PrivacyEvent(override val showAnimation: Boolean = true) : StatusEvent { val v = LayoutInflater.from(context) .inflate(R.layout.ongoing_privacy_chip, null) as OngoingPrivacyChip v.privacyList = privacyItems + v.contentDescription = contentDescription privacyChip = v v } @@ -81,7 +86,9 @@ class PrivacyEvent(override val showAnimation: Boolean = true) : StatusEvent { } override fun shouldUpdateFromEvent(other: StatusEvent?): Boolean { - return other is PrivacyEvent && other.privacyItems != privacyItems + return other is PrivacyEvent && + (other.privacyItems != privacyItems || + other.contentDescription != contentDescription) } override fun updateFromEvent(other: StatusEvent?) { @@ -90,6 +97,9 @@ class PrivacyEvent(override val showAnimation: Boolean = true) : StatusEvent { } privacyItems = other.privacyItems + contentDescription = other.contentDescription + + privacyChip?.contentDescription = other.contentDescription privacyChip?.privacyList = other.privacyItems } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventChipAnimationController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventChipAnimationController.kt index b861c1db9b8b..7291b5a8be3b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventChipAnimationController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventChipAnimationController.kt @@ -34,8 +34,7 @@ import com.android.systemui.statusbar.phone.StatusBarWindowView import javax.inject.Inject /** - * //TODO: this _probably_ doesn't control a window anymore - * Controls the window for system event animations. + * Controls the view for system event animations. */ class SystemEventChipAnimationController @Inject constructor( private val context: Context, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventCoordinator.kt index ba50659f5567..04f7492e8562 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventCoordinator.kt @@ -16,9 +16,12 @@ package com.android.systemui.statusbar.events +import android.content.Context import android.provider.DeviceConfig import android.provider.DeviceConfig.NAMESPACE_PRIVACY +import com.android.systemui.R import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.privacy.PrivacyChipBuilder import com.android.systemui.privacy.PrivacyItem import com.android.systemui.privacy.PrivacyItemController import com.android.systemui.statusbar.policy.BatteryController @@ -33,7 +36,8 @@ import javax.inject.Inject class SystemEventCoordinator @Inject constructor( private val systemClock: SystemClock, private val batteryController: BatteryController, - private val privacyController: PrivacyItemController + private val privacyController: PrivacyItemController, + private val context: Context ) { private lateinit var scheduler: SystemStatusAnimationScheduler @@ -66,6 +70,11 @@ class SystemEventCoordinator @Inject constructor( fun notifyPrivacyItemsChanged(showAnimation: Boolean = true) { val event = PrivacyEvent(showAnimation) event.privacyItems = privacyStateListener.currentPrivacyItems + event.contentDescription = { + val items = PrivacyChipBuilder(context, event.privacyItems).joinTypes() + context.getString( + R.string.ongoing_privacy_chip_content_multiple_apps, items) + }() scheduler.onStatusEvent(event) } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt index f30010cf4d1c..dcf8e739a76e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt @@ -100,14 +100,21 @@ class SystemStatusAnimationScheduler @Inject constructor( // Don't deal with threading for now (no need let's be honest) Assert.isMainThread() - if (event.priority > scheduledEvent?.priority ?: -1 || - scheduledEvent?.shouldUpdateFromEvent(event) == true) { + if ((event.priority > scheduledEvent?.priority ?: -1) && + animationState != ANIMATING_OUT && + (animationState != SHOWING_PERSISTENT_DOT && event.forceVisible)) { + // events can only be scheduled if a higher priority or no other event is in progress if (DEBUG) { Log.d(TAG, "scheduling event $event") } - if (event.showAnimation) { - scheduleEvent(event) - } else if (event.forceVisible) { + + scheduleEvent(event) + } else if (scheduledEvent?.shouldUpdateFromEvent(event) == true) { + if (DEBUG) { + Log.d(TAG, "updating current event from: $event") + } + scheduledEvent?.updateFromEvent(event) + if (event.forceVisible) { hasPersistentDot = true notifyTransitionToPersistentDot() } @@ -142,22 +149,17 @@ class SystemStatusAnimationScheduler @Inject constructor( * Clear the scheduled event (if any) and schedule a new one */ private fun scheduleEvent(event: StatusEvent) { - if (animationState == ANIMATING_OUT || - (animationState == SHOWING_PERSISTENT_DOT && event.forceVisible)) { - // do not schedule an event or change the current one - return - } + scheduledEvent = event - // If we are showing the chip, possibly update the current event, rather than replacing - if (scheduledEvent?.shouldUpdateFromEvent(event) == true) { - scheduledEvent?.updateFromEvent(event) - return - } else { - scheduledEvent = event + if (event.forceVisible) { + hasPersistentDot = true } - if (scheduledEvent!!.forceVisible) { - hasPersistentDot = true + // If animations are turned off, we'll transition directly to the dot + if (!event.showAnimation && event.forceVisible) { + notifyTransitionToPersistentDot() + scheduledEvent = null + return } // Schedule the animation to start after a debounce period @@ -218,7 +220,7 @@ class SystemStatusAnimationScheduler @Inject constructor( private fun notifyTransitionToPersistentDot(): Animator? { val anims: List<Animator> = listeners.mapNotNull { - it.onSystemStatusAnimationTransitionToPersistentDot() + it.onSystemStatusAnimationTransitionToPersistentDot(scheduledEvent?.contentDescription) } if (anims.isNotEmpty()) { val aSet = AnimatorSet() @@ -346,7 +348,10 @@ interface SystemStatusAnimationCallback { @JvmDefault fun onSystemChromeAnimationEnd() {} // Best method name, change my mind - @JvmDefault fun onSystemStatusAnimationTransitionToPersistentDot(): Animator? { return null } + @JvmDefault + fun onSystemStatusAnimationTransitionToPersistentDot(contentDescription: String?): Animator? { + return null + } @JvmDefault fun onHidePersistentDot(): Animator? { return null } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java index 86c90c7bcb2e..9eb95c409009 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java @@ -16,7 +16,6 @@ package com.android.systemui.statusbar.notification.row; -import android.annotation.ColorInt; import android.content.Context; import android.content.res.Configuration; import android.content.res.Resources; @@ -28,15 +27,12 @@ import com.android.systemui.statusbar.notification.stack.ExpandableViewState; import com.android.systemui.statusbar.notification.stack.ViewState; public class FooterView extends StackScrollerDecorView { - private final int mClearAllTopPadding; private FooterViewButton mDismissButton; private FooterViewButton mManageButton; private boolean mShowHistory; public FooterView(Context context, AttributeSet attrs) { super(context, attrs); - mClearAllTopPadding = context.getResources().getDimensionPixelSize( - R.dimen.clear_all_padding_top); } @Override @@ -55,11 +51,6 @@ public class FooterView extends StackScrollerDecorView { mManageButton = findViewById(R.id.manage_text); } - public void setTextColor(@ColorInt int color) { - mManageButton.setTextColor(color); - mDismissButton.setTextColor(color); - } - public void setManageButtonClickListener(OnClickListener listener) { mManageButton.setOnClickListener(listener); } @@ -95,21 +86,25 @@ public class FooterView extends StackScrollerDecorView { @Override protected void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); - int textColor = getResources().getColor(R.color.notif_pill_text); - Resources.Theme theme = getContext().getTheme(); - mDismissButton.setBackground( - getResources().getDrawable(R.drawable.notif_footer_btn_background, theme)); - mDismissButton.setTextColor(textColor); - mManageButton.setBackground( - getResources().getDrawable(R.drawable.notif_footer_btn_background, theme)); - mManageButton = findViewById(R.id.manage_text); + updateColors(); mDismissButton.setText(R.string.clear_all_notifications_text); - mManageButton.setTextColor(textColor); mDismissButton.setContentDescription( mContext.getString(R.string.accessibility_clear_all)); showHistory(mShowHistory); } + /** + * Update the text and background colors for the current color palette and night mode setting. + */ + public void updateColors() { + Resources.Theme theme = mContext.getTheme(); + int textColor = getResources().getColor(R.color.notif_pill_text, theme); + mDismissButton.setBackground(theme.getDrawable(R.drawable.notif_footer_btn_background)); + mDismissButton.setTextColor(textColor); + mManageButton.setBackground(theme.getDrawable(R.drawable.notif_footer_btn_background)); + mManageButton.setTextColor(textColor); + } + @Override public ExpandableViewState createExpandableViewState() { return new FooterViewState(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java index dba3401cc28e..9c755e970a0f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java @@ -85,23 +85,26 @@ public abstract class StackScrollerDecorView extends ExpandableView { } /** - * Set the content of this view to be visible in an animated way. - * - * @param contentVisible True if the content should be visible or false if it should be hidden. + * @param visible True if we should animate contents visible */ - public void setContentVisible(boolean contentVisible) { - setContentVisible(contentVisible, true /* animate */); + public void setContentVisible(boolean visible) { + setContentVisible(visible, true /* animate */, null /* runAfter */); } + /** - * Set the content of this view to be visible. - * @param contentVisible True if the content should be visible or false if it should be hidden. - * @param animate Should an animation be performed. + * @param visible True if the contents should be visible + * @param animate True if we should fade to new visibility + * @param runAfter Runnable to run after visibility updates */ - private void setContentVisible(boolean contentVisible, boolean animate) { - if (mContentVisible != contentVisible) { + public void setContentVisible(boolean visible, boolean animate, Runnable runAfter) { + if (mContentVisible != visible) { mContentAnimating = animate; - mContentVisible = contentVisible; - setViewVisible(mContent, contentVisible, animate, mContentVisibilityEndRunnable); + mContentVisible = visible; + Runnable endRunnable = runAfter == null ? mContentVisibilityEndRunnable : () -> { + mContentVisibilityEndRunnable.run(); + runAfter.run(); + }; + setViewVisible(mContent, visible, animate, endRunnable); } if (!mContentAnimating) { @@ -113,6 +116,10 @@ public abstract class StackScrollerDecorView extends ExpandableView { return mContentVisible; } + public void setVisible(boolean nowVisible, boolean animate) { + setVisible(nowVisible, animate, null); + } + /** * Make this view visible. If {@code false} is passed, the view will fade out it's content * and set the view Visibility to GONE. If only the content should be changed @@ -121,7 +128,7 @@ public abstract class StackScrollerDecorView extends ExpandableView { * @param nowVisible should the view be visible * @param animate should the change be animated. */ - public void setVisible(boolean nowVisible, boolean animate) { + public void setVisible(boolean nowVisible, boolean animate, Runnable runAfter) { if (mIsVisible != nowVisible) { mIsVisible = nowVisible; if (animate) { @@ -132,10 +139,10 @@ public abstract class StackScrollerDecorView extends ExpandableView { } else { setWillBeGone(true); } - setContentVisible(nowVisible, true /* animate */); + setContentVisible(nowVisible, true /* animate */, runAfter); } else { setVisibility(nowVisible ? VISIBLE : GONE); - setContentVisible(nowVisible, false /* animate */); + setContentVisible(nowVisible, false /* animate */, runAfter); setWillBeGone(false); notifyHeightChanged(false /* needsAnimation */); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java index 23aefd9bfd8e..594afceab63a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java @@ -46,6 +46,7 @@ public class NotificationRoundnessManager { private Runnable mRoundingChangedCallback; private ExpandableNotificationRow mTrackedHeadsUp; private float mAppearFraction; + private boolean mIsDismissAllInProgress; private ExpandableView mSwipedView = null; private ExpandableView mViewBeforeSwipedView = null; @@ -162,6 +163,10 @@ public class NotificationRoundnessManager { } } + void setDismissAllInProgress(boolean isClearingAll) { + mIsDismissAllInProgress = isClearingAll; + } + private float getRoundness(ExpandableView view, boolean top) { if (view == null) { return 0f; @@ -171,6 +176,11 @@ public class NotificationRoundnessManager { || view == mViewAfterSwipedView) { return 1f; } + if (view instanceof ExpandableNotificationRow + && ((ExpandableNotificationRow) view).canViewBeDismissed() + && mIsDismissAllInProgress) { + return 1.0f; + } if ((view.isPinned() || (view.isHeadsUpAnimatingAway()) && !mExpanded)) { return 1.0f; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java index 880e55847a46..7babcba293be 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java @@ -139,6 +139,10 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable private static final boolean DEBUG_REMOVE_ANIMATION = SystemProperties.getBoolean( "persist.debug.nssl.dismiss", false /* default */); + // Delay in milli-seconds before shade closes for clear all. + private final int DELAY_BEFORE_SHADE_CLOSE = 200; + private boolean mShadeNeedsToClose = false; + private static final float RUBBER_BAND_FACTOR_NORMAL = 0.35f; private static final float RUBBER_BAND_FACTOR_AFTER_EXPAND = 0.15f; private static final float RUBBER_BAND_FACTOR_ON_PANEL_EXPAND = 0.21f; @@ -253,7 +257,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable protected FooterView mFooterView; protected EmptyShadeView mEmptyShadeView; private boolean mDismissAllInProgress; - private boolean mFadeNotificationsOnDismiss; private FooterDismissListener mFooterDismissListener; private boolean mFlingAfterUpEvent; @@ -1205,7 +1208,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable @ShadeViewRefactor(RefactorComponent.COORDINATOR) private void clampScrollPosition() { int scrollRange = getScrollRange(); - if (scrollRange < mOwnScrollY) { + if (scrollRange < mOwnScrollY && !mAmbientState.isDismissAllInProgress()) { boolean animateStackY = false; if (scrollRange < getScrollAmountToScrollBoundary() && mAnimateStackYForContentHeightChange) { @@ -1721,6 +1724,11 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER) public void dismissViewAnimated(View child, Runnable endRunnable, int delay, long duration) { + if (child instanceof SectionHeaderView) { + ((StackScrollerDecorView) child).setContentVisible( + false /* visible */, true /* animate */, endRunnable); + return; + } mSwipeHelper.dismissChild(child, 0, endRunnable, delay, true, duration, true /* isDismissAll */); } @@ -4062,6 +4070,19 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable runAnimationFinishedRunnables(); clearTransient(); clearHeadsUpDisappearRunning(); + + if (mAmbientState.isDismissAllInProgress()) { + setDismissAllInProgress(false); + + if (mShadeNeedsToClose) { + mShadeNeedsToClose = false; + postDelayed( + () -> { + mShadeController.animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE); + }, + DELAY_BEFORE_SHADE_CLOSE /* delayMillis */); + } + } } @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER) @@ -4243,7 +4264,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable final @ColorInt int textColor = Utils.getColorAttrDefaultColor(mContext, android.R.attr.textColorPrimary); mSectionsManager.setHeaderForegroundColor(textColor); - mFooterView.setTextColor(textColor); + mFooterView.updateColors(); mEmptyShadeView.setTextColor(textColor); } @@ -4430,6 +4451,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable public void setDismissAllInProgress(boolean dismissAllInProgress) { mDismissAllInProgress = dismissAllInProgress; mAmbientState.setDismissAllInProgress(dismissAllInProgress); + mController.getNoticationRoundessManager().setDismissAllInProgress(dismissAllInProgress); handleDismissAllClipping(); } @@ -4973,129 +4995,137 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable mHeadsUpAppearanceController = headsUpAppearanceController; } - @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) - @VisibleForTesting - void clearNotifications(@SelectedRows int selection, boolean closeShade) { - // animate-swipe all dismissable notifications, then animate the shade closed - int numChildren = getChildCount(); + private boolean isVisible(View child) { + boolean hasClipBounds = child.getClipBounds(mTmpRect); + return child.getVisibility() == View.VISIBLE + && (!hasClipBounds || mTmpRect.height() > 0); + } - final ArrayList<View> viewsToHide = new ArrayList<>(numChildren); - final ArrayList<ExpandableNotificationRow> viewsToRemove = new ArrayList<>(numChildren); - for (int i = 0; i < numChildren; i++) { - final View child = getChildAt(i); - if (child instanceof ExpandableNotificationRow) { - ExpandableNotificationRow row = (ExpandableNotificationRow) child; - boolean parentVisible = false; - boolean hasClipBounds = child.getClipBounds(mTmpRect); - if (includeChildInDismissAll(row, selection)) { - viewsToRemove.add(row); - if (child.getVisibility() == View.VISIBLE - && (!hasClipBounds || mTmpRect.height() > 0)) { - viewsToHide.add(child); - parentVisible = true; - } - } else if (child.getVisibility() == View.VISIBLE - && (!hasClipBounds || mTmpRect.height() > 0)) { - parentVisible = true; - } - List<ExpandableNotificationRow> children = row.getAttachedChildren(); - if (children != null) { - for (ExpandableNotificationRow childRow : children) { - if (includeChildInDismissAll(row, selection)) { - viewsToRemove.add(childRow); - if (parentVisible && row.areChildrenExpanded()) { - hasClipBounds = childRow.getClipBounds(mTmpRect); - if (childRow.getVisibility() == View.VISIBLE - && (!hasClipBounds || mTmpRect.height() > 0)) { - viewsToHide.add(childRow); - } - } - } - } - } + private boolean shouldHideParent(View view, @SelectedRows int selection) { + final boolean silentSectionWillBeGone = + !mController.hasNotifications(ROWS_GENTLE, false /* clearable */); + + // The only SectionHeaderView we have is the silent section header. + if (view instanceof SectionHeaderView && silentSectionWillBeGone) { + return true; + } + if (view instanceof ExpandableNotificationRow) { + ExpandableNotificationRow row = (ExpandableNotificationRow) view; + if (isVisible(row) && includeChildInDismissAll(row, selection)) { + return true; } } + return false; + } - if (mDismissListener != null) { - mDismissListener.onDismiss(selection); - } + private boolean isChildrenVisible(ExpandableNotificationRow parent) { + List<ExpandableNotificationRow> children = parent.getAttachedChildren(); + return isVisible(parent) + && children != null + && parent.areChildrenExpanded(); + } + + // Similar to #getRowsToDismissInBackend, but filters for visible views. + private ArrayList<View> getVisibleViewsToAnimateAway(@SelectedRows int selection) { + final int viewCount = getChildCount(); + final ArrayList<View> viewsToHide = new ArrayList<>(viewCount); + + for (int i = 0; i < viewCount; i++) { + final View view = getChildAt(i); - if (viewsToRemove.isEmpty()) { - if (closeShade && mShadeController != null) { - mShadeController.animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE); + if (shouldHideParent(view, selection)) { + viewsToHide.add(view); } - return; - } + if (view instanceof ExpandableNotificationRow) { + ExpandableNotificationRow parent = (ExpandableNotificationRow) view; - performDismissAllAnimations( - viewsToHide, - closeShade, - () -> onDismissAllAnimationsEnd(viewsToRemove, selection)); + if (isChildrenVisible(parent)) { + for (ExpandableNotificationRow child : parent.getAttachedChildren()) { + if (isVisible(child) && includeChildInDismissAll(child, selection)) { + viewsToHide.add(child); + } + } + } + } + } + return viewsToHide; } - private boolean includeChildInDismissAll( - ExpandableNotificationRow row, + private ArrayList<ExpandableNotificationRow> getRowsToDismissInBackend( @SelectedRows int selection) { - return canChildBeDismissed(row) && matchesSelection(row, selection); + final int childCount = getChildCount(); + final ArrayList<ExpandableNotificationRow> viewsToRemove = new ArrayList<>(childCount); + + for (int i = 0; i < childCount; i++) { + final View view = getChildAt(i); + if (!(view instanceof ExpandableNotificationRow)) { + continue; + } + ExpandableNotificationRow parent = (ExpandableNotificationRow) view; + if (includeChildInDismissAll(parent, selection)) { + viewsToRemove.add(parent); + } + List<ExpandableNotificationRow> children = parent.getAttachedChildren(); + if (isVisible(parent) && children != null) { + for (ExpandableNotificationRow child : children) { + if (includeChildInDismissAll(parent, selection)) { + viewsToRemove.add(child); + } + } + } + } + return viewsToRemove; } /** - * Given a list of rows, animates them away in a staggered fashion as if they were dismissed. - * Doesn't actually dismiss them, though -- that must be done in the onAnimationComplete - * handler. - * - * @param hideAnimatedList List of rows to animated away. Should only be views that are - * currently visible, or else the stagger will look funky. - * @param closeShade Whether to close the shade after the stagger animation completes. - * @param onAnimationComplete Called after the entire animation completes (including the shade - * closing if appropriate). The rows must be dismissed for real here. + * Collects a list of visible rows, and animates them away in a staggered fashion as if they + * were dismissed. Notifications are dismissed in the backend via onDismissAllAnimationsEnd. */ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) - private void performDismissAllAnimations( - final ArrayList<View> hideAnimatedList, - final boolean closeShade, - final Runnable onAnimationComplete) { - - final Runnable onSlideAwayAnimationComplete = () -> { - if (closeShade) { - mShadeController.addPostCollapseAction(() -> { - setDismissAllInProgress(false); - onAnimationComplete.run(); - }); - mShadeController.animateCollapsePanels( - CommandQueue.FLAG_EXCLUDE_NONE); - } else { - setDismissAllInProgress(false); - onAnimationComplete.run(); - } + @VisibleForTesting + void clearNotifications(@SelectedRows int selection, boolean closeShade) { + // Animate-swipe all dismissable notifications, then animate the shade closed + final ArrayList<View> viewsToAnimateAway = getVisibleViewsToAnimateAway(selection); + final ArrayList<ExpandableNotificationRow> rowsToDismissInBackend = + getRowsToDismissInBackend(selection); + if (mDismissListener != null) { + mDismissListener.onDismiss(selection); + } + final Runnable dismissInBackend = () -> { + onDismissAllAnimationsEnd(rowsToDismissInBackend, selection); }; - - if (hideAnimatedList.isEmpty()) { - onSlideAwayAnimationComplete.run(); + if (viewsToAnimateAway.isEmpty()) { + dismissInBackend.run(); return; } - - // let's disable our normal animations + // Disable normal animations setDismissAllInProgress(true); + mShadeNeedsToClose = closeShade; // Decrease the delay for every row we animate to give the sense of // accelerating the swipes - int rowDelayDecrement = 10; - int currentDelay = 140; - int totalDelay = 180; - int numItems = hideAnimatedList.size(); + final int rowDelayDecrement = 5; + int currentDelay = 60; + int totalDelay = 0; + final int numItems = viewsToAnimateAway.size(); for (int i = numItems - 1; i >= 0; i--) { - View view = hideAnimatedList.get(i); + View view = viewsToAnimateAway.get(i); Runnable endRunnable = null; if (i == 0) { - endRunnable = onSlideAwayAnimationComplete; + endRunnable = dismissInBackend; } dismissViewAnimated(view, endRunnable, totalDelay, ANIMATION_DURATION_SWIPE); - currentDelay = Math.max(50, currentDelay - rowDelayDecrement); + currentDelay = Math.max(30, currentDelay - rowDelayDecrement); totalDelay += currentDelay; } } + private boolean includeChildInDismissAll( + ExpandableNotificationRow row, + @SelectedRows int selection) { + return canChildBeDismissed(row) && matchesSelection(row, selection); + } + /** Register a {@link View.OnClickListener} to be invoked when the Manage button is clicked. */ public void setManageButtonClickListener(@Nullable OnClickListener listener) { mManageButtonClickListener = listener; @@ -5114,6 +5144,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable mFooterDismissListener.onDismiss(); } clearNotifications(ROWS_ALL, true /* closeShade */); + footerView.setSecondaryVisible(false /* visible */, true /* animate */); }); setFooterView(footerView); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java index 04129b52b4ea..f85c749b1f19 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java @@ -1152,6 +1152,10 @@ public class NotificationStackScrollLayoutController { mZenModeController.areNotificationsHiddenInShade()); } + public boolean areNotificationsHiddenInShade() { + return mZenModeController.areNotificationsHiddenInShade(); + } + public boolean isShowingEmptyShadeView() { return mShowEmptyShadeView; } @@ -1200,6 +1204,10 @@ public class NotificationStackScrollLayoutController { * Return whether there are any clearable notifications */ public boolean hasActiveClearableNotifications(@SelectedRows int selection) { + return hasNotifications(selection, true /* clearable */); + } + + public boolean hasNotifications(@SelectedRows int selection, boolean isClearable) { if (mDynamicPrivacyController.isInLockedDownShade()) { return false; } @@ -1210,8 +1218,11 @@ public class NotificationStackScrollLayoutController { continue; } final ExpandableNotificationRow row = (ExpandableNotificationRow) child; - if (row.canViewBeDismissed() && - NotificationStackScrollLayout.matchesSelection(row, selection)) { + final boolean matchClearable = + isClearable ? row.canViewBeDismissed() : !row.canViewBeDismissed(); + final boolean inSection = + NotificationStackScrollLayout.matchesSelection(row, selection); + if (matchClearable && inSection) { return true; } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java index 2c810c93b2ee..8be5de7ae56e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java @@ -366,6 +366,20 @@ public class StackScrollAlgorithm { return stackHeight / stackEndHeight; } + public boolean hasOngoingNotifs(StackScrollAlgorithmState algorithmState) { + for (int i = 0; i < algorithmState.visibleChildren.size(); i++) { + View child = algorithmState.visibleChildren.get(i); + if (!(child instanceof ExpandableNotificationRow)) { + continue; + } + final ExpandableNotificationRow row = (ExpandableNotificationRow) child; + if (!row.canViewBeDismissed()) { + return true; + } + } + return false; + } + // TODO(b/172289889) polish shade open from HUN /** * Populates the {@link ExpandableViewState} for a single child. @@ -430,7 +444,9 @@ public class StackScrollAlgorithm { + view.getIntrinsicHeight(); final boolean noSpaceForFooter = footerEnd > ambientState.getStackEndHeight(); ((FooterView.FooterViewState) viewState).hideContent = - isShelfShowing || noSpaceForFooter; + isShelfShowing || noSpaceForFooter + || (ambientState.isDismissAllInProgress() + && !hasOngoingNotifs(algorithmState)); } } else { if (view != ambientState.getTrackedHeadsUpRow()) { @@ -467,7 +483,6 @@ public class StackScrollAlgorithm { } } } - // Clip height of view right before shelf. viewState.height = (int) (getMaxAllowedChildHeight(view) * expansionFraction); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java index 2702bf7d31da..e3a4bf0170fb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java @@ -46,7 +46,7 @@ public class StackStateAnimator { public static final int ANIMATION_DURATION_WAKEUP = 500; public static final int ANIMATION_DURATION_GO_TO_FULL_SHADE = 448; public static final int ANIMATION_DURATION_APPEAR_DISAPPEAR = 464; - public static final int ANIMATION_DURATION_SWIPE = 260; + public static final int ANIMATION_DURATION_SWIPE = 200; public static final int ANIMATION_DURATION_DIMMED_ACTIVATED = 220; public static final int ANIMATION_DURATION_CLOSE_REMOTE_INPUT = 150; public static final int ANIMATION_DURATION_HEADS_UP_APPEAR = 400; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java index 7610e6138ebb..96c405866a68 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java @@ -42,6 +42,8 @@ import com.android.systemui.animation.Interpolators; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.CommandQueue; +import com.android.systemui.statusbar.OperatorNameView; +import com.android.systemui.statusbar.OperatorNameViewController; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.events.SystemStatusAnimationCallback; import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler; @@ -90,8 +92,8 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue private int mDisabled2; private Lazy<Optional<StatusBar>> mStatusBarOptionalLazy; private DarkIconManager mDarkIconManager; - private View mOperatorNameFrame; private final CommandQueue mCommandQueue; + private final OperatorNameViewController.Factory mOperatorNameViewControllerFactory; private final OngoingCallController mOngoingCallController; private final SystemStatusAnimationScheduler mAnimationScheduler; private final StatusBarLocationPublisher mLocationPublisher; @@ -114,6 +116,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue disable(getContext().getDisplayId(), mDisabled1, mDisabled2, animate); } }; + private OperatorNameViewController mOperatorNameViewController; @Inject public CollapsedStatusBarFragment( @@ -127,7 +130,8 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue KeyguardStateController keyguardStateController, NetworkController networkController, StatusBarStateController statusBarStateController, - CommandQueue commandQueue + CommandQueue commandQueue, + OperatorNameViewController.Factory operatorNameViewControllerFactory ) { mOngoingCallController = ongoingCallController; mAnimationScheduler = animationScheduler; @@ -140,6 +144,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue mNetworkController = networkController; mStatusBarStateController = statusBarStateController; mCommandQueue = commandQueue; + mOperatorNameViewControllerFactory = operatorNameViewControllerFactory; } @Override @@ -411,14 +416,14 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue } public void hideOperatorName(boolean animate) { - if (mOperatorNameFrame != null) { - animateHide(mOperatorNameFrame, animate); + if (mOperatorNameViewController != null) { + animateHide(mOperatorNameViewController.getView(), animate); } } public void showOperatorName(boolean animate) { - if (mOperatorNameFrame != null) { - animateShow(mOperatorNameFrame, animate); + if (mOperatorNameViewController != null) { + animateShow(mOperatorNameViewController.getView(), animate); } } @@ -495,7 +500,9 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue private void initOperatorName() { if (getResources().getBoolean(R.bool.config_showOperatorNameInStatusBar)) { ViewStub stub = mStatusBar.findViewById(R.id.operator_name); - mOperatorNameFrame = stub.inflate(); + mOperatorNameViewController = + mOperatorNameViewControllerFactory.create((OperatorNameView) stub.inflate()); + mOperatorNameViewController.init(); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java index 7908d84bcd70..84b8f52c23ac 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java @@ -268,6 +268,13 @@ public class DozeParameters implements TunerService.Tunable, } /** + * Whether the brightness sensor uses the proximity sensor. + */ + public boolean brightnessUsesProx() { + return mResources.getBoolean(R.bool.doze_brightness_uses_prox); + } + + /** * Callback to listen for DozeParameter changes. */ public void addCallback(Callback callback) { @@ -303,6 +310,7 @@ public class DozeParameters implements TunerService.Tunable, pw.print("getPickupVibrationThreshold(): "); pw.println(getPickupVibrationThreshold()); pw.print("getSelectivelyRegisterSensorsUsingProx(): "); pw.println(getSelectivelyRegisterSensorsUsingProx()); + pw.print("brightnessUsesProx(): "); pw.println(brightnessUsesProx()); } interface Callback { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java index b2cf72aca864..21c3e5e0a8d0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java @@ -116,11 +116,18 @@ public class DozeScrimController implements StateListener { if (!mDozing || mPulseCallback != null) { if (DEBUG) { - Log.d(TAG, "Pulse supressed. Dozing: " + mDozeParameters + " had callback? " + Log.d(TAG, "Pulse suppressed. Dozing: " + mDozeParameters + " had callback? " + (mPulseCallback != null)); } // Pulse suppressed. callback.onPulseFinished(); + if (!mDozing) { + mDozeLog.tracePulseDropped("device isn't dozing"); + } else { + mDozeLog.tracePulseDropped("already has pulse callback mPulseCallback=" + + mPulseCallback); + } + return; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java index 0a4e59c0391e..c639eecf037c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java @@ -80,6 +80,7 @@ import com.android.settingslib.Utils; import com.android.systemui.ActivityIntentHelper; import com.android.systemui.Dependency; import com.android.systemui.R; +import com.android.systemui.animation.ActivityLaunchAnimator; import com.android.systemui.animation.Interpolators; import com.android.systemui.assist.AssistManager; import com.android.systemui.camera.CameraIntents; @@ -203,13 +204,15 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL private ControlsListingController.ControlsListingCallback mListingCallback = new ControlsListingController.ControlsListingCallback() { public void onServicesUpdated(List<ControlsServiceInfo> serviceInfos) { - boolean available = !serviceInfos.isEmpty(); + post(() -> { + boolean available = !serviceInfos.isEmpty(); - if (available != mControlServicesAvailable) { - mControlServicesAvailable = available; - updateControlsVisibility(); - updateAffordanceColors(); - } + if (available != mControlServicesAvailable) { + mControlServicesAvailable = available; + updateControlsVisibility(); + updateAffordanceColors(); + } + }); } }; @@ -1044,11 +1047,13 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL return; } + ActivityLaunchAnimator.Controller animationController = createLaunchAnimationController(v); if (mHasCard) { Intent intent = new Intent(mContext, WalletActivity.class) .setAction(Intent.ACTION_VIEW) .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK); - mContext.startActivity(intent); + mActivityStarter.startActivity(intent, true /* dismissShade */, animationController, + true /* showOverLockscreenWhenLocked */); } else { if (mQuickAccessWalletController.getWalletClient().createWalletIntent() == null) { Log.w(TAG, "Could not get intent of the wallet app."); @@ -1056,10 +1061,14 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL } mActivityStarter.postStartActivityDismissingKeyguard( mQuickAccessWalletController.getWalletClient().createWalletIntent(), - /* delay= */ 0); + /* delay= */ 0, animationController); } } + protected ActivityLaunchAnimator.Controller createLaunchAnimationController(View view) { + return ActivityLaunchAnimator.Controller.fromView(view, null); + } + private void onControlsClick(View v) { if (mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) { return; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java index a73cad7d2c6d..df4bbcfc46bb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java @@ -321,7 +321,7 @@ public class KeyguardStatusBarView extends RelativeLayout { } } - public void setKeyguardUserSwitcherEnabled(boolean enabled) { + void setKeyguardUserSwitcherEnabled(boolean enabled) { mKeyguardUserSwitcherEnabled = enabled; } @@ -475,8 +475,10 @@ public class KeyguardStatusBarView extends RelativeLayout { /** * Set the clipping on the top of the view. + * + * Should only be called from {@link KeyguardStatusBarViewController}. */ - public void setTopClipping(int topClipping) { + void setTopClipping(int topClipping) { if (topClipping != mTopClipping) { mTopClipping = topClipping; updateClipping(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java index 8770e86df735..ef3dcedf8315 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java @@ -159,17 +159,6 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat @Override protected void onViewDetached() { - destroy(); - } - - @Override - public void destroy() { - // Don't receive future #onViewAttached calls so that we don't accidentally have two - // controllers registered for the same view. - // TODO(b/194181195): This shouldn't be necessary. - super.destroy(); - mBatteryMeterViewController.destroy(); - mConfigurationController.removeCallback(mConfigurationListener); mAnimationScheduler.removeCallback(mAnimationCallback); mUserInfoController.removeCallback(mOnUserInfoChangedListener); @@ -183,6 +172,11 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat mView.onThemeChanged(mTintedIconManager); } + /** Sets whether user switcher is enabled. */ + public void setKeyguardUserSwitcherEnabled(boolean enabled) { + mView.setKeyguardUserSwitcherEnabled(enabled); + } + /** Sets whether this controller should listen to battery updates. */ public void setBatteryListening(boolean listening) { if (listening == mBatteryListening) { @@ -196,6 +190,21 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat } } + /** Set the view to have no top clipping. */ + public void setNoTopClipping() { + mView.setTopClipping(0); + } + + /** + * Update the view's top clipping based on the value of notificationPanelTop and the view's + * current top. + * + * @param notificationPanelTop the current top of the notification panel view. + */ + public void updateTopClipping(int notificationPanelTop) { + mView.setTopClipping(notificationPanelTop - mView.getTop()); + } + /** */ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { pw.println("KeyguardStatusBarView:"); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenGestureLogger.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenGestureLogger.java index 094ebb9ef0a7..5f222afd77e5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenGestureLogger.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenGestureLogger.java @@ -24,7 +24,6 @@ import com.android.internal.logging.UiEvent; import com.android.internal.logging.UiEventLogger; import com.android.internal.logging.UiEventLoggerImpl; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; -import com.android.systemui.Dependency; import com.android.systemui.EventLogConstants; import com.android.systemui.EventLogTags; import com.android.systemui.dagger.SysUISingleton; @@ -88,10 +87,11 @@ public class LockscreenGestureLogger { } private ArrayMap<Integer, Integer> mLegacyMap; - private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class); + private final MetricsLogger mMetricsLogger; @Inject - public LockscreenGestureLogger() { + public LockscreenGestureLogger(MetricsLogger metricsLogger) { + mMetricsLogger = metricsLogger; mLegacyMap = new ArrayMap<>(EventLogConstants.METRICS_GESTURE_TYPE_MAP.length); for (int i = 0; i < EventLogConstants.METRICS_GESTURE_TYPE_MAP.length ; i++) { mLegacyMap.put(EventLogConstants.METRICS_GESTURE_TYPE_MAP[i], i); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java index 30cb1dc9bd16..39dc5403ac7c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java @@ -482,7 +482,6 @@ public class NotificationPanelViewController extends PanelViewController { private float mLinearDarkAmount; private boolean mPulsing; - private LockscreenGestureLogger mLockscreenGestureLogger = new LockscreenGestureLogger(); private boolean mUserSetupComplete; private int mQsNotificationTopPadding; private boolean mHideIconsDuringLaunchAnimation = true; @@ -653,7 +652,7 @@ public class NotificationPanelViewController extends PanelViewController { private KeyguardMediaController mKeyguardMediaController; - private boolean mStatusViewCentered = false; + private boolean mStatusViewCentered = true; private View.AccessibilityDelegate mAccessibilityDelegate = new View.AccessibilityDelegate() { @Override @@ -742,12 +741,21 @@ public class NotificationPanelViewController extends PanelViewController { SplitShadeHeaderController splitShadeHeaderController, LockscreenSmartspaceController lockscreenSmartspaceController, UnlockedScreenOffAnimationController unlockedScreenOffAnimationController, + LockscreenGestureLogger lockscreenGestureLogger, NotificationRemoteInputManager remoteInputManager, ControlsComponent controlsComponent) { - super(view, falsingManager, dozeLog, keyguardStateController, - (SysuiStatusBarStateController) statusBarStateController, vibratorHelper, - statusBarKeyguardViewManager, latencyTracker, flingAnimationUtilsBuilder.get(), - statusBarTouchableRegionManager, ambientState); + super(view, + falsingManager, + dozeLog, + keyguardStateController, + (SysuiStatusBarStateController) statusBarStateController, + vibratorHelper, + statusBarKeyguardViewManager, + latencyTracker, + flingAnimationUtilsBuilder.get(), + statusBarTouchableRegionManager, + lockscreenGestureLogger, + ambientState); mView = view; mVibratorHelper = vibratorHelper; mKeyguardMediaController = keyguardMediaController; @@ -884,10 +892,14 @@ public class NotificationPanelViewController extends PanelViewController { } } + mKeyguardStatusBarViewController = + mKeyguardStatusBarViewComponentFactory.build(mKeyguardStatusBar) + .getKeyguardStatusBarViewController(); + mKeyguardStatusBarViewController.init(); + updateViewControllers( mView.findViewById(R.id.keyguard_status_view), userAvatarView, - mKeyguardStatusBar, keyguardUserSwitcherView); mNotificationContainerParent = mView.findViewById(R.id.notification_container_parent); NotificationStackScrollLayout stackScrollLayout = mView.findViewById( @@ -979,7 +991,6 @@ public class NotificationPanelViewController extends PanelViewController { private void updateViewControllers(KeyguardStatusView keyguardStatusView, UserAvatarView userAvatarView, - KeyguardStatusBarView keyguardStatusBarView, KeyguardUserSwitcherView keyguardUserSwitcherView) { // Re-associate the KeyguardStatusViewController KeyguardStatusViewComponent statusViewComponent = @@ -987,16 +998,6 @@ public class NotificationPanelViewController extends PanelViewController { mKeyguardStatusViewController = statusViewComponent.getKeyguardStatusViewController(); mKeyguardStatusViewController.init(); - KeyguardStatusBarViewComponent statusBarViewComponent = - mKeyguardStatusBarViewComponentFactory.build(keyguardStatusBarView); - if (mKeyguardStatusBarViewController != null) { - // TODO(b/194181195): This shouldn't be necessary. - mKeyguardStatusBarViewController.destroy(); - } - mKeyguardStatusBarViewController = - statusBarViewComponent.getKeyguardStatusBarViewController(); - mKeyguardStatusBarViewController.init(); - if (mKeyguardUserSwitcherController != null) { // Try to close the switcher so that callbacks are triggered if necessary. // Otherwise, NPV can get into a state where some of the views are still hidden @@ -1014,16 +1015,16 @@ public class NotificationPanelViewController extends PanelViewController { userSwitcherComponent.getKeyguardQsUserSwitchController(); mKeyguardQsUserSwitchController.setNotificationPanelViewController(this); mKeyguardQsUserSwitchController.init(); - mKeyguardStatusBar.setKeyguardUserSwitcherEnabled(true); + mKeyguardStatusBarViewController.setKeyguardUserSwitcherEnabled(true); } else if (keyguardUserSwitcherView != null) { KeyguardUserSwitcherComponent userSwitcherComponent = mKeyguardUserSwitcherComponentFactory.build(keyguardUserSwitcherView); mKeyguardUserSwitcherController = userSwitcherComponent.getKeyguardUserSwitcherController(); mKeyguardUserSwitcherController.init(); - mKeyguardStatusBar.setKeyguardUserSwitcherEnabled(true); + mKeyguardStatusBarViewController.setKeyguardUserSwitcherEnabled(true); } else { - mKeyguardStatusBar.setKeyguardUserSwitcherEnabled(false); + mKeyguardStatusBarViewController.setKeyguardUserSwitcherEnabled(false); } } @@ -1081,7 +1082,7 @@ public class NotificationPanelViewController extends PanelViewController { constraintSet.applyTo(mNotificationContainerParent); mNotificationContainerParent.setSplitShadeEnabled(mShouldUseSplitNotificationShade); - updateKeyguardStatusViewAlignment(false /* animate */); + updateKeyguardStatusViewAlignment(/* animate= */false); mLockscreenSmartspaceController.onSplitShadeChanged(mShouldUseSplitNotificationShade); mKeyguardMediaController.refreshMediaPosition(); } @@ -1128,6 +1129,9 @@ public class NotificationPanelViewController extends PanelViewController { keyguardStatusView = (KeyguardStatusView) mLayoutInflater.inflate( R.layout.keyguard_status_view, mNotificationContainerParent, false); mNotificationContainerParent.addView(keyguardStatusView, statusIndex); + // When it's reinflated, this is centered by default. If it shouldn't be, this will update + // below when resources are updated. + mStatusViewCentered = true; attachSplitShadeMediaPlayerContainer( keyguardStatusView.findViewById(R.id.status_view_media_container)); @@ -1156,7 +1160,7 @@ public class NotificationPanelViewController extends PanelViewController { mBigClockContainer.removeAllViews(); updateViewControllers(mView.findViewById(R.id.keyguard_status_view), userAvatarView, - mKeyguardStatusBar, keyguardUserSwitcherView); + keyguardUserSwitcherView); // Update keyguard bottom area int index = mView.indexOfChild(mKeyguardBottomArea); @@ -2445,7 +2449,6 @@ public class NotificationPanelViewController extends PanelViewController { boolean qsVisible) { // Fancy clipping for quick settings int radius = mScrimCornerRadius; - int statusBarClipTop = 0; boolean clipStatusView = false; if (!mShouldUseSplitNotificationShade) { // The padding on this area is large enough that we can use a cheaper clipping strategy @@ -2454,7 +2457,6 @@ public class NotificationPanelViewController extends PanelViewController { float screenCornerRadius = mRecordingController.isRecording() ? 0 : mScreenCornerRadius; radius = (int) MathUtils.lerp(screenCornerRadius, mScrimCornerRadius, Math.min(top / (float) mScrimCornerRadius, 1f)); - statusBarClipTop = top - mKeyguardStatusBar.getTop(); } if (mQs != null) { float qsTranslation = 0; @@ -2492,8 +2494,13 @@ public class NotificationPanelViewController extends PanelViewController { mScrimController.setNotificationsBounds(left, top, right, bottom); } + if (mShouldUseSplitNotificationShade) { + mKeyguardStatusBarViewController.setNoTopClipping(); + } else { + mKeyguardStatusBarViewController.updateTopClipping(top); + } + mScrimController.setScrimCornerRadius(radius); - mKeyguardStatusBar.setTopClipping(statusBarClipTop); int nsslLeft = left - mNotificationStackScrollLayoutController.getLeft(); int nsslRight = right - mNotificationStackScrollLayoutController.getLeft(); int nsslTop = top - mNotificationStackScrollLayoutController.getTop(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java index c52a094d4b57..030a8951943d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java @@ -606,12 +606,11 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW } @Override - public void setLightRevealScrimAmount(float amount) { - boolean lightRevealScrimOpaque = amount == 0; - if (mCurrentState.mLightRevealScrimOpaque == lightRevealScrimOpaque) { + public void setLightRevealScrimOpaque(boolean opaque) { + if (mCurrentState.mLightRevealScrimOpaque == opaque) { return; } - mCurrentState.mLightRevealScrimOpaque = lightRevealScrimOpaque; + mCurrentState.mLightRevealScrimOpaque = opaque; apply(mCurrentState); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java index b5d9bd67bd2d..e57e2005e3b5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java @@ -311,6 +311,12 @@ public class NotificationShadeWindowViewController { // Capture all touch events in always-on. return true; } + + if (mStatusBarKeyguardViewManager.isShowingAlternateAuthOrAnimating()) { + // capture all touches if the alt auth bouncer is showing + return true; + } + boolean intercept = false; if (mNotificationPanelViewController.isFullyExpanded() && mDragDownHelper.isDragDownEnabled() @@ -338,6 +344,12 @@ public class NotificationShadeWindowViewController { if (mStatusBarStateController.isDozing()) { handled = !mService.isPulsing(); } + + if (mStatusBarKeyguardViewManager.isShowingAlternateAuthOrAnimating()) { + // eat the touch + handled = true; + } + if ((mDragDownHelper.isDragDownEnabled() && !handled) || mDragDownHelper.isDraggingDown()) { // we still want to finish our drag down gesture when locking the screen diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java index 27f08a860f28..a3877b0eb5e6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java @@ -79,7 +79,6 @@ public abstract class PanelViewController { protected long mDownTime; protected boolean mTouchSlopExceededBeforeDown; private float mMinExpandHeight; - private LockscreenGestureLogger mLockscreenGestureLogger = new LockscreenGestureLogger(); private boolean mPanelUpdateWhenAnimatorEnds; private boolean mVibrateOnOpening; protected boolean mIsLaunchAnimationRunning; @@ -182,6 +181,7 @@ public abstract class PanelViewController { protected final KeyguardStateController mKeyguardStateController; protected final SysuiStatusBarStateController mStatusBarStateController; protected final AmbientState mAmbientState; + protected final LockscreenGestureLogger mLockscreenGestureLogger; protected void onExpandingFinished() { mBar.onExpandingFinished(); @@ -217,10 +217,12 @@ public abstract class PanelViewController { LatencyTracker latencyTracker, FlingAnimationUtils.Builder flingAnimationUtilsBuilder, StatusBarTouchableRegionManager statusBarTouchableRegionManager, + LockscreenGestureLogger lockscreenGestureLogger, AmbientState ambientState) { mAmbientState = ambientState; mView = view; mStatusBarKeyguardViewManager = statusBarKeyguardViewManager; + mLockscreenGestureLogger = lockscreenGestureLogger; mView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() { @Override public void onViewAttachedToWindow(View v) { @@ -1211,10 +1213,14 @@ public abstract class PanelViewController { case MotionEvent.ACTION_MOVE: final float h = y - mInitialTouchY; addMovement(event); - if (canCollapsePanel || mTouchStartedInEmptyArea || mAnimatingOnDown) { + final boolean openShadeWithoutHun = + mPanelClosedOnDown && !mCollapsedAndHeadsUpOnDown; + if (canCollapsePanel || mTouchStartedInEmptyArea || mAnimatingOnDown + || openShadeWithoutHun) { float hAbs = Math.abs(h); float touchSlop = getTouchSlop(event); - if ((h < -touchSlop || (mAnimatingOnDown && hAbs > touchSlop)) + if ((h < -touchSlop + || ((openShadeWithoutHun || mAnimatingOnDown) && hAbs > touchSlop)) && hAbs > Math.abs(x - mInitialTouchX)) { cancelHeightAnimator(); startExpandMotion(x, y, true /* startTracking */, mExpandedHeight); @@ -1227,10 +1233,7 @@ public abstract class PanelViewController { mVelocityTracker.clear(); break; } - - // Finally, if none of the above cases applies, ensure that touches do not get handled - // by the contents of a panel that is not showing (a bit of a hack to avoid b/178277858) - return (mView.getVisibility() != View.VISIBLE); + return false; } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java index 43a8630e2791..e3319e5ef200 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java @@ -111,6 +111,20 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump */ private boolean mTransitioningToFullShade; + /** + * Is there currently an unocclusion animation running. Used to avoid bright flickers + * of the notification scrim. + */ + private boolean mUnOcclusionAnimationRunning; + + /** + * Set whether an unocclusion animation is currently running on the notification panel. Used + * to avoid bright flickers of the notification scrim. + */ + public void setUnocclusionAnimationRunning(boolean unocclusionAnimationRunning) { + mUnOcclusionAnimationRunning = unocclusionAnimationRunning; + } + @IntDef(prefix = {"VISIBILITY_"}, value = { TRANSPARENT, SEMI_TRANSPARENT, @@ -436,6 +450,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump public void onExpandingFinished() { mTracking = false; + setUnocclusionAnimationRunning(false); } @VisibleForTesting @@ -672,6 +687,17 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump mNotificationsTint = mState.getNotifTint(); mBehindTint = behindTint; } + + // At the end of a launch animation over the lockscreen, the state is either KEYGUARD or + // SHADE_LOCKED and this code is called. We have to set the notification alpha to 0 + // otherwise there is a flicker to its previous value. + if (mKeyguardOccluded) { + mNotificationsAlpha = 0; + } + if (mUnOcclusionAnimationRunning && mState == ScrimState.KEYGUARD) { + // We're unoccluding the keyguard and don't want to have a bright flash. + mNotificationsAlpha = 0f; + } } assertAlphasValid(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java index edacbe1b9696..768222d34862 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java @@ -118,10 +118,6 @@ public class ShadeControllerImpl implements ShadeController { + " flags=" + flags); } - if ((flags & CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL) == 0) { - getStatusBar().postHideRecentApps(); - } - // TODO(b/62444020): remove when this bug is fixed Log.v(TAG, "NotificationShadeWindow: " + getNotificationShadeWindowView() + " canPanelBeCollapsed(): " diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java index cbc852b47076..0cfdf79b5497 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -51,6 +51,7 @@ import android.app.NotificationManager; import android.app.PendingIntent; import android.app.StatusBarManager; import android.app.TaskInfo; +import android.app.TaskStackBuilder; import android.app.UiModeManager; import android.app.WallpaperInfo; import android.app.WallpaperManager; @@ -73,7 +74,6 @@ import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.os.Looper; -import android.os.Message; import android.os.PowerManager; import android.os.RemoteException; import android.os.ServiceManager; @@ -143,6 +143,7 @@ import com.android.systemui.camera.CameraIntents; import com.android.systemui.charging.WirelessChargingAnimation; import com.android.systemui.classifier.FalsingCollector; import com.android.systemui.colorextraction.SysuiColorExtractor; +import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dagger.qualifiers.UiBackground; import com.android.systemui.demomode.DemoModeController; import com.android.systemui.emergency.EmergencyGesture; @@ -189,6 +190,7 @@ import com.android.systemui.statusbar.NotificationShadeDepthController; import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.NotificationShelfController; import com.android.systemui.statusbar.NotificationViewHierarchyManager; +import com.android.systemui.statusbar.OperatorNameViewController; import com.android.systemui.statusbar.PowerButtonReveal; import com.android.systemui.statusbar.PulseExpansionHandler; import com.android.systemui.statusbar.StatusBarState; @@ -197,6 +199,7 @@ import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler; import com.android.systemui.statusbar.notification.DynamicPrivacyController; import com.android.systemui.statusbar.notification.NotificationActivityStarter; +import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.NotificationLaunchAnimatorControllerProvider; import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager; @@ -223,11 +226,13 @@ import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.NetworkController; import com.android.systemui.statusbar.policy.UserInfoControllerImpl; import com.android.systemui.statusbar.policy.UserSwitcherController; -import com.android.systemui.unfold.UnfoldLightRevealOverlayAnimation; import com.android.systemui.tuner.TunerService; +import com.android.systemui.unfold.UnfoldLightRevealOverlayAnimation; +import com.android.systemui.util.concurrency.DelayableExecutor; +import com.android.systemui.util.concurrency.MessageRouter; import com.android.systemui.volume.VolumeComponent; import com.android.systemui.wmshell.BubblesManager; -import com.android.unfold.config.UnfoldTransitionConfig; +import com.android.systemui.unfold.config.UnfoldTransitionConfig; import com.android.wm.shell.bubbles.Bubbles; import com.android.wm.shell.legacysplitscreen.LegacySplitScreen; import com.android.wm.shell.startingsurface.SplashscreenContentDrawer; @@ -250,16 +255,9 @@ import dagger.Lazy; /** */ public class StatusBar extends SystemUI implements ActivityStarter, - ConfigurationListener, - StatusBarStateController.StateListener, - LifecycleOwner, BatteryController.BatteryStateChangeCallback, - ActivityLaunchAnimator.Callback { + LifecycleOwner { public static final boolean MULTIUSER_DEBUG = false; - protected static final int MSG_HIDE_RECENT_APPS = 1020; - protected static final int MSG_PRELOAD_RECENT_APPS = 1022; - protected static final int MSG_CANCEL_PRELOAD_RECENT_APPS = 1023; - protected static final int MSG_TOGGLE_KEYBOARD_SHORTCUTS_MENU = 1026; protected static final int MSG_DISMISS_KEYBOARD_SHORTCUTS_MENU = 1027; // Should match the values in PhoneWindowManager @@ -287,8 +285,6 @@ public class StatusBar extends SystemUI implements public static final String ACTION_FAKE_ARTWORK = "fake_artwork"; - private static final int MSG_OPEN_NOTIFICATION_PANEL = 1000; - private static final int MSG_CLOSE_PANELS = 1001; private static final int MSG_OPEN_SETTINGS_PANEL = 1002; private static final int MSG_LAUNCH_TRANSITION_TIMEOUT = 1003; // 1020-1040 reserved for BaseStatusBar @@ -368,13 +364,14 @@ public class StatusBar extends SystemUI implements mWereIconsJustHidden = justHidden; } - void resetHandlerMsg(int msg) { - mHandler.removeMessages(msg); - mHandler.sendEmptyMessage(msg); + void resendMessage(int msg) { + mMessageRouter.cancelMessages(msg); + mMessageRouter.sendMessage(msg); } - Handler getHandler() { - return mHandler; + void resendMessage(Object msg) { + mMessageRouter.cancelMessages(msg.getClass()); + mMessageRouter.sendMessage(msg); } int getDisabled1() { @@ -454,6 +451,7 @@ public class StatusBar extends SystemUI implements private BiometricUnlockController mBiometricUnlockController; private final LightBarController mLightBarController; private final Lazy<LockscreenWallpaper> mLockscreenWallpaperLazy; + private final LockscreenGestureLogger mLockscreenGestureLogger; @Nullable protected LockscreenWallpaper mLockscreenWallpaper; private final AutoHideController mAutoHideController; @@ -521,6 +519,7 @@ public class StatusBar extends SystemUI implements // settings private QSPanelController mQSPanelController; + private final OperatorNameViewController.Factory mOperatorNameViewControllerFactory; KeyguardIndicationController mKeyguardIndicationController; private View mReportRejectedTouch; @@ -529,6 +528,7 @@ public class StatusBar extends SystemUI implements private final int[] mAbsPos = new int[2]; + protected final NotificationEntryManager mEntryManager; private final NotificationGutsManager mGutsManager; private final NotificationLogger mNotificationLogger; private final NotificationViewHierarchyManager mViewHierarchyManager; @@ -539,6 +539,7 @@ public class StatusBar extends SystemUI implements private final UnfoldTransitionConfig mUnfoldTransitionConfig; private final Lazy<UnfoldLightRevealOverlayAnimation> mUnfoldLightRevealOverlayAnimation; private final KeyguardUnlockAnimationController mKeyguardUnlockAnimationController; + private final MessageRouter mMessageRouter; private final WallpaperManager mWallpaperManager; private final UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController; private final TunerService mTunerService; @@ -569,29 +570,6 @@ public class StatusBar extends SystemUI implements // ensure quick settings is disabled until the current user makes it through the setup wizard @VisibleForTesting protected boolean mUserSetup = false; - private final DeviceProvisionedListener mUserSetupObserver = new DeviceProvisionedListener() { - @Override - public void onUserSetupChanged() { - final boolean userSetup = mDeviceProvisionedController.isUserSetup( - mDeviceProvisionedController.getCurrentUser()); - Log.d(TAG, "mUserSetupObserver - DeviceProvisionedListener called for user " - + mDeviceProvisionedController.getCurrentUser()); - if (MULTIUSER_DEBUG) { - Log.d(TAG, String.format("User setup changed: userSetup=%s mUserSetup=%s", - userSetup, mUserSetup)); - } - - if (userSetup != mUserSetup) { - mUserSetup = userSetup; - if (!mUserSetup && mStatusBarView != null) - animateCollapseQuickSettings(); - if (mNotificationPanelViewController != null) { - mNotificationPanelViewController.setUserSetupComplete(mUserSetup); - } - updateQsExpansionEnabled(); - } - } - }; @VisibleForTesting public enum StatusBarUiEvent implements UiEventLogger.UiEventEnum { @@ -631,7 +609,8 @@ public class StatusBar extends SystemUI implements } } - protected final H mHandler = createHandler(); + private Handler mMainHandler; + private final DelayableExecutor mMainExecutor; private int mInteractingWindows; private @TransitionMode int mStatusBarMode; @@ -648,33 +627,6 @@ public class StatusBar extends SystemUI implements private final NotificationRemoteInputManager mRemoteInputManager; private boolean mWallpaperSupported; - private final BroadcastReceiver mWallpaperChangedReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - if (!mWallpaperSupported) { - // Receiver should not have been registered at all... - Log.wtf(TAG, "WallpaperManager not supported"); - return; - } - WallpaperInfo info = mWallpaperManager.getWallpaperInfo(UserHandle.USER_CURRENT); - final boolean deviceSupportsAodWallpaper = mContext.getResources().getBoolean( - com.android.internal.R.bool.config_dozeSupportsAodWallpaper); - // If WallpaperInfo is null, it must be ImageWallpaper. - final boolean supportsAmbientMode = deviceSupportsAodWallpaper - && (info != null && info.supportsAmbientMode()); - - mNotificationShadeWindowController.setWallpaperSupportsAmbientMode(supportsAmbientMode); - mScrimController.setWallpaperSupportsAmbientMode(supportsAmbientMode); - } - }; - - BroadcastReceiver mTaskbarChangeReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - mBubblesOptional.ifPresent(bubbles -> bubbles.onTaskbarChanged(intent.getExtras())); - } - }; - private Runnable mLaunchTransitionEndRunnable; private boolean mLaunchCameraWhenFinishedWaking; private boolean mLaunchCameraOnFinishedGoingToSleep; @@ -693,29 +645,7 @@ public class StatusBar extends SystemUI implements private boolean mIsOccluded; private boolean mWereIconsJustHidden; private boolean mBouncerWasShowingWhenHidden; - - // Notifies StatusBarKeyguardViewManager every time the keyguard transition is over, - // this animation is tied to the scrim for historic reasons. - // TODO: notify when keyguard has faded away instead of the scrim. - private final ScrimController.Callback mUnlockScrimCallback = new ScrimController - .Callback() { - @Override - public void onFinished() { - if (mStatusBarKeyguardViewManager == null) { - Log.w(TAG, "Tried to notify keyguard visibility when " - + "mStatusBarKeyguardViewManager was null"); - return; - } - if (mKeyguardStateController.isKeyguardFadingAway()) { - mStatusBarKeyguardViewManager.onKeyguardFadedAway(); - } - } - - @Override - public void onCancelled() { - onFinished(); - } - }; + private boolean mIsLaunchingActivityOverLockscreen; private final UserSwitcherController mUserSwitcherController; private final NetworkController mNetworkController; @@ -734,36 +664,6 @@ public class StatusBar extends SystemUI implements private boolean mNoAnimationOnNextBarModeChange; private final SysuiStatusBarStateController mStatusBarStateController; - private final KeyguardUpdateMonitorCallback mUpdateCallback = - new KeyguardUpdateMonitorCallback() { - @Override - public void onDreamingStateChanged(boolean dreaming) { - if (dreaming) { - maybeEscalateHeadsUp(); - } - } - - // TODO: (b/145659174) remove when moving to NewNotifPipeline. Replaced by - // KeyguardCoordinator - @Override - public void onStrongAuthStateChanged(int userId) { - super.onStrongAuthStateChanged(userId); - mNotificationsController.requestNotificationUpdate("onStrongAuthStateChanged"); - } - }; - - - private final FalsingManager.FalsingBeliefListener mFalsingBeliefListener = - new FalsingManager.FalsingBeliefListener() { - @Override - public void onFalse() { - // Hides quick settings, bouncer, and quick-quick settings. - mStatusBarKeyguardViewManager.reset(true); - } - }; - - private final Handler mMainThreadHandler = new Handler(Looper.getMainLooper()); - private HeadsUpAppearanceController mHeadsUpAppearanceController; private ActivityLaunchAnimator mActivityLaunchAnimator; private NotificationLaunchAnimatorControllerProvider mNotificationAnimationProvider; @@ -782,6 +682,7 @@ public class StatusBar extends SystemUI implements private final ColorExtractor.OnColorsChangedListener mOnColorsChangedListener = (extractor, which) -> updateTheme(); + /** * Public constructor for StatusBar. * @@ -805,6 +706,7 @@ public class StatusBar extends SystemUI implements FalsingManager falsingManager, FalsingCollector falsingCollector, BroadcastDispatcher broadcastDispatcher, + NotificationEntryManager notificationEntryManager, NotificationGutsManager notificationGutsManager, NotificationLogger notificationLogger, NotificationInterruptStateProvider notificationInterruptStateProvider, @@ -834,6 +736,7 @@ public class StatusBar extends SystemUI implements DozeParameters dozeParameters, ScrimController scrimController, Lazy<LockscreenWallpaper> lockscreenWallpaperLazy, + LockscreenGestureLogger lockscreenGestureLogger, Lazy<BiometricUnlockController> biometricUnlockControllerLazy, DozeServiceHost dozeServiceHost, PowerManager powerManager, @@ -857,6 +760,7 @@ public class StatusBar extends SystemUI implements KeyguardDismissUtil keyguardDismissUtil, ExtensionController extensionController, UserInfoControllerImpl userInfoControllerImpl, + OperatorNameViewController.Factory operatorNameViewControllerFactory, PhoneStatusBarPolicy phoneStatusBarPolicy, KeyguardIndicationController keyguardIndicationController, DemoModeController demoModeController, @@ -873,6 +777,9 @@ public class StatusBar extends SystemUI implements LockscreenShadeTransitionController lockscreenShadeTransitionController, FeatureFlags featureFlags, KeyguardUnlockAnimationController keyguardUnlockAnimationController, + @Main Handler mainHandler, + @Main DelayableExecutor delayableExecutor, + @Main MessageRouter messageRouter, WallpaperManager wallpaperManager, UnlockedScreenOffAnimationController unlockedScreenOffAnimationController, Optional<StartingSurface> startingSurfaceOptional, @@ -887,6 +794,7 @@ public class StatusBar extends SystemUI implements mKeyguardBypassController = keyguardBypassController; mKeyguardStateController = keyguardStateController; mHeadsUpManager = headsUpManagerPhone; + mOperatorNameViewControllerFactory = operatorNameViewControllerFactory; mKeyguardIndicationController = keyguardIndicationController; mStatusBarTouchableRegionManager = statusBarTouchableRegionManager; mDynamicPrivacyController = dynamicPrivacyController; @@ -894,6 +802,7 @@ public class StatusBar extends SystemUI implements mFalsingCollector = falsingCollector; mFalsingManager = falsingManager; mBroadcastDispatcher = broadcastDispatcher; + mEntryManager = notificationEntryManager; mGutsManager = notificationGutsManager; mNotificationLogger = notificationLogger; mNotificationInterruptStateProvider = notificationInterruptStateProvider; @@ -925,6 +834,7 @@ public class StatusBar extends SystemUI implements mDozeParameters = dozeParameters; mScrimController = scrimController; mLockscreenWallpaperLazy = lockscreenWallpaperLazy; + mLockscreenGestureLogger = lockscreenGestureLogger; mScreenPinningRequest = screenPinningRequest; mDozeScrimController = dozeScrimController; mBiometricUnlockControllerLazy = biometricUnlockControllerLazy; @@ -957,6 +867,9 @@ public class StatusBar extends SystemUI implements mStatusBarIconController = statusBarIconController; mFeatureFlags = featureFlags; mKeyguardUnlockAnimationController = keyguardUnlockAnimationController; + mMainHandler = mainHandler; + mMainExecutor = delayableExecutor; + mMessageRouter = messageRouter; mWallpaperManager = wallpaperManager; mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController; mTunerService = tunerService; @@ -974,7 +887,18 @@ public class StatusBar extends SystemUI implements }); mActivityIntentHelper = new ActivityIntentHelper(mContext); + + // TODO(b/190746471): Find a better home for this. DateTimeView.setReceiverHandler(timeTickHandler); + + mMessageRouter.subscribeTo(KeyboardShortcutsMessage.class, + data -> toggleKeyboardShortcuts(data.mDeviceId)); + mMessageRouter.subscribeTo(MSG_DISMISS_KEYBOARD_SHORTCUTS_MENU, + id -> dismissKeyboardShortcuts()); + mMessageRouter.subscribeTo(AnimateExpandSettingsPanelMessage.class, + data -> mCommandQueueCallbacks.animateExpandSettingsPanel(data.mSubpanel)); + mMessageRouter.subscribeTo(MSG_LAUNCH_TRANSITION_TIMEOUT, + id -> onLaunchTransitionTimeout()); } @Override @@ -992,7 +916,7 @@ public class StatusBar extends SystemUI implements mKeyguardIndicationController.init(); mColorExtractor.addOnColorsChangedListener(mOnColorsChangedListener); - mStatusBarStateController.addCallback(this, + mStatusBarStateController.addCallback(mStateListener, SysuiStatusBarStateController.RANK_STATUS_BAR); mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE); @@ -1106,9 +1030,9 @@ public class StatusBar extends SystemUI implements mAmbientIndicationContainer); mDozeParameters.addCallback(this::updateLightRevealScrimVisibility); - mConfigurationController.addCallback(this); + mConfigurationController.addCallback(mConfigurationListener); - mBatteryController.observe(mLifecycle, this); + mBatteryController.observe(mLifecycle, mBatteryStateChangeCallback); mLifecycle.setCurrentState(RESUMED); // set the initial view visibility @@ -1129,7 +1053,7 @@ public class StatusBar extends SystemUI implements @Override public void onPluginConnected(OverlayPlugin plugin, Context pluginContext) { - mMainThreadHandler.post( + mMainExecutor.execute( () -> plugin.setup(getNotificationShadeWindowView(), getNavigationBarView(), new Callback(plugin), mDozeParameters)); @@ -1137,7 +1061,7 @@ public class StatusBar extends SystemUI implements @Override public void onPluginDisconnected(OverlayPlugin plugin) { - mMainThreadHandler.post(() -> { + mMainExecutor.execute(() -> { mOverlays.remove(plugin); mNotificationShadeWindowController .setForcePluginOpen(mOverlays.size() != 0, this); @@ -1158,7 +1082,7 @@ public class StatusBar extends SystemUI implements } else { mOverlays.remove(mPlugin); } - mMainThreadHandler.post(() -> { + mMainExecutor.execute(() -> { mNotificationShadeWindowController .setStateListener(b -> mOverlays.forEach( o -> o.setCollapseDesired(b))); @@ -1220,8 +1144,9 @@ public class StatusBar extends SystemUI implements mConfigurationController, mTunerService, mBroadcastDispatcher, - mMainThreadHandler, - mContext.getContentResolver() + mMainHandler, + mContext.getContentResolver(), + mBatteryController ); mBatteryMeterViewController.init(); @@ -1275,7 +1200,8 @@ public class StatusBar extends SystemUI implements mKeyguardStateController, mNetworkController, mStatusBarStateController, - mCommandQueue + mCommandQueue, + mOperatorNameViewControllerFactory ), CollapsedStatusBarFragment.TAG) .commit(); @@ -1331,8 +1257,19 @@ public class StatusBar extends SystemUI implements mScrimController.attachViews(scrimBehind, notificationsScrim, scrimInFront); mLightRevealScrim = mNotificationShadeWindowView.findViewById(R.id.light_reveal_scrim); - mLightRevealScrim.setRevealAmountListener( - mNotificationShadeWindowController::setLightRevealScrimAmount); + mLightRevealScrim.setScrimOpaqueChangedListener((opaque) -> { + Runnable updateOpaqueness = () -> { + mNotificationShadeWindowController.setLightRevealScrimOpaque( + mLightRevealScrim.isScrimOpaque()); + }; + if (opaque) { + // Delay making the view opaque for a frame, because it needs some time to render + // otherwise this can lead to a flicker where the scrim doesn't cover the screen + mLightRevealScrim.post(updateOpaqueness); + } else { + updateOpaqueness.run(); + } + }); mUnlockedScreenOffAnimationController.initialize(this, mLightRevealScrim); updateLightRevealScrimVisibility(); @@ -1475,19 +1412,6 @@ public class StatusBar extends SystemUI implements return mLifecycle; } - @Override - public void onPowerSaveChanged(boolean isPowerSave) { - mHandler.post(mCheckBarModes); - if (mDozeServiceHost != null) { - mDozeServiceHost.firePowerSaveChanged(isPowerSave); - } - } - - @Override - public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) { - // noop - } - @VisibleForTesting protected void registerBroadcastReceiver() { IntentFilter filter = new IntentFilter(); @@ -1503,7 +1427,7 @@ public class StatusBar extends SystemUI implements private void setUpPresenter() { // Set up the initial notification state. - mActivityLaunchAnimator = new ActivityLaunchAnimator(this, mContext); + mActivityLaunchAnimator = new ActivityLaunchAnimator(mKeyguardHandler, mContext); mNotificationAnimationProvider = new NotificationLaunchAnimatorControllerProvider( mNotificationShadeWindowViewController, mStackScrollerController.getNotificationListContainer(), @@ -1511,14 +1435,34 @@ public class StatusBar extends SystemUI implements ); // TODO: inject this. - mPresenter = new StatusBarNotificationPresenter(mContext, mNotificationPanelViewController, - mHeadsUpManager, mNotificationShadeWindowView, mStackScrollerController, - mDozeScrimController, mScrimController, mNotificationShadeWindowController, - mDynamicPrivacyController, mKeyguardStateController, + mPresenter = new StatusBarNotificationPresenter( + mContext, + mNotificationPanelViewController, + mHeadsUpManager, + mNotificationShadeWindowView, + mStackScrollerController, + mDozeScrimController, + mScrimController, + mNotificationShadeWindowController, + mDynamicPrivacyController, + mKeyguardStateController, mKeyguardIndicationController, - this /* statusBar */, mShadeController, - mLockscreenShadeTransitionController, mCommandQueue, mInitController, - mNotificationInterruptStateProvider); + this /* statusBar */, + mShadeController, + mLockscreenShadeTransitionController, + mCommandQueue, + mViewHierarchyManager, + mLockscreenUserManager, + mStatusBarStateController, + mEntryManager, + mMediaManager, + mGutsManager, + mKeyguardUpdateMonitor, + mLockscreenGestureLogger, + mInitController, + mNotificationInterruptStateProvider, + mRemoteInputManager, + mConfigurationController); mNotificationShelfController.setOnActivatedListener(mPresenter); mRemoteInputManager.addControllerCallback(mNotificationShadeWindowController); @@ -1599,48 +1543,6 @@ public class StatusBar extends SystemUI implements .getNotificationShelfController(mStackScroller); } - @Override - public void onDensityOrFontScaleChanged() { - // TODO: Remove this. - if (mBrightnessMirrorController != null) { - mBrightnessMirrorController.onDensityOrFontScaleChanged(); - } - // TODO: Bring these out of StatusBar. - mUserInfoControllerImpl.onDensityOrFontScaleChanged(); - mUserSwitcherController.onDensityOrFontScaleChanged(); - mNotificationIconAreaController.onDensityOrFontScaleChanged(mContext); - mHeadsUpManager.onDensityOrFontScaleChanged(); - } - - @Override - public void onThemeChanged() { - if (mStatusBarKeyguardViewManager != null) { - mStatusBarKeyguardViewManager.onThemeChanged(); - } - if (mAmbientIndicationContainer instanceof AutoReinflateContainer) { - ((AutoReinflateContainer) mAmbientIndicationContainer).inflateLayout(); - } - mNotificationIconAreaController.onThemeChanged(); - } - - @Override - public void onOverlayChanged() { - if (mBrightnessMirrorController != null) { - mBrightnessMirrorController.onOverlayChanged(); - } - // We need the new R.id.keyguard_indication_area before recreating - // mKeyguardIndicationController - mNotificationPanelViewController.onThemeChanged(); - onThemeChanged(); - } - - @Override - public void onUiModeChanged() { - if (mBrightnessMirrorController != null) { - mBrightnessMirrorController.onUiModeChanged(); - } - } - private void inflateStatusBarWindow() { mNotificationShadeWindowView = mSuperStatusBarViewFactory.getNotificationShadeWindowView(); StatusBarComponent statusBarComponent = mStatusBarComponentBuilder.get() @@ -1740,7 +1642,7 @@ public class StatusBar extends SystemUI implements } protected ViewGroup getBouncerContainer() { - return mNotificationShadeWindowView; + return mNotificationShadeWindowView.findViewById(R.id.keyboard_bouncer_container); } public int getStatusBarHeight() { @@ -1826,10 +1728,6 @@ public class StatusBar extends SystemUI implements return (mDisabled1 & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0; } - protected H createHandler() { - return new StatusBar.H(); - } - @Override public void startActivity(Intent intent, boolean onlyProvisioned, boolean dismissShade, int flags) { @@ -1843,10 +1741,77 @@ public class StatusBar extends SystemUI implements @Override public void startActivity(Intent intent, boolean dismissShade, - ActivityLaunchAnimator.Controller animationController) { - startActivityDismissingKeyguard(intent, false, dismissShade, + @Nullable ActivityLaunchAnimator.Controller animationController, + boolean showOverLockscreenWhenLocked) { + // Make sure that we dismiss the keyguard if it is directly dismissable or when we don't + // want to show the activity above it. + if (mKeyguardStateController.isUnlocked() || !showOverLockscreenWhenLocked) { + startActivityDismissingKeyguard(intent, false, dismissShade, false /* disallowEnterPictureInPictureWhileLaunching */, null /* callback */, 0 /* flags */, animationController); + return; + } + + boolean animate = + animationController != null && shouldAnimateLaunch(true /* isActivityIntent */, + showOverLockscreenWhenLocked); + + ActivityLaunchAnimator.Controller controller = null; + if (animate) { + // Wrap the animation controller to dismiss the shade and set + // mIsLaunchingActivityOverLockscreen during the animation. + ActivityLaunchAnimator.Controller delegate = wrapAnimationController( + animationController, dismissShade); + controller = new DelegateLaunchAnimatorController(delegate) { + @Override + public void onIntentStarted(boolean willAnimate) { + getDelegate().onIntentStarted(willAnimate); + + if (willAnimate) { + StatusBar.this.mIsLaunchingActivityOverLockscreen = true; + } + } + + @Override + public void onLaunchAnimationEnd(boolean isExpandingFullyAbove) { + // Set mIsLaunchingActivityOverLockscreen to false before actually finishing the + // animation so that we can assume that mIsLaunchingActivityOverLockscreen + // being true means that we will collapse the shade (or at least run the + // post collapse runnables) later on. + StatusBar.this.mIsLaunchingActivityOverLockscreen = false; + getDelegate().onLaunchAnimationEnd(isExpandingFullyAbove); + } + + @Override + public void onLaunchAnimationCancelled() { + // Set mIsLaunchingActivityOverLockscreen to false before actually finishing the + // animation so that we can assume that mIsLaunchingActivityOverLockscreen + // being true means that we will collapse the shade (or at least run the + // post collapse runnables) later on. + StatusBar.this.mIsLaunchingActivityOverLockscreen = false; + getDelegate().onLaunchAnimationCancelled(); + } + }; + } else if (dismissShade) { + // The animation will take care of dismissing the shade at the end of the animation. If + // we don't animate, collapse it directly. + collapseShade(); + } + + mActivityLaunchAnimator.startIntentWithAnimation(controller, animate, + intent.getPackage(), showOverLockscreenWhenLocked, (adapter) -> TaskStackBuilder + .create(mContext) + .addNextIntent(intent) + .startActivities(getActivityOptions(getDisplayId(), adapter), + UserHandle.CURRENT)); + } + + /** + * Whether we are currently animating an activity launch above the lockscreen (occluding + * activity). + */ + public boolean isLaunchingActivityOverLockscreen() { + return mIsLaunchingActivityOverLockscreen; } @Override @@ -1964,7 +1929,7 @@ public class StatusBar extends SystemUI implements // We're delaying the showing, since most of the time the fullscreen app will // hide the icons again and we don't want them to fade in and out immediately again. mWereIconsJustHidden = true; - mHandler.postDelayed(() -> { + mMainExecutor.executeDelayed(() -> { mWereIconsJustHidden = false; mCommandQueue.recomputeDisableFlags(mDisplayId, true); }, 500); @@ -2007,51 +1972,28 @@ public class StatusBar extends SystemUI implements * * Note: This method must be called *before* dismissing the keyguard. */ - public boolean shouldAnimateLaunch(boolean isActivityIntent) { + public boolean shouldAnimateLaunch(boolean isActivityIntent, boolean showOverLockscreen) { // TODO(b/184121838): Support launch animations when occluded. if (isOccluded()) { return false; } - // Always animate if we are unlocked. - if (!mKeyguardStateController.isShowing()) { + // Always animate if we are not showing the keyguard or if we animate over the lockscreen + // (without unlocking it). + if (showOverLockscreen || !mKeyguardStateController.isShowing()) { return true; } - // If we are locked, only animate if remote unlock animations are enabled. We also don't - // animate non-activity launches as they can break the animation. + // If we are locked and have to dismiss the keyguard, only animate if remote unlock + // animations are enabled. We also don't animate non-activity launches as they can break the + // animation. // TODO(b/184121838): Support non activity launches on the lockscreen. return isActivityIntent && KeyguardService.sEnableRemoteKeyguardGoingAwayAnimation; } - @Override - public boolean isOnKeyguard() { - return mKeyguardStateController.isShowing(); - } - - @Override - public void hideKeyguardWithAnimation(IRemoteAnimationRunner runner) { - // We post to the main thread for 2 reasons: - // 1. KeyguardViewMediator is not thread-safe. - // 2. To ensure that ViewMediatorCallback#keyguardDonePending is called before - // ViewMediatorCallback#readyForKeyguardDone. The wrong order could occur when doing - // dismissKeyguardThenExecute { hideKeyguardWithAnimation(runner) }. - mMainThreadHandler.post(() -> mKeyguardViewMediator.hideWithAnimation(runner)); - } - - @Override - public void setBlursDisabledForAppLaunch(boolean disabled) { - mKeyguardViewMediator.setBlursDisabledForAppLaunch(disabled); - } - - @Override - public int getBackgroundColor(TaskInfo task) { - if (!mStartingSurfaceOptional.isPresent()) { - Log.w(TAG, "No starting surface, defaulting to SystemBGColor"); - return SplashscreenContentDrawer.getSystemBGColor(); - } - - return mStartingSurfaceOptional.get().getBackgroundColor(task); + /** Whether we should animate an activity launch. */ + public boolean shouldAnimateLaunch(boolean isActivityIntent) { + return shouldAnimateLaunch(isActivityIntent, false /* showOverLockscreen */); } public boolean isDeviceInVrMode() { @@ -2067,37 +2009,19 @@ public class StatusBar extends SystemUI implements mState = state; } - /** - * All changes to the status bar and notifications funnel through here and are batched. - */ - protected class H extends Handler { - H() { - super(Looper.myLooper()); + static class KeyboardShortcutsMessage { + final int mDeviceId; + + KeyboardShortcutsMessage(int deviceId) { + mDeviceId = deviceId; } + } - @Override - public void handleMessage(Message m) { - switch (m.what) { - case MSG_TOGGLE_KEYBOARD_SHORTCUTS_MENU: - toggleKeyboardShortcuts(m.arg1); - break; - case MSG_DISMISS_KEYBOARD_SHORTCUTS_MENU: - dismissKeyboardShortcuts(); - break; - // End old BaseStatusBar.H handling. - case MSG_OPEN_NOTIFICATION_PANEL: - mCommandQueueCallbacks.animateExpandNotificationsPanel(); - break; - case MSG_OPEN_SETTINGS_PANEL: - mCommandQueueCallbacks.animateExpandSettingsPanel((String) m.obj); - break; - case MSG_CLOSE_PANELS: - mShadeController.animateCollapsePanels(); - break; - case MSG_LAUNCH_TRANSITION_TIMEOUT: - onLaunchTransitionTimeout(); - break; - } + static class AnimateExpandSettingsPanelMessage { + final String mSubpanel; + + AnimateExpandSettingsPanelMessage(String subpanel) { + mSubpanel = subpanel; } } @@ -2139,27 +2063,17 @@ public class StatusBar extends SystemUI implements } public void postAnimateCollapsePanels() { - mHandler.post(mShadeController::animateCollapsePanels); + mMainExecutor.execute(mShadeController::animateCollapsePanels); } public void postAnimateForceCollapsePanels() { - mHandler.post(() -> mShadeController.animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE, + mMainExecutor.execute( + () -> mShadeController.animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE, true /* force */)); } public void postAnimateOpenPanels() { - mHandler.sendEmptyMessage(MSG_OPEN_SETTINGS_PANEL); - } - - /** - * Called by {@link ShadeController} when it calls - * {@link ShadeController#animateCollapsePanels(int, boolean, boolean, float)}. - */ - void postHideRecentApps() { - if (!mHandler.hasMessages(MSG_HIDE_RECENT_APPS)) { - mHandler.removeMessages(MSG_HIDE_RECENT_APPS); - mHandler.sendEmptyMessage(MSG_HIDE_RECENT_APPS); - } + mMessageRouter.sendMessage(MSG_OPEN_SETTINGS_PANEL); } public boolean isExpandedVisible() { @@ -2629,7 +2543,7 @@ public class StatusBar extends SystemUI implements options.setRotationAnimationHint( WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS); } - if (intent.getAction().equals(Settings.Panel.ACTION_VOLUME)) { + if (Settings.Panel.ACTION_VOLUME.equals(intent.getAction())) { // Settings Panel is implemented as activity(not a dialog), so // underlying app is paused and may enter picture-in-picture mode // as a result. @@ -2728,7 +2642,7 @@ public class StatusBar extends SystemUI implements && mStatusBarKeyguardViewManager.isOccluded()) { mStatusBarKeyguardViewManager.addAfterKeyguardGoneRunnable(runnable); } else { - mHandler.post(runnable); + mMainExecutor.execute(runnable); } } if (dismissShade) { @@ -2740,7 +2654,7 @@ public class StatusBar extends SystemUI implements // Do it after DismissAction has been processed to conserve the needed // ordering. - mHandler.post(mShadeController::runPostCollapseRunnables); + mMainExecutor.execute(mShadeController::runPostCollapseRunnables); } } else if (StatusBar.this.isInLaunchTransition() && mNotificationPanelViewController.isLaunchTransitionFinished()) { @@ -2749,7 +2663,7 @@ public class StatusBar extends SystemUI implements // finished, // so nobody will call readyForKeyguardDone anymore. Post it such that // keyguardDonePending gets called first. - mHandler.post(mStatusBarKeyguardViewManager::readyForKeyguardDone); + mMainExecutor.execute(mStatusBarKeyguardViewManager::readyForKeyguardDone); } return deferred; } @@ -2788,8 +2702,8 @@ public class StatusBar extends SystemUI implements mNotificationShadeWindowController.setNotTouchable(false); } if (mBubblesOptional.isPresent() && mBubblesOptional.get().isStackExpanded()) { - // Post to main thread handler, since updating the UI. - mMainThreadHandler.post(() -> mBubblesOptional.get().collapseStack()); + // Post to main thread, since updating the UI. + mMainExecutor.execute(() -> mBubblesOptional.get().collapseStack()); } finishBarAnimations(); resetUserExpandedStates(); @@ -2850,20 +2764,6 @@ public class StatusBar extends SystemUI implements action.onDismiss(); } } - - @Override - public void onConfigChanged(Configuration newConfig) { - updateResources(); - updateDisplaySize(); // populates mDisplayMetrics - - if (DEBUG) { - Log.v(TAG, "configuration changed: " + mContext.getResources().getConfiguration()); - } - - mViewHierarchyManager.updateRowStates(); - mScreenPinningRequest.onConfigurationChanged(); - } - /** * Notify the shade controller that the current user changed * @@ -3013,10 +2913,10 @@ public class StatusBar extends SystemUI implements @Override public void postQSRunnableDismissingKeyguard(final Runnable runnable) { - mHandler.post(() -> { + mMainExecutor.execute(() -> { mStatusBarStateController.setLeaveOpenOnKeyguardHide(true); - executeRunnableDismissingKeyguard(() -> mHandler.post(runnable), null, false, false, - false); + executeRunnableDismissingKeyguard( + () -> mMainExecutor.execute(runnable), null, false, false, false); }); } @@ -3028,7 +2928,7 @@ public class StatusBar extends SystemUI implements @Override public void postStartActivityDismissingKeyguard(final PendingIntent intent, @Nullable ActivityLaunchAnimator.Controller animationController) { - mHandler.post(() -> startPendingIntentDismissingKeyguard(intent, + mMainExecutor.execute(() -> startPendingIntentDismissingKeyguard(intent, null /* intentSentUiThreadCallback */, animationController)); } @@ -3040,7 +2940,7 @@ public class StatusBar extends SystemUI implements @Override public void postStartActivityDismissingKeyguard(Intent intent, int delay, @Nullable ActivityLaunchAnimator.Controller animationController) { - mHandler.postDelayed( + mMainExecutor.executeDelayed( () -> startActivityDismissingKeyguard(intent, true /* onlyProvisioned */, true /* dismissShade */, @@ -3110,7 +3010,7 @@ public class StatusBar extends SystemUI implements mNotificationPanelViewController.cancelAnimation(); onLaunchTransitionFadingEnded(); } - mHandler.removeMessages(MSG_LAUNCH_TRANSITION_TIMEOUT); + mMessageRouter.cancelMessages(MSG_LAUNCH_TRANSITION_TIMEOUT); if (mUserSwitcherController != null && mUserSwitcherController.useFullscreenUserSwitcher()) { mStatusBarStateController.setState(StatusBarState.FULLSCREEN_USER_SWITCHER); } else if (!mPulseExpansionHandler.isWakingToShadeLocked()) { @@ -3151,7 +3051,7 @@ public class StatusBar extends SystemUI implements */ public void fadeKeyguardAfterLaunchTransition(final Runnable beforeFading, Runnable endRunnable) { - mHandler.removeMessages(MSG_LAUNCH_TRANSITION_TIMEOUT); + mMessageRouter.cancelMessages(MSG_LAUNCH_TRANSITION_TIMEOUT); mLaunchTransitionEndRunnable = endRunnable; Runnable hideRunnable = () -> { mKeyguardStateController.setLaunchTransitionFadingAway(true); @@ -3192,6 +3092,7 @@ public class StatusBar extends SystemUI implements public void animateKeyguardUnoccluding() { mNotificationPanelViewController.setExpandedFraction(0f); mCommandQueueCallbacks.animateExpandNotificationsPanel(); + mScrimController.setUnocclusionAnimationRunning(true); } /** @@ -3200,8 +3101,8 @@ public class StatusBar extends SystemUI implements * because the launched app crashed or something else went wrong. */ public void startLaunchTransitionTimeout() { - mHandler.sendEmptyMessageDelayed(MSG_LAUNCH_TRANSITION_TIMEOUT, - LAUNCH_TRANSITION_TIMEOUT_MS); + mMessageRouter.sendMessageDelayed( + MSG_LAUNCH_TRANSITION_TIMEOUT, LAUNCH_TRANSITION_TIMEOUT_MS); } private void onLaunchTransitionTimeout() { @@ -3256,7 +3157,7 @@ public class StatusBar extends SystemUI implements if (mQSPanelController != null) { mQSPanelController.refreshAllTiles(); } - mHandler.removeMessages(MSG_LAUNCH_TRANSITION_TIMEOUT); + mMessageRouter.cancelMessages(MSG_LAUNCH_TRANSITION_TIMEOUT); releaseGestureWakeLock(); mNotificationPanelViewController.onAffordanceLaunchEnded(); mNotificationPanelViewController.cancelAnimation(); @@ -3484,69 +3385,7 @@ public class StatusBar extends SystemUI implements mNotificationPanelViewController.collapseWithDuration(duration); } - @Override - public void onStatePreChange(int oldState, int newState) { - // If we're visible and switched to SHADE_LOCKED (the user dragged - // down on the lockscreen), clear notification LED, vibration, - // ringing. - // Other transitions are covered in handleVisibleToUserChanged(). - if (mVisible && (newState == StatusBarState.SHADE_LOCKED - || mStatusBarStateController.goingToFullShade())) { - clearNotificationEffects(); - } - if (newState == StatusBarState.KEYGUARD) { - mRemoteInputManager.onPanelCollapsed(); - maybeEscalateHeadsUp(); - } - } - - @Override - public void onStateChanged(int newState) { - mState = newState; - updateReportRejectedTouchVisibility(); - mDozeServiceHost.updateDozing(); - updateTheme(); - mNavigationBarController.touchAutoDim(mDisplayId); - Trace.beginSection("StatusBar#updateKeyguardState"); - if (mState == StatusBarState.KEYGUARD && mStatusBarView != null) { - mStatusBarView.removePendingHideExpandedRunnables(); - } - updateDozingState(); - checkBarModes(); - updateScrimController(); - mPresenter.updateMediaMetaData(false, mState != StatusBarState.KEYGUARD); - updateKeyguardState(); - Trace.endSection(); - } - @Override - public void onDozeAmountChanged(float linear, float eased) { - if (mFeatureFlags.useNewLockscreenAnimations() - && !(mLightRevealScrim.getRevealEffect() instanceof CircleReveal)) { - mLightRevealScrim.setRevealAmount(1f - linear); - } - } - - @Override - public void onDozingChanged(boolean isDozing) { - Trace.beginSection("StatusBar#updateDozing"); - mDozing = isDozing; - - // Collapse the notification panel if open - boolean dozingAnimated = mDozeServiceHost.getDozingRequested() - && mDozeParameters.shouldControlScreenOff(); - mNotificationPanelViewController.resetViews(dozingAnimated); - - updateQsExpansionEnabled(); - mKeyguardViewMediator.setDozing(mDozing); - - mNotificationsController.requestNotificationUpdate("onDozingChanged"); - updateDozingState(); - mDozeServiceHost.updateDozing(); - updateScrimController(); - updateReportRejectedTouchVisibility(); - Trace.endSection(); - } /** * Updates the light reveal effect to reflect the reason we're waking or sleeping (for example, @@ -3688,7 +3527,7 @@ public class StatusBar extends SystemUI implements // This gets executed before we will show Keyguard, so post it in order that the state // is correct. - mHandler.post(() -> mCommandQueueCallbacks.onCameraLaunchGestureDetected( + mMainExecutor.execute(() -> mCommandQueueCallbacks.onCameraLaunchGestureDetected( mLastCameraLaunchSource)); } @@ -3697,7 +3536,7 @@ public class StatusBar extends SystemUI implements // This gets executed before we will show Keyguard, so post it in order that the // state is correct. - mHandler.post( + mMainExecutor.execute( () -> mCommandQueueCallbacks.onEmergencyActionLaunchGestureDetected()); } updateIsKeyguard(); @@ -3917,10 +3756,8 @@ public class StatusBar extends SystemUI implements ScrimState state = mStatusBarKeyguardViewManager.bouncerNeedsScrimming() ? ScrimState.BOUNCER_SCRIMMED : ScrimState.BOUNCER; mScrimController.transitionTo(state); - } else if (isInLaunchTransition() - || mLaunchCameraWhenFinishedWaking - || launchingAffordanceWithPreview) { - // TODO(b/170133395) Investigate whether Emergency Gesture flag should be included here. + } else if (launchingAffordanceWithPreview) { + // We want to avoid animating when launching with a preview. mScrimController.transitionTo(ScrimState.UNLOCKED, mUnlockScrimCallback); } else if (mBrightnessMirrorVisible) { mScrimController.transitionTo(ScrimState.BRIGHTNESS_MIRROR); @@ -4166,7 +4003,7 @@ public class StatusBar extends SystemUI implements } private void postOnUiThread(Runnable runnable) { - mMainThreadHandler.post(runnable); + mMainExecutor.execute(runnable); } /** @@ -4344,4 +4181,277 @@ public class StatusBar extends SystemUI implements mLightRevealScrim.setVisibility(View.GONE); } } + + private final KeyguardUpdateMonitorCallback mUpdateCallback = + new KeyguardUpdateMonitorCallback() { + @Override + public void onDreamingStateChanged(boolean dreaming) { + if (dreaming) { + maybeEscalateHeadsUp(); + } + } + + // TODO: (b/145659174) remove when moving to NewNotifPipeline. Replaced by + // KeyguardCoordinator + @Override + public void onStrongAuthStateChanged(int userId) { + super.onStrongAuthStateChanged(userId); + mNotificationsController.requestNotificationUpdate("onStrongAuthStateChanged"); + } + }; + + + private final FalsingManager.FalsingBeliefListener mFalsingBeliefListener = + new FalsingManager.FalsingBeliefListener() { + @Override + public void onFalse() { + // Hides quick settings, bouncer, and quick-quick settings. + mStatusBarKeyguardViewManager.reset(true); + } + }; + + // Notifies StatusBarKeyguardViewManager every time the keyguard transition is over, + // this animation is tied to the scrim for historic reasons. + // TODO: notify when keyguard has faded away instead of the scrim. + private final ScrimController.Callback mUnlockScrimCallback = new ScrimController + .Callback() { + @Override + public void onFinished() { + if (mStatusBarKeyguardViewManager == null) { + Log.w(TAG, "Tried to notify keyguard visibility when " + + "mStatusBarKeyguardViewManager was null"); + return; + } + if (mKeyguardStateController.isKeyguardFadingAway()) { + mStatusBarKeyguardViewManager.onKeyguardFadedAway(); + } + } + + @Override + public void onCancelled() { + onFinished(); + } + }; + + private final DeviceProvisionedListener mUserSetupObserver = new DeviceProvisionedListener() { + @Override + public void onUserSetupChanged() { + final boolean userSetup = mDeviceProvisionedController.isUserSetup( + mDeviceProvisionedController.getCurrentUser()); + Log.d(TAG, "mUserSetupObserver - DeviceProvisionedListener called for user " + + mDeviceProvisionedController.getCurrentUser()); + if (MULTIUSER_DEBUG) { + Log.d(TAG, String.format("User setup changed: userSetup=%s mUserSetup=%s", + userSetup, mUserSetup)); + } + + if (userSetup != mUserSetup) { + mUserSetup = userSetup; + if (!mUserSetup && mStatusBarView != null) { + animateCollapseQuickSettings(); + } + if (mNotificationPanelViewController != null) { + mNotificationPanelViewController.setUserSetupComplete(mUserSetup); + } + updateQsExpansionEnabled(); + } + } + }; + + private final BroadcastReceiver mWallpaperChangedReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (!mWallpaperSupported) { + // Receiver should not have been registered at all... + Log.wtf(TAG, "WallpaperManager not supported"); + return; + } + WallpaperInfo info = mWallpaperManager.getWallpaperInfo(UserHandle.USER_CURRENT); + final boolean deviceSupportsAodWallpaper = mContext.getResources().getBoolean( + com.android.internal.R.bool.config_dozeSupportsAodWallpaper); + // If WallpaperInfo is null, it must be ImageWallpaper. + final boolean supportsAmbientMode = deviceSupportsAodWallpaper + && (info != null && info.supportsAmbientMode()); + + mNotificationShadeWindowController.setWallpaperSupportsAmbientMode(supportsAmbientMode); + mScrimController.setWallpaperSupportsAmbientMode(supportsAmbientMode); + } + }; + + BroadcastReceiver mTaskbarChangeReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + mBubblesOptional.ifPresent(bubbles -> bubbles.onTaskbarChanged(intent.getExtras())); + } + }; + + private final ConfigurationListener mConfigurationListener = new ConfigurationListener() { + @Override + public void onConfigChanged(Configuration newConfig) { + updateResources(); + updateDisplaySize(); // populates mDisplayMetrics + + if (DEBUG) { + Log.v(TAG, "configuration changed: " + mContext.getResources().getConfiguration()); + } + + mViewHierarchyManager.updateRowStates(); + mScreenPinningRequest.onConfigurationChanged(); + } + + @Override + public void onDensityOrFontScaleChanged() { + // TODO: Remove this. + if (mBrightnessMirrorController != null) { + mBrightnessMirrorController.onDensityOrFontScaleChanged(); + } + // TODO: Bring these out of StatusBar. + mUserInfoControllerImpl.onDensityOrFontScaleChanged(); + mUserSwitcherController.onDensityOrFontScaleChanged(); + mNotificationIconAreaController.onDensityOrFontScaleChanged(mContext); + mHeadsUpManager.onDensityOrFontScaleChanged(); + } + + @Override + public void onThemeChanged() { + if (mStatusBarKeyguardViewManager != null) { + mStatusBarKeyguardViewManager.onThemeChanged(); + } + if (mAmbientIndicationContainer instanceof AutoReinflateContainer) { + ((AutoReinflateContainer) mAmbientIndicationContainer).inflateLayout(); + } + mNotificationIconAreaController.onThemeChanged(); + } + + @Override + public void onOverlayChanged() { + if (mBrightnessMirrorController != null) { + mBrightnessMirrorController.onOverlayChanged(); + } + // We need the new R.id.keyguard_indication_area before recreating + // mKeyguardIndicationController + mNotificationPanelViewController.onThemeChanged(); + onThemeChanged(); + } + + @Override + public void onUiModeChanged() { + if (mBrightnessMirrorController != null) { + mBrightnessMirrorController.onUiModeChanged(); + } + } + }; + + private StatusBarStateController.StateListener mStateListener = + new StatusBarStateController.StateListener() { + @Override + public void onStatePreChange(int oldState, int newState) { + // If we're visible and switched to SHADE_LOCKED (the user dragged + // down on the lockscreen), clear notification LED, vibration, + // ringing. + // Other transitions are covered in handleVisibleToUserChanged(). + if (mVisible && (newState == StatusBarState.SHADE_LOCKED + || mStatusBarStateController.goingToFullShade())) { + clearNotificationEffects(); + } + if (newState == StatusBarState.KEYGUARD) { + mRemoteInputManager.onPanelCollapsed(); + maybeEscalateHeadsUp(); + } + } + + @Override + public void onStateChanged(int newState) { + mState = newState; + updateReportRejectedTouchVisibility(); + mDozeServiceHost.updateDozing(); + updateTheme(); + mNavigationBarController.touchAutoDim(mDisplayId); + Trace.beginSection("StatusBar#updateKeyguardState"); + if (mState == StatusBarState.KEYGUARD && mStatusBarView != null) { + mStatusBarView.removePendingHideExpandedRunnables(); + } + updateDozingState(); + checkBarModes(); + updateScrimController(); + mPresenter.updateMediaMetaData(false, mState != StatusBarState.KEYGUARD); + updateKeyguardState(); + Trace.endSection(); + } + + @Override + public void onDozeAmountChanged(float linear, float eased) { + if (mFeatureFlags.useNewLockscreenAnimations() + && !(mLightRevealScrim.getRevealEffect() instanceof CircleReveal)) { + mLightRevealScrim.setRevealAmount(1f - linear); + } + } + + @Override + public void onDozingChanged(boolean isDozing) { + Trace.beginSection("StatusBar#updateDozing"); + mDozing = isDozing; + + // Collapse the notification panel if open + boolean dozingAnimated = mDozeServiceHost.getDozingRequested() + && mDozeParameters.shouldControlScreenOff(); + mNotificationPanelViewController.resetViews(dozingAnimated); + + updateQsExpansionEnabled(); + mKeyguardViewMediator.setDozing(mDozing); + + mNotificationsController.requestNotificationUpdate("onDozingChanged"); + updateDozingState(); + mDozeServiceHost.updateDozing(); + updateScrimController(); + updateReportRejectedTouchVisibility(); + Trace.endSection(); + } + }; + + private final BatteryController.BatteryStateChangeCallback mBatteryStateChangeCallback = + new BatteryController.BatteryStateChangeCallback() { + @Override + public void onPowerSaveChanged(boolean isPowerSave) { + mMainExecutor.execute(mCheckBarModes); + if (mDozeServiceHost != null) { + mDozeServiceHost.firePowerSaveChanged(isPowerSave); + } + } + }; + + private final ActivityLaunchAnimator.Callback mKeyguardHandler = + new ActivityLaunchAnimator.Callback() { + @Override + public boolean isOnKeyguard() { + return mKeyguardStateController.isShowing(); + } + + @Override + public void hideKeyguardWithAnimation(IRemoteAnimationRunner runner) { + // We post to the main thread for 2 reasons: + // 1. KeyguardViewMediator is not thread-safe. + // 2. To ensure that ViewMediatorCallback#keyguardDonePending is called before + // ViewMediatorCallback#readyForKeyguardDone. The wrong order could occur + // when doing + // dismissKeyguardThenExecute { hideKeyguardWithAnimation(runner) }. + mMainExecutor.execute(() -> mKeyguardViewMediator.hideWithAnimation(runner)); + } + + @Override + public void setBlursDisabledForAppLaunch(boolean disabled) { + mKeyguardViewMediator.setBlursDisabledForAppLaunch(disabled); + } + + @Override + public int getBackgroundColor(TaskInfo task) { + if (!mStartingSurfaceOptional.isPresent()) { + Log.w(TAG, "No starting surface, defaulting to SystemBGColor"); + return SplashscreenContentDrawer.getSystemBGColor(); + } + + return mStartingSurfaceOptional.get().getBackgroundColor(task); + } + }; + } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacks.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacks.java index 95f2d0a868c6..6a510c92a3a0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacks.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacks.java @@ -256,18 +256,8 @@ public class StatusBarCommandQueueCallbacks implements CommandQueue.Callbacks { } @Override - public void preloadRecentApps() { - mStatusBar.resetHandlerMsg(StatusBar.MSG_PRELOAD_RECENT_APPS); - } - - @Override - public void cancelPreloadRecentApps() { - mStatusBar.resetHandlerMsg(StatusBar.MSG_CANCEL_PRELOAD_RECENT_APPS); - } - - @Override public void dismissKeyboardShortcutsMenu() { - mStatusBar.resetHandlerMsg(StatusBar.MSG_DISMISS_KEYBOARD_SHORTCUTS_MENU); + mStatusBar.resendMessage(StatusBar.MSG_DISMISS_KEYBOARD_SHORTCUTS_MENU); } /** * State is one or more of the DISABLE constants from StatusBarManager. @@ -330,13 +320,6 @@ public class StatusBarCommandQueueCallbacks implements CommandQueue.Callbacks { } } - if ((diff1 & StatusBarManager.DISABLE_RECENT) != 0) { - if ((state1 & StatusBarManager.DISABLE_RECENT) != 0) { - // close recents if it's visible - mStatusBar.resetHandlerMsg(StatusBar.MSG_HIDE_RECENT_APPS); - } - } - if ((diff1 & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0) { if (mStatusBar.areNotificationAlertsDisabled()) { mHeadsUpManager.releaseAllImmediately(); @@ -544,9 +527,7 @@ public class StatusBarCommandQueueCallbacks implements CommandQueue.Callbacks { @Override public void toggleKeyboardShortcutsMenu(int deviceId) { - int msg = StatusBar.MSG_TOGGLE_KEYBOARD_SHORTCUTS_MENU; - mStatusBar.getHandler().removeMessages(msg); - mStatusBar.getHandler().obtainMessage(msg, deviceId, 0).sendToTarget(); + mStatusBar.resendMessage(new StatusBar.KeyboardShortcutsMessage(deviceId)); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java index 95fd886d6046..119eff6f96a0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -73,6 +73,8 @@ import java.util.Optional; import javax.inject.Inject; +import dagger.Lazy; + /** * Manages creating, showing, hiding and resetting the keyguard within the status bar. Calls back * via {@link ViewMediatorCallback} to poke the wake lock and report that the keyguard is done, @@ -111,6 +113,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb private final UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController; private final KeyguardMessageAreaController.Factory mKeyguardMessageAreaFactory; private KeyguardMessageAreaController mKeyguardMessageAreaController; + private final Lazy<ShadeController> mShadeController; private final BouncerExpansionCallback mExpansionCallback = new BouncerExpansionCallback() { @Override public void onFullyShown() { @@ -243,7 +246,8 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb KeyguardBouncer.Factory keyguardBouncerFactory, WakefulnessLifecycle wakefulnessLifecycle, UnlockedScreenOffAnimationController unlockedScreenOffAnimationController, - KeyguardMessageAreaController.Factory keyguardMessageAreaFactory) { + KeyguardMessageAreaController.Factory keyguardMessageAreaFactory, + Lazy<ShadeController> shadeController) { mContext = context; mViewMediatorCallback = callback; mLockPatternUtils = lockPatternUtils; @@ -260,6 +264,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb mWakefulnessLifecycle = wakefulnessLifecycle; mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController; mKeyguardMessageAreaFactory = keyguardMessageAreaFactory; + mShadeController = shadeController; } @Override @@ -634,6 +639,18 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb }); return; } + + if (mStatusBar.isLaunchingActivityOverLockscreen()) { + mOccluded = true; + + // When isLaunchingActivityOverLockscreen() is true, we know for sure that the post + // collapse runnables will be run. + mShadeController.get().addPostCollapseAction(() -> { + mNotificationShadeWindowController.setKeyguardOccluded(mOccluded); + reset(true /* hideBouncerWhenShowing */); + }); + return; + } } else if (!occluded && mOccluded && mShowing) { SysUiStatsLog.write(SysUiStatsLog.KEYGUARD_STATE_CHANGED, SysUiStatsLog.KEYGUARD_STATE_CHANGED__STATE__SHOWN); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java index cb844d8c5ac9..832f317d5783 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java @@ -47,7 +47,6 @@ import com.android.systemui.InitController; import com.android.systemui.R; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.ActivityStarter.OnDismissAction; -import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.KeyguardIndicationController; import com.android.systemui.statusbar.LockscreenShadeTransitionController; @@ -65,7 +64,6 @@ import com.android.systemui.statusbar.notification.NotificationEntryListener; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl; -import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager; import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider; import com.android.systemui.statusbar.notification.interruption.NotificationInterruptSuppressor; import com.android.systemui.statusbar.notification.row.ActivatableNotificationView; @@ -84,28 +82,18 @@ public class StatusBarNotificationPresenter implements NotificationPresenter, ConfigurationController.ConfigurationListener, NotificationRowBinderImpl.BindRowCallback, CommandQueue.Callbacks { - - private final LockscreenGestureLogger mLockscreenGestureLogger = - Dependency.get(LockscreenGestureLogger.class); - private static final String TAG = "StatusBarNotificationPresenter"; private final ActivityStarter mActivityStarter = Dependency.get(ActivityStarter.class); private final KeyguardStateController mKeyguardStateController; - private final NotificationViewHierarchyManager mViewHierarchyManager = - Dependency.get(NotificationViewHierarchyManager.class); - private final NotificationLockscreenUserManager mLockscreenUserManager = - Dependency.get(NotificationLockscreenUserManager.class); - private final SysuiStatusBarStateController mStatusBarStateController = - (SysuiStatusBarStateController) Dependency.get(StatusBarStateController.class); - private final NotificationEntryManager mEntryManager = - Dependency.get(NotificationEntryManager.class); - private final NotificationMediaManager mMediaManager = - Dependency.get(NotificationMediaManager.class); - private final VisualStabilityManager mVisualStabilityManager = - Dependency.get(VisualStabilityManager.class); - private final NotificationGutsManager mGutsManager = - Dependency.get(NotificationGutsManager.class); + private final NotificationViewHierarchyManager mViewHierarchyManager; + private final NotificationLockscreenUserManager mLockscreenUserManager; + private final SysuiStatusBarStateController mStatusBarStateController; + private final NotificationEntryManager mEntryManager; + private final NotificationMediaManager mMediaManager; + private final NotificationGutsManager mGutsManager; + private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; + private final LockscreenGestureLogger mLockscreenGestureLogger; private final NotificationPanelViewController mNotificationPanel; private final HeadsUpManagerPhone mHeadsUpManager; @@ -144,8 +132,18 @@ public class StatusBarNotificationPresenter implements NotificationPresenter, ShadeController shadeController, LockscreenShadeTransitionController shadeTransitionController, CommandQueue commandQueue, + NotificationViewHierarchyManager notificationViewHierarchyManager, + NotificationLockscreenUserManager lockscreenUserManager, + SysuiStatusBarStateController sysuiStatusBarStateController, + NotificationEntryManager notificationEntryManager, + NotificationMediaManager notificationMediaManager, + NotificationGutsManager notificationGutsManager, + KeyguardUpdateMonitor keyguardUpdateMonitor, + LockscreenGestureLogger lockscreenGestureLogger, InitController initController, - NotificationInterruptStateProvider notificationInterruptStateProvider) { + NotificationInterruptStateProvider notificationInterruptStateProvider, + NotificationRemoteInputManager remoteInputManager, + ConfigurationController configurationController) { mKeyguardStateController = keyguardStateController; mNotificationPanel = panel; mHeadsUpManager = headsUp; @@ -156,6 +154,14 @@ public class StatusBarNotificationPresenter implements NotificationPresenter, mShadeController = shadeController; mShadeTransitionController = shadeTransitionController; mCommandQueue = commandQueue; + mViewHierarchyManager = notificationViewHierarchyManager; + mLockscreenUserManager = lockscreenUserManager; + mStatusBarStateController = sysuiStatusBarStateController; + mEntryManager = notificationEntryManager; + mMediaManager = notificationMediaManager; + mGutsManager = notificationGutsManager; + mKeyguardUpdateMonitor = keyguardUpdateMonitor; + mLockscreenGestureLogger = lockscreenGestureLogger; mAboveShelfObserver = new AboveShelfObserver(stackScrollerController.getView()); mNotificationShadeWindowController = notificationShadeWindowController; mAboveShelfObserver.setListener(statusBarWindow.findViewById( @@ -176,8 +182,6 @@ public class StatusBarNotificationPresenter implements NotificationPresenter, Slog.e(TAG, "Failed to register VR mode state listener: " + e); } } - NotificationRemoteInputManager remoteInputManager = - Dependency.get(NotificationRemoteInputManager.class); remoteInputManager.setUpWithCallback( Dependency.get(NotificationRemoteInputManager.Callback.class), mNotificationPanel.createRemoteInputDelegate()); @@ -220,14 +224,14 @@ public class StatusBarNotificationPresenter implements NotificationPresenter, onUserSwitched(mLockscreenUserManager.getCurrentUserId()); }); - Dependency.get(ConfigurationController.class).addCallback(this); + configurationController.addCallback(this); } @Override public void onDensityOrFontScaleChanged() { MessagingMessage.dropCache(); MessagingGroup.dropCache(); - if (!Dependency.get(KeyguardUpdateMonitor.class).isSwitchingUser()) { + if (!mKeyguardUpdateMonitor.isSwitchingUser()) { updateNotificationsOnDensityOrFontScaleChanged(); } else { mReinflateNotificationsOnUserSwitched = true; @@ -236,7 +240,7 @@ public class StatusBarNotificationPresenter implements NotificationPresenter, @Override public void onUiModeChanged() { - if (!Dependency.get(KeyguardUpdateMonitor.class).isSwitchingUser()) { + if (!mKeyguardUpdateMonitor.isSwitchingUser()) { updateNotificationOnUiModeChanged(); } else { mDispatchUiModeChangeOnUserSwitched = true; @@ -308,17 +312,11 @@ public class StatusBarNotificationPresenter implements NotificationPresenter, private void onNotificationRemoved(String key, StatusBarNotification old, int reason) { if (SPEW) Log.d(TAG, "removeNotification key=" + key + " old=" + old); - if (old != null) { - if (CLOSE_PANEL_WHEN_EMPTIED && !hasActiveNotifications() - && !mNotificationPanel.isTracking() && !mNotificationPanel.isQsExpanded()) { - if (mStatusBarStateController.getState() == StatusBarState.SHADE - && reason != NotificationListenerService.REASON_CLICK) { - mCommandQueue.animateCollapsePanels(); - } else if (mStatusBarStateController.getState() == StatusBarState.SHADE_LOCKED + if (old != null && CLOSE_PANEL_WHEN_EMPTIED && !hasActiveNotifications() + && !mNotificationPanel.isTracking() && !mNotificationPanel.isQsExpanded() + && mStatusBarStateController.getState() == StatusBarState.SHADE_LOCKED && !isCollapsing()) { - mStatusBarStateController.setState(StatusBarState.KEYGUARD); - } - } + mStatusBarStateController.setState(StatusBarState.KEYGUARD); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java index eabb2ab696ee..b36c45e7f8ef 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java @@ -33,6 +33,7 @@ import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.classifier.FalsingCollector; import com.android.systemui.colorextraction.SysuiColorExtractor; import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dagger.qualifiers.UiBackground; import com.android.systemui.demomode.DemoModeController; import com.android.systemui.flags.FeatureFlags; @@ -55,11 +56,13 @@ import com.android.systemui.statusbar.NotificationRemoteInputManager; import com.android.systemui.statusbar.NotificationShadeDepthController; import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.NotificationViewHierarchyManager; +import com.android.systemui.statusbar.OperatorNameViewController; import com.android.systemui.statusbar.PulseExpansionHandler; import com.android.systemui.statusbar.SuperStatusBarViewFactory; import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler; import com.android.systemui.statusbar.notification.DynamicPrivacyController; +import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager; import com.android.systemui.statusbar.notification.init.NotificationsController; @@ -77,6 +80,7 @@ import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.phone.KeyguardDismissUtil; import com.android.systemui.statusbar.phone.LightBarController; import com.android.systemui.statusbar.phone.LightsOutNotifController; +import com.android.systemui.statusbar.phone.LockscreenGestureLogger; import com.android.systemui.statusbar.phone.LockscreenWallpaper; import com.android.systemui.statusbar.phone.NotificationIconAreaController; import com.android.systemui.statusbar.phone.PhoneStatusBarPolicy; @@ -98,11 +102,13 @@ import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.NetworkController; import com.android.systemui.statusbar.policy.UserInfoControllerImpl; import com.android.systemui.statusbar.policy.UserSwitcherController; -import com.android.systemui.unfold.UnfoldLightRevealOverlayAnimation; import com.android.systemui.tuner.TunerService; +import com.android.systemui.unfold.UnfoldLightRevealOverlayAnimation; +import com.android.systemui.util.concurrency.DelayableExecutor; +import com.android.systemui.util.concurrency.MessageRouter; import com.android.systemui.volume.VolumeComponent; import com.android.systemui.wmshell.BubblesManager; -import com.android.unfold.config.UnfoldTransitionConfig; +import com.android.systemui.unfold.config.UnfoldTransitionConfig; import com.android.wm.shell.bubbles.Bubbles; import com.android.wm.shell.legacysplitscreen.LegacySplitScreen; import com.android.wm.shell.startingsurface.StartingSurface; @@ -143,6 +149,7 @@ public interface StatusBarPhoneModule { FalsingManager falsingManager, FalsingCollector falsingCollector, BroadcastDispatcher broadcastDispatcher, + NotificationEntryManager notificationEntryManager, NotificationGutsManager notificationGutsManager, NotificationLogger notificationLogger, NotificationInterruptStateProvider notificationInterruptStateProvider, @@ -172,6 +179,7 @@ public interface StatusBarPhoneModule { DozeParameters dozeParameters, ScrimController scrimController, Lazy<LockscreenWallpaper> lockscreenWallpaperLazy, + LockscreenGestureLogger lockscreenGestureLogger, Lazy<BiometricUnlockController> biometricUnlockControllerLazy, DozeServiceHost dozeServiceHost, PowerManager powerManager, @@ -195,6 +203,7 @@ public interface StatusBarPhoneModule { KeyguardDismissUtil keyguardDismissUtil, ExtensionController extensionController, UserInfoControllerImpl userInfoControllerImpl, + OperatorNameViewController.Factory operatorNameViewControllerFactory, PhoneStatusBarPolicy phoneStatusBarPolicy, KeyguardIndicationController keyguardIndicationController, DemoModeController demoModeController, @@ -211,6 +220,9 @@ public interface StatusBarPhoneModule { LockscreenShadeTransitionController transitionController, FeatureFlags featureFlags, KeyguardUnlockAnimationController keyguardUnlockAnimationController, + @Main Handler mainHandler, + @Main DelayableExecutor delayableExecutor, + @Main MessageRouter messageRouter, WallpaperManager wallpaperManager, UnlockedScreenOffAnimationController unlockedScreenOffAnimationController, Optional<StartingSurface> startingSurfaceOptional, @@ -231,6 +243,7 @@ public interface StatusBarPhoneModule { falsingManager, falsingCollector, broadcastDispatcher, + notificationEntryManager, notificationGutsManager, notificationLogger, notificationInterruptStateProvider, @@ -260,6 +273,7 @@ public interface StatusBarPhoneModule { dozeParameters, scrimController, lockscreenWallpaperLazy, + lockscreenGestureLogger, biometricUnlockControllerLazy, dozeServiceHost, powerManager, @@ -282,6 +296,7 @@ public interface StatusBarPhoneModule { keyguardDismissUtil, extensionController, userInfoControllerImpl, + operatorNameViewControllerFactory, phoneStatusBarPolicy, keyguardIndicationController, demoModeController, @@ -298,6 +313,9 @@ public interface StatusBarPhoneModule { transitionController, featureFlags, keyguardUnlockAnimationController, + mainHandler, + delayableExecutor, + messageRouter, wallpaperManager, unlockedScreenOffAnimationController, startingSurfaceOptional, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessPointControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessPointControllerImpl.java index ab58286859cc..5a3d72555d76 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessPointControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessPointControllerImpl.java @@ -40,6 +40,7 @@ import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.settings.UserTracker; +import com.android.wifitrackerlib.MergedCarrierEntry; import com.android.wifitrackerlib.WifiEntry; import com.android.wifitrackerlib.WifiPickerTracker; @@ -157,6 +158,15 @@ public class AccessPointControllerImpl } @Override + public MergedCarrierEntry getMergedCarrierEntry() { + if (mWifiPickerTracker == null) { + fireAcccessPointsCallback(Collections.emptyList()); + return null; + } + return mWifiPickerTracker.getMergedCarrierEntry(); + } + + @Override public int getIcon(WifiEntry ap) { int level = ap.getLevel(); return ICONS[Math.max(0, level)]; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java index ef2ca985858d..6b71f46238e4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java @@ -23,6 +23,7 @@ import android.telephony.SubscriptionInfo; import com.android.settingslib.net.DataUsageController; import com.android.systemui.demomode.DemoMode; import com.android.systemui.statusbar.policy.NetworkController.SignalCallback; +import com.android.wifitrackerlib.MergedCarrierEntry; import com.android.wifitrackerlib.WifiEntry; import java.util.List; @@ -223,6 +224,7 @@ public interface NetworkController extends CallbackController<SignalCallback>, D void addAccessPointCallback(AccessPointCallback callback); void removeAccessPointCallback(AccessPointCallback callback); void scanForAccessPoints(); + MergedCarrierEntry getMergedCarrierEntry(); int getIcon(WifiEntry ap); boolean connect(WifiEntry ap); boolean canConfigWifi(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java index 84d7c05ddc14..5d7d4809dd57 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java @@ -77,7 +77,6 @@ import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.UiEvent; import com.android.internal.logging.UiEventLogger; import com.android.internal.logging.nano.MetricsProto; -import com.android.internal.util.ContrastColorUtil; import com.android.systemui.Dependency; import com.android.systemui.R; import com.android.systemui.statusbar.NotificationRemoteInputManager; @@ -204,7 +203,7 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene final int stroke = colorized ? mContext.getResources().getDimensionPixelSize( R.dimen.remote_input_view_text_stroke) : 0; if (colorized) { - final boolean dark = !ContrastColorUtil.isColorLight(backgroundColor); + final boolean dark = Notification.Builder.isColorDark(backgroundColor); final int foregroundColor = dark ? Color.WHITE : Color.BLACK; final int inverseColor = dark ? Color.BLACK : Color.WHITE; editBgColor = backgroundColor; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java index 41b1dd12639a..4e33529f3c36 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java @@ -628,7 +628,7 @@ public class SmartReplyView extends ViewGroup { mCurrentBackgroundColor = backgroundColor; mCurrentColorized = colorized; - final boolean dark = !ContrastColorUtil.isColorLight(backgroundColor); + final boolean dark = Notification.Builder.isColorDark(backgroundColor); mCurrentTextColor = ContrastColorUtil.ensureTextContrast( dark ? mDefaultTextColorDarkBg : mDefaultTextColor, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/VariableDateView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/VariableDateView.kt new file mode 100644 index 000000000000..ae9d9ee445f2 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/VariableDateView.kt @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.policy + +import android.content.Context +import android.text.StaticLayout +import android.util.AttributeSet +import android.widget.TextView +import com.android.systemui.R + +/** + * View for showing a date that can toggle between two different formats depending on size. + * + * If no pattern can fit, it will display empty. + * + * @see R.styleable.VariableDateView_longDatePattern + * @see R.styleable.VariableDateView_shortDatePattern + */ +class VariableDateView(context: Context, attrs: AttributeSet) : TextView(context, attrs) { + + val longerPattern: String + val shorterPattern: String + + init { + val a = context.theme.obtainStyledAttributes( + attrs, + R.styleable.VariableDateView, + 0, 0) + longerPattern = a.getString(R.styleable.VariableDateView_longDatePattern) + ?: context.getString(R.string.system_ui_date_pattern) + shorterPattern = a.getString(R.styleable.VariableDateView_shortDatePattern) + ?: context.getString(R.string.abbrev_month_day_no_year) + + a.recycle() + } + + /** + * Freeze the pattern switching + * + * Use during animations if the container will change its size but this view should not change + */ + var freezeSwitching = false + + private var onMeasureListener: OnMeasureListener? = null + + fun onAttach(listener: OnMeasureListener?) { + onMeasureListener = listener + } + + override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { + val availableWidth = MeasureSpec.getSize(widthMeasureSpec) - paddingStart - paddingEnd + if (MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.UNSPECIFIED && !freezeSwitching) { + onMeasureListener?.onMeasureAction(availableWidth) + } + super.onMeasure(widthMeasureSpec, heightMeasureSpec) + } + + fun getDesiredWidthForText(text: CharSequence): Float { + return StaticLayout.getDesiredWidth(text, paint) + } + + interface OnMeasureListener { + fun onMeasureAction(availableWidth: Int) + } +}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/VariableDateViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/VariableDateViewController.kt new file mode 100644 index 000000000000..99d84c4d0ced --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/VariableDateViewController.kt @@ -0,0 +1,221 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.policy + +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.content.IntentFilter +import android.icu.text.DateFormat +import android.icu.text.DisplayContext +import android.icu.util.Calendar +import android.os.Handler +import android.os.HandlerExecutor +import android.os.UserHandle +import android.text.TextUtils +import android.util.Log +import androidx.annotation.VisibleForTesting +import com.android.systemui.Dependency +import com.android.systemui.broadcast.BroadcastDispatcher +import com.android.systemui.util.ViewController +import com.android.systemui.util.time.SystemClock +import java.text.FieldPosition +import java.text.ParsePosition +import java.util.Date +import java.util.Locale +import javax.inject.Inject +import javax.inject.Named + +@VisibleForTesting +internal fun getTextForFormat(date: Date?, format: DateFormat): String { + return if (format === EMPTY_FORMAT) { // Check if same object + "" + } else format.format(date) +} + +@VisibleForTesting +internal fun getFormatFromPattern(pattern: String?): DateFormat { + if (TextUtils.equals(pattern, "")) { + return EMPTY_FORMAT + } + val l = Locale.getDefault() + val format = DateFormat.getInstanceForSkeleton(pattern, l) + format.setContext(DisplayContext.CAPITALIZATION_FOR_STANDALONE) + return format +} + +private val EMPTY_FORMAT: DateFormat = object : DateFormat() { + override fun format( + cal: Calendar, + toAppendTo: StringBuffer, + fieldPosition: FieldPosition + ): StringBuffer? { + return null + } + + override fun parse(text: String, cal: Calendar, pos: ParsePosition) {} +} + +private const val DEBUG = false +private const val TAG = "VariableDateViewController" + +class VariableDateViewController( + private val systemClock: SystemClock, + private val broadcastDispatcher: BroadcastDispatcher, + private val timeTickHandler: Handler, + view: VariableDateView +) : ViewController<VariableDateView>(view) { + + private var dateFormat: DateFormat? = null + private var datePattern = view.longerPattern + set(value) { + if (field == value) return + field = value + dateFormat = null + if (isAttachedToWindow) { + post(::updateClock) + } + } + private var lastWidth = Integer.MAX_VALUE + private var lastText = "" + private var currentTime = Date() + + // View class easy accessors + private val longerPattern: String + get() = mView.longerPattern + private val shorterPattern: String + get() = mView.shorterPattern + private fun post(block: () -> Unit) = mView.handler?.post(block) + + private val intentReceiver: BroadcastReceiver = object : BroadcastReceiver() { + override fun onReceive(context: Context, intent: Intent) { + // If the handler is null, it means we received a broadcast while the view has not + // finished being attached or in the process of being detached. + // In that case, do not post anything. + val handler = mView.handler ?: return + val action = intent.action + if ( + Intent.ACTION_TIME_TICK == action || + Intent.ACTION_TIME_CHANGED == action || + Intent.ACTION_TIMEZONE_CHANGED == action || + Intent.ACTION_LOCALE_CHANGED == action + ) { + if ( + Intent.ACTION_LOCALE_CHANGED == action || + Intent.ACTION_TIMEZONE_CHANGED == action + ) { + // need to get a fresh date format + handler.post { dateFormat = null } + } + handler.post(::updateClock) + } + } + } + + private val onMeasureListener = object : VariableDateView.OnMeasureListener { + override fun onMeasureAction(availableWidth: Int) { + if (availableWidth != lastWidth) { + // maybeChangeFormat will post if the pattern needs to change. + maybeChangeFormat(availableWidth) + lastWidth = availableWidth + } + } + } + + override fun onViewAttached() { + val filter = IntentFilter().apply { + addAction(Intent.ACTION_TIME_TICK) + addAction(Intent.ACTION_TIME_CHANGED) + addAction(Intent.ACTION_TIMEZONE_CHANGED) + addAction(Intent.ACTION_LOCALE_CHANGED) + } + + broadcastDispatcher.registerReceiver(intentReceiver, filter, + HandlerExecutor(timeTickHandler), UserHandle.SYSTEM) + + post(::updateClock) + mView.onAttach(onMeasureListener) + } + + override fun onViewDetached() { + dateFormat = null + mView.onAttach(null) + broadcastDispatcher.unregisterReceiver(intentReceiver) + } + + private fun updateClock() { + if (dateFormat == null) { + dateFormat = getFormatFromPattern(datePattern) + } + + currentTime.time = systemClock.currentTimeMillis() + + val text = getTextForFormat(currentTime, dateFormat!!) + if (text != lastText) { + mView.setText(text) + lastText = text + } + } + + private fun maybeChangeFormat(availableWidth: Int) { + if (mView.freezeSwitching || + availableWidth > lastWidth && datePattern == longerPattern || + availableWidth < lastWidth && datePattern == "" + ) { + // Nothing to do + return + } + if (DEBUG) Log.d(TAG, "Width changed. Maybe changing pattern") + // Start with longer pattern and see what fits + var text = getTextForFormat(currentTime, getFormatFromPattern(longerPattern)) + var length = mView.getDesiredWidthForText(text) + if (length <= availableWidth) { + changePattern(longerPattern) + return + } + + text = getTextForFormat(currentTime, getFormatFromPattern(shorterPattern)) + length = mView.getDesiredWidthForText(text) + if (length <= availableWidth) { + changePattern(shorterPattern) + return + } + + changePattern("") + } + + private fun changePattern(newPattern: String) { + if (newPattern.equals(datePattern)) return + if (DEBUG) Log.d(TAG, "Changing pattern to $newPattern") + datePattern = newPattern + } + + class Factory @Inject constructor( + private val systemClock: SystemClock, + private val broadcastDispatcher: BroadcastDispatcher, + @Named(Dependency.TIME_TICK_HANDLER_NAME) private val handler: Handler + ) { + fun create(view: VariableDateView): VariableDateViewController { + return VariableDateViewController( + systemClock, + broadcastDispatcher, + handler, + view + ) + } + } +}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java index c807ad828e30..a53d528ac78c 100644 --- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java +++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java @@ -530,6 +530,10 @@ public class ThemeOverlayController extends SystemUI implements Dumpable { mThemeManager.applyCurrentUserOverlays(categoryToPackage, null, currentUser, managedProfiles); } + onOverlaysApplied(); + } + + protected void onOverlaysApplied() { } @Override diff --git a/packages/SystemUI/src/com/android/systemui/tuner/PluginFragment.java b/packages/SystemUI/src/com/android/systemui/tuner/PluginFragment.java index 0a29e04ce20f..20857eaba7d4 100644 --- a/packages/SystemUI/src/com/android/systemui/tuner/PluginFragment.java +++ b/packages/SystemUI/src/com/android/systemui/tuner/PluginFragment.java @@ -106,8 +106,8 @@ public class PluginFragment extends PreferenceFragment { PackageManager.MATCH_DISABLED_COMPONENTS | PackageManager.GET_SERVICES); apps.forEach(app -> { if (!plugins.containsKey(app.packageName)) return; - if (ArrayUtils.contains(manager.getWhitelistedPlugins(), app.packageName)) { - // Don't manage whitelisted plugins, they are part of the OS. + if (ArrayUtils.contains(manager.getPrivilegedPlugins(), app.packageName)) { + // Don't manage privileged plugins, they are part of the OS. return; } SwitchPreference pref = new PluginPreference(prefContext, app, mPluginEnabler); diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt index 8e7c49a4780c..9283403edd1a 100644 --- a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt +++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt @@ -23,8 +23,7 @@ import android.view.Surface import android.view.WindowManager import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Main -import com.android.unfold.UnfoldTransitionProgressProvider -import com.android.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener +import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener import com.android.systemui.statusbar.LightRevealScrim import com.android.systemui.statusbar.LinearLightRevealEffect import java.util.concurrent.Executor @@ -93,7 +92,7 @@ class UnfoldLightRevealOverlayAnimation @Inject constructor( val newScrimView = LightRevealScrim(context, null) .apply { revealEffect = LinearLightRevealEffect(isVerticalFold) - revealAmountListener = Consumer {} + isScrimOpaqueChangedListener = Consumer {} revealAmount = 0f } diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbPermissionActivity.java b/packages/SystemUI/src/com/android/systemui/usb/UsbPermissionActivity.java index 98b4209ede00..bfa50bcee270 100644 --- a/packages/SystemUI/src/com/android/systemui/usb/UsbPermissionActivity.java +++ b/packages/SystemUI/src/com/android/systemui/usb/UsbPermissionActivity.java @@ -36,6 +36,7 @@ import android.os.UserHandle; import android.util.Log; import android.view.LayoutInflater; import android.view.View; +import android.view.WindowManager; import android.widget.CheckBox; import android.widget.CompoundButton; import android.widget.TextView; @@ -63,6 +64,8 @@ public class UsbPermissionActivity extends AlertActivity public void onCreate(Bundle icicle) { super.onCreate(icicle); + getWindow().addPrivateFlags( + WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS); Intent intent = getIntent(); mDevice = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE); mAccessory = (UsbAccessory)intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY); diff --git a/packages/SystemUI/src/com/android/systemui/util/ViewController.java b/packages/SystemUI/src/com/android/systemui/util/ViewController.java index 32bbe1c44c5c..0dd5788105b7 100644 --- a/packages/SystemUI/src/com/android/systemui/util/ViewController.java +++ b/packages/SystemUI/src/com/android/systemui/util/ViewController.java @@ -110,16 +110,6 @@ public abstract class ViewController<T extends View> { } /** - * Destroys this controller so that it never receives view attach and detach events again. - * Does nothing if the view is null. - */ - public void destroy() { - if (mView != null) { - mView.removeOnAttachStateChangeListener(mOnAttachStateListener); - } - } - - /** * Called when the view is attached and a call to {@link #init()} has been made in either order. */ protected abstract void onViewAttached(); diff --git a/packages/SystemUI/src/com/android/systemui/util/concurrency/GlobalConcurrencyModule.java b/packages/SystemUI/src/com/android/systemui/util/concurrency/GlobalConcurrencyModule.java index 1c504961e715..107fe870a07a 100644 --- a/packages/SystemUI/src/com/android/systemui/util/concurrency/GlobalConcurrencyModule.java +++ b/packages/SystemUI/src/com/android/systemui/util/concurrency/GlobalConcurrencyModule.java @@ -22,8 +22,10 @@ import android.os.Looper; import com.android.systemui.dagger.qualifiers.Main; +import java.util.Optional; import java.util.concurrent.Executor; +import javax.inject.Named; import javax.inject.Singleton; import dagger.Binds; @@ -35,6 +37,7 @@ import dagger.Provides; */ @Module public abstract class GlobalConcurrencyModule { + public static final String PRE_HANDLER = "pre_handler"; /** * Binds {@link ThreadFactoryImpl} to {@link ThreadFactory}. @@ -64,13 +67,32 @@ public abstract class GlobalConcurrencyModule { * Provide a Main-Thread Executor. */ @Provides + @Singleton @Main public static Executor provideMainExecutor(Context context) { return context.getMainExecutor(); } + /** + * Provide a Main-Thread DelayableExecutor. + */ + @Provides + @Singleton + @Main + public static DelayableExecutor provideMainDelayableExecutor(@Main Looper looper) { + return new ExecutorImpl(looper); + } + + /** */ @Binds @Singleton public abstract Execution provideExecution(ExecutionImpl execution); + + /** */ + @Provides + @Named(PRE_HANDLER) + public static Optional<Thread.UncaughtExceptionHandler> providesUncaughtExceptionHandler() { + return Optional.ofNullable(Thread.getUncaughtExceptionPreHandler()); + } } diff --git a/packages/SystemUI/src/com/android/systemui/util/concurrency/SysUIConcurrencyModule.java b/packages/SystemUI/src/com/android/systemui/util/concurrency/SysUIConcurrencyModule.java index e9e794ea884b..e8a9bc702352 100644 --- a/packages/SystemUI/src/com/android/systemui/util/concurrency/SysUIConcurrencyModule.java +++ b/packages/SystemUI/src/com/android/systemui/util/concurrency/SysUIConcurrencyModule.java @@ -120,16 +120,6 @@ public abstract class SysUIConcurrencyModule { } /** - * Provide a Main-Thread Executor. - */ - @Provides - @SysUISingleton - @Main - public static DelayableExecutor provideMainDelayableExecutor(@Main Looper looper) { - return new ExecutorImpl(looper); - } - - /** * Provide a Background-Thread Executor by default. */ @Provides diff --git a/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java b/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java index 90e022a52d7a..bd1103982017 100644 --- a/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java +++ b/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java @@ -87,15 +87,23 @@ public class ProximitySensor implements ThresholdSensor { && (mLastPrimaryEvent == null || !mLastPrimaryEvent.getBelow() || !event.getBelow())) { - mSecondaryThresholdSensor.pause(); + chooseSensor(); if (mLastPrimaryEvent == null || !mLastPrimaryEvent.getBelow()) { // Only check the secondary as long as the primary thinks we're near. - mCancelSecondaryRunnable = null; + if (mCancelSecondaryRunnable != null) { + mCancelSecondaryRunnable.run(); + mCancelSecondaryRunnable = null; + } return; } else { // Check this sensor again in a moment. - mCancelSecondaryRunnable = mDelayableExecutor.executeDelayed( - mSecondaryThresholdSensor::resume, SECONDARY_PING_INTERVAL_MS); + mCancelSecondaryRunnable = mDelayableExecutor.executeDelayed(() -> { + // This is safe because we know that mSecondaryThresholdSensor + // is loaded, otherwise we wouldn't be here. + mPrimaryThresholdSensor.pause(); + mSecondaryThresholdSensor.resume(); + }, + SECONDARY_PING_INTERVAL_MS); } } logDebug("Secondary sensor event: " + event.getBelow() + "."); @@ -159,12 +167,8 @@ public class ProximitySensor implements ThresholdSensor { * of what is reported by the primary sensor. */ public void setSecondarySafe(boolean safe) { - mSecondarySafe = safe; - if (!mSecondarySafe) { - mSecondaryThresholdSensor.pause(); - } else { - mSecondaryThresholdSensor.resume(); - } + mSecondarySafe = mSecondaryThresholdSensor.isLoaded() && safe; + chooseSensor(); } /** @@ -209,16 +213,30 @@ public class ProximitySensor implements ThresholdSensor { return; } if (!mInitializedListeners) { + mPrimaryThresholdSensor.pause(); + mSecondaryThresholdSensor.pause(); mPrimaryThresholdSensor.register(mPrimaryEventListener); - if (!mSecondarySafe) { - mSecondaryThresholdSensor.pause(); - } mSecondaryThresholdSensor.register(mSecondaryEventListener); mInitializedListeners = true; } logDebug("Registering sensor listener"); - mPrimaryThresholdSensor.resume(); + mRegistered = true; + chooseSensor(); + } + + private void chooseSensor() { + mExecution.assertIsMainThread(); + if (!mRegistered || mPaused || mListeners.isEmpty()) { + return; + } + if (mSecondarySafe) { + mSecondaryThresholdSensor.resume(); + mPrimaryThresholdSensor.pause(); + } else { + mPrimaryThresholdSensor.resume(); + mSecondaryThresholdSensor.pause(); + } } /** @@ -312,7 +330,7 @@ public class ProximitySensor implements ThresholdSensor { } if (!mSecondarySafe && !event.getBelow()) { - mSecondaryThresholdSensor.pause(); + chooseSensor(); } mLastEvent = event; diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java index 7937dfd0dbdc..c178b29326f7 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java @@ -126,8 +126,8 @@ public abstract class WMShellBaseModule { @WMSingleton @Provides static DragAndDropController provideDragAndDropController(Context context, - DisplayController displayController) { - return new DragAndDropController(context, displayController); + DisplayController displayController, UiEventLogger uiEventLogger) { + return new DragAndDropController(context, displayController, uiEventLogger); } @WMSingleton diff --git a/packages/SystemUI/tests/AndroidManifest.xml b/packages/SystemUI/tests/AndroidManifest.xml index f1c687ff3224..2c9c98032245 100644 --- a/packages/SystemUI/tests/AndroidManifest.xml +++ b/packages/SystemUI/tests/AndroidManifest.xml @@ -93,6 +93,13 @@ <activity android:name="com.android.systemui.screenshot.RecyclerViewActivity" android:exported="false" /> + <!-- started from UsbDeviceSettingsManager --> + <activity android:name=".usb.UsbPermissionActivityTest$UsbPermissionActivityTestable" + android:exported="false" + android:theme="@style/Theme.SystemUI.Dialog.Alert" + android:finishOnCloseSystemDialogs="true" + android:excludeFromRecents="true" /> + <provider android:name="androidx.lifecycle.ProcessLifecycleOwnerInitializer" tools:replace="android:authorities" diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java index 76cc7a0eda13..77941366b4d3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java @@ -188,6 +188,7 @@ public class WindowMagnificationControllerTest extends SysuiTestCase { public void deleteWindowMagnification_enableAtTheBottom_overlapFlagIsFalse() { final WindowManager wm = mContext.getSystemService(WindowManager.class); final Rect bounds = wm.getCurrentWindowMetrics().getBounds(); + setSystemGestureInsets(); mInstrumentation.runOnMainSync(() -> { mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN, @@ -463,10 +464,7 @@ public class WindowMagnificationControllerTest extends SysuiTestCase { @Test public void moveWindowMagnificationToTheBottom_enabledWithGestureInset_overlapFlagIsTrue() { final Rect bounds = mWindowManager.getCurrentWindowMetrics().getBounds(); - final WindowInsets testInsets = new WindowInsets.Builder() - .setInsets(systemGestures(), Insets.of(0, 0, 0, 10)) - .build(); - mWindowManager.setWindowInsets(testInsets); + setSystemGestureInsets(); mInstrumentation.runOnMainSync(() -> { mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN, Float.NaN); @@ -492,4 +490,11 @@ public class WindowMagnificationControllerTest extends SysuiTestCase { private boolean hasMagnificationOverlapFlag() { return (mSysUiState.getFlags() & SYSUI_STATE_MAGNIFICATION_OVERLAP) != 0; } + + private void setSystemGestureInsets() { + final WindowInsets testInsets = new WindowInsets.Builder() + .setInsets(systemGestures(), Insets.of(0, 0, 0, 10)) + .build(); + mWindowManager.setWindowInsets(testInsets); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/battery/BatteryMeterViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/battery/BatteryMeterViewControllerTest.java index 903cbb522e4f..1d038a43ec8e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/battery/BatteryMeterViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/battery/BatteryMeterViewControllerTest.java @@ -35,6 +35,7 @@ import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; import com.android.systemui.broadcast.BroadcastDispatcher; +import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.tuner.TunerService; @@ -58,6 +59,8 @@ public class BatteryMeterViewControllerTest extends SysuiTestCase { private Handler mHandler; @Mock private ContentResolver mContentResolver; + @Mock + private BatteryController mBatteryController; private BatteryMeterViewController mController; @@ -74,7 +77,8 @@ public class BatteryMeterViewControllerTest extends SysuiTestCase { mTunerService, mBroadcastDispatcher, mHandler, - mContentResolver + mContentResolver, + mBatteryController ); } @@ -92,6 +96,7 @@ public class BatteryMeterViewControllerTest extends SysuiTestCase { anyBoolean(), any() ); + verify(mBatteryController).addCallback(any()); } @Test @@ -104,6 +109,7 @@ public class BatteryMeterViewControllerTest extends SysuiTestCase { verify(mConfigurationController).removeCallback(any()); verify(mTunerService).removeTunable(any()); verify(mContentResolver).unregisterContentObserver(any()); + verify(mBatteryController).removeCallback(any()); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/battery/BatteryMeterViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/battery/BatteryMeterViewTest.kt new file mode 100644 index 000000000000..b4ff2a5e1fbf --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/battery/BatteryMeterViewTest.kt @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.systemui.battery + +import android.testing.AndroidTestingRunner +import android.testing.TestableLooper.RunWithLooper +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.battery.BatteryMeterView.BatteryEstimateFetcher +import com.android.systemui.statusbar.policy.BatteryController.EstimateFetchCompletion +import com.google.common.truth.Truth.assertThat +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.MockitoAnnotations + +@SmallTest +@RunWith(AndroidTestingRunner::class) +@RunWithLooper +class BatteryMeterViewTest : SysuiTestCase() { + + private lateinit var mBatteryMeterView: BatteryMeterView + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + mBatteryMeterView = BatteryMeterView(mContext, null) + } + + @Test + fun updatePercentText_estimateModeAndNotCharging_estimateFetched() { + mBatteryMeterView.setPercentShowMode(BatteryMeterView.MODE_ESTIMATE) + mBatteryMeterView.setBatteryEstimateFetcher(Fetcher()) + + mBatteryMeterView.updatePercentText() + + assertThat(mBatteryMeterView.batteryPercentViewText).isEqualTo(ESTIMATE) + } + + @Test + fun updatePercentText_noBatteryEstimateFetcher_noCrash() { + mBatteryMeterView.setPercentShowMode(BatteryMeterView.MODE_ESTIMATE) + + mBatteryMeterView.updatePercentText() + // No assert needed + } + + private class Fetcher : BatteryEstimateFetcher { + override fun fetchBatteryTimeRemainingEstimate( + completion: EstimateFetchCompletion) { + completion.onBatteryRemainingEstimateRetrieved(ESTIMATE) + } + } + + private companion object { + const val ESTIMATE = "2 hours 2 minutes" + } +}
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java index 9098e44ad547..59010803db61 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java @@ -69,6 +69,7 @@ import com.android.systemui.util.concurrency.Execution; import com.android.systemui.util.concurrency.FakeExecution; import com.android.systemui.util.concurrency.FakeExecutor; import com.android.systemui.util.time.FakeSystemClock; +import com.android.systemui.util.time.SystemClock; import org.junit.Before; import org.junit.Rule; @@ -147,6 +148,8 @@ public class UdfpsControllerTest extends SysuiTestCase { private Handler mHandler; @Mock private ConfigurationController mConfigurationController; + @Mock + private SystemClock mSystemClock; private FakeExecutor mFgExecutor; @@ -229,7 +232,8 @@ public class UdfpsControllerTest extends SysuiTestCase { mKeyguardBypassController, mDisplayManager, mHandler, - mConfigurationController); + mConfigurationController, + mSystemClock); verify(mFingerprintManager).setUdfpsOverlayController(mOverlayCaptor.capture()); mOverlayController = mOverlayCaptor.getValue(); verify(mScreenLifecycle).addObserver(mScreenObserverCaptor.capture()); diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java index cc34c303ef2d..17730d960435 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java @@ -18,8 +18,11 @@ package com.android.systemui.biometrics; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.atLeast; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -41,6 +44,7 @@ import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.util.concurrency.DelayableExecutor; +import com.android.systemui.util.time.FakeSystemClock; import org.junit.Before; import org.junit.Test; @@ -81,6 +85,7 @@ public class UdfpsKeyguardViewControllerTest extends SysuiTestCase { private ConfigurationController mConfigurationController; @Mock private UdfpsController mUdfpsController; + private FakeSystemClock mSystemClock = new FakeSystemClock(); private UdfpsKeyguardViewController mController; @@ -115,6 +120,7 @@ public class UdfpsKeyguardViewControllerTest extends SysuiTestCase { mKeyguardViewMediator, mLockscreenShadeTransitionController, mConfigurationController, + mSystemClock, mUdfpsController); } @@ -274,6 +280,59 @@ public class UdfpsKeyguardViewControllerTest extends SysuiTestCase { verify(mStatusBarKeyguardViewManager).removeAlternateAuthInterceptor(mAltAuthInterceptor); } + @Test + public void testHiddenUdfpsBouncerOnTouchOutside_nothingHappens() { + // GIVEN view is attached + mController.onViewAttached(); + captureAltAuthInterceptor(); + + // GIVEN udfps bouncer isn't showing + mAltAuthInterceptor.hideAlternateAuthBouncer(); + + // WHEN touch is observed outside the view + mController.onTouchOutsideView(); + + // THEN bouncer / alt auth methods are never called + verify(mStatusBarKeyguardViewManager, never()).showBouncer(anyBoolean()); + verify(mStatusBarKeyguardViewManager, never()).resetAlternateAuth(anyBoolean()); + } + + @Test + public void testShowingUdfpsBouncerOnTouchOutsideWithinThreshold_nothingHappens() { + // GIVEN view is attached + mController.onViewAttached(); + captureAltAuthInterceptor(); + + // GIVEN udfps bouncer is showing + mAltAuthInterceptor.showAlternateAuthBouncer(); + + // WHEN touch is observed outside the view 200ms later (just within threshold) + mSystemClock.advanceTime(200); + mController.onTouchOutsideView(); + + // THEN bouncer / alt auth methods are never called because not enough time has passed + verify(mStatusBarKeyguardViewManager, never()).showBouncer(anyBoolean()); + verify(mStatusBarKeyguardViewManager, never()).resetAlternateAuth(anyBoolean()); + } + + @Test + public void testShowingUdfpsBouncerOnTouchOutsideAboveThreshold_showInputBouncer() { + // GIVEN view is attached + mController.onViewAttached(); + captureAltAuthInterceptor(); + + // GIVEN udfps bouncer is showing + mAltAuthInterceptor.showAlternateAuthBouncer(); + + // WHEN touch is observed outside the view 205ms later + mSystemClock.advanceTime(205); + mController.onTouchOutsideView(); + + // THEN show the bouncer and reset alt auth + verify(mStatusBarKeyguardViewManager).showBouncer(eq(true)); + verify(mStatusBarKeyguardViewManager).resetAlternateAuth(anyBoolean()); + } + private void sendStatusBarStateChanged(int statusBarState) { mStatusBarStateListener.onStateChanged(statusBarState); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationUtil.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationUtil.java index d6226aa53f67..a32cb9b6baa9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationUtil.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationUtil.java @@ -43,6 +43,7 @@ public class DozeConfigurationUtil { when(params.singleTapUsesProx()).thenReturn(true); when(params.longPressUsesProx()).thenReturn(true); when(params.getQuickPickupAodDuration()).thenReturn(500); + when(params.brightnessUsesProx()).thenReturn(true); doneHolder[0] = true; return params; diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java index 4e8b59c95681..deb7d31d87a3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java @@ -29,6 +29,7 @@ import static com.android.systemui.doze.DozeMachine.State.UNINITIALIZED; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNotSame; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; @@ -47,6 +48,7 @@ import android.view.Display; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; +import com.android.systemui.dock.DockManager; import com.android.systemui.keyguard.WakefulnessLifecycle; import com.android.systemui.statusbar.phone.DozeParameters; import com.android.systemui.util.concurrency.FakeExecutor; @@ -82,6 +84,8 @@ public class DozeScreenBrightnessTest extends SysuiTestCase { WakefulnessLifecycle mWakefulnessLifecycle; @Mock DozeParameters mDozeParameters; + @Mock + DockManager mDockManager; private FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock()); private FakeThreadFactory mFakeThreadFactory = new FakeThreadFactory(mFakeExecutor); @@ -109,7 +113,7 @@ public class DozeScreenBrightnessTest extends SysuiTestCase { mSensor = fakeSensorManager.getFakeLightSensor(); mScreen = new DozeScreenBrightness(mContext, mServiceFake, mSensorManager, Optional.of(mSensor.getSensor()), mDozeHost, null /* handler */, - mAlwaysOnDisplayPolicy, mWakefulnessLifecycle, mDozeParameters); + mAlwaysOnDisplayPolicy, mWakefulnessLifecycle, mDozeParameters, mDockManager); mScreen.onScreenState(Display.STATE_ON); } @@ -157,6 +161,67 @@ public class DozeScreenBrightnessTest extends SysuiTestCase { } @Test + public void testAodDocked_doNotSelectivelyUseProx_usesLightSensor() { + // GIVEN the device doesn't need to selectively register for prox sensors and + // brightness sensor uses prox + when(mDozeParameters.getSelectivelyRegisterSensorsUsingProx()).thenReturn(false); + when(mDozeParameters.brightnessUsesProx()).thenReturn(true); + + // GIVEN the device is docked and the display state changes to ON + when(mDockManager.isDocked()).thenReturn(true); + mScreen.onScreenState(Display.STATE_ON); + waitForSensorManager(); + + // WHEN new sensor event sent + mSensor.sendSensorEvent(3); + + // THEN brightness is updated + assertEquals(3, mServiceFake.screenBrightness); + } + + @Test + public void testAodDocked_brightnessDoesNotUseProx_usesLightSensor() { + // GIVEN the device doesn't need to selectively register for prox sensors but + // the brightness sensor doesn't use prox + when(mDozeParameters.getSelectivelyRegisterSensorsUsingProx()).thenReturn(true); + when(mDozeParameters.brightnessUsesProx()).thenReturn(false); + + // GIVEN the device is docked and the display state changes to ON + when(mDockManager.isDocked()).thenReturn(true); + mScreen.onScreenState(Display.STATE_ON); + waitForSensorManager(); + + // WHEN new sensor event sent + mSensor.sendSensorEvent(3); + + // THEN brightness is updated + assertEquals(3, mServiceFake.screenBrightness); + } + + + @Test + public void testAodDocked_noProx_brightnessUsesProx_doNotUseLightSensor() { + final int startBrightness = mServiceFake.screenBrightness; + + // GIVEN the device needs to selectively register for prox sensors and + // the brightness sensor uses prox + when(mDozeParameters.getSelectivelyRegisterSensorsUsingProx()).thenReturn(true); + when(mDozeParameters.brightnessUsesProx()).thenReturn(true); + + // GIVEN the device is docked and the display state is on + when(mDockManager.isDocked()).thenReturn(true); + mScreen.onScreenState(Display.STATE_ON); + waitForSensorManager(); + + // WHEN new sensor event sent + mSensor.sendSensorEvent(3); + + // THEN brightness is NOT changed + assertNotSame(3, mServiceFake.screenBrightness); + assertEquals(startBrightness, mServiceFake.screenBrightness); + } + + @Test public void testPausingAod_doesNotResetBrightness() throws Exception { mScreen.transitionTo(UNINITIALIZED, INITIALIZED); mScreen.transitionTo(INITIALIZED, DOZE_AOD); @@ -175,7 +240,7 @@ public class DozeScreenBrightnessTest extends SysuiTestCase { public void testPulsing_withoutLightSensor_setsAoDDimmingScrimTransparent() throws Exception { mScreen = new DozeScreenBrightness(mContext, mServiceFake, mSensorManager, Optional.empty() /* sensor */, mDozeHost, null /* handler */, - mAlwaysOnDisplayPolicy, mWakefulnessLifecycle, mDozeParameters); + mAlwaysOnDisplayPolicy, mWakefulnessLifecycle, mDozeParameters, mDockManager); mScreen.transitionTo(UNINITIALIZED, INITIALIZED); mScreen.transitionTo(INITIALIZED, DOZE); reset(mDozeHost); @@ -216,7 +281,7 @@ public class DozeScreenBrightnessTest extends SysuiTestCase { public void testNullSensor() throws Exception { mScreen = new DozeScreenBrightness(mContext, mServiceFake, mSensorManager, Optional.empty() /* sensor */, mDozeHost, null /* handler */, - mAlwaysOnDisplayPolicy, mWakefulnessLifecycle, mDozeParameters); + mAlwaysOnDisplayPolicy, mWakefulnessLifecycle, mDozeParameters, mDockManager); mScreen.transitionTo(UNINITIALIZED, INITIALIZED); mScreen.transitionTo(INITIALIZED, DOZE_AOD); diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java index 8c0d21e911da..0e344a6269b7 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java @@ -21,6 +21,7 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; @@ -51,6 +52,7 @@ import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.UiEventLogger; import com.android.internal.statusbar.IStatusBarService; import com.android.internal.widget.LockPatternUtils; +import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.SysuiTestCase; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.colorextraction.SysuiColorExtractor; @@ -112,6 +114,7 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase { @Mock private Handler mHandler; @Mock private UserContextProvider mUserContextProvider; @Mock private StatusBar mStatusBar; + @Mock private KeyguardUpdateMonitor mKeyguardUpdateMonitor; private TestableLooper mTestableLooper; @@ -155,7 +158,8 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase { mSysUiState, mHandler, mPackageManager, - Optional.of(mStatusBar) + Optional.of(mStatusBar), + mKeyguardUpdateMonitor ); mGlobalActionsDialogLite.setZeroDialogPressDelayForTesting(); @@ -421,4 +425,31 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase { restartAction.onLongPress(); verifyLogPosted(GlobalActionsDialog.GlobalActionsEvent.GA_REBOOT_LONG_PRESS); } + + @Test + public void testOnLockScreen_disableSmartLock() { + mGlobalActionsDialogLite = spy(mGlobalActionsDialogLite); + int user = KeyguardUpdateMonitor.getCurrentUser(); + doReturn(4).when(mGlobalActionsDialogLite).getMaxShownPowerItems(); + doReturn(true).when(mGlobalActionsDialogLite).shouldDisplayLockdown(any()); + doReturn(true).when(mGlobalActionsDialogLite).shouldShowAction(any()); + doReturn(false).when(mStatusBar).isKeyguardShowing(); + String[] actions = { + GlobalActionsDialogLite.GLOBAL_ACTION_KEY_EMERGENCY, + GlobalActionsDialogLite.GLOBAL_ACTION_KEY_LOCKDOWN, + GlobalActionsDialogLite.GLOBAL_ACTION_KEY_POWER, + GlobalActionsDialogLite.GLOBAL_ACTION_KEY_RESTART, + }; + doReturn(actions).when(mGlobalActionsDialogLite).getDefaultActions(); + + // When entering power menu from lockscreen, with smart lock enabled + when(mKeyguardUpdateMonitor.getUserHasTrust(anyInt())).thenReturn(true); + mGlobalActionsDialogLite.showOrHideDialog(true, true); + + // Then smart lock will be disabled + verify(mLockPatternUtils).requireCredentialEntry(eq(user)); + + // hide dialog again + mGlobalActionsDialogLite.showOrHideDialog(true, true); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java index 6d1db378bf2a..f8ab42fe3df5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java @@ -56,6 +56,7 @@ import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.UiEventLogger; import com.android.internal.statusbar.IStatusBarService; import com.android.internal.widget.LockPatternUtils; +import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.SysuiTestCase; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.colorextraction.SysuiColorExtractor; @@ -127,6 +128,7 @@ public class GlobalActionsDialogTest extends SysuiTestCase { @Mock private PackageManager mPackageManager; @Mock private SecureSettings mSecureSettings; @Mock private StatusBar mStatusBar; + @Mock private KeyguardUpdateMonitor mKeyguardUpdateMonitor; private TestableLooper mTestableLooper; @@ -170,7 +172,8 @@ public class GlobalActionsDialogTest extends SysuiTestCase { mSysUiState, mHandler, mPackageManager, - Optional.of(mStatusBar) + Optional.of(mStatusBar), + mKeyguardUpdateMonitor ); mGlobalActionsDialog.setZeroDialogPressDelayForTesting(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java new file mode 100644 index 000000000000..d279bbb02cee --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java @@ -0,0 +1,189 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.keyguard; + +import static junit.framework.Assert.assertEquals; + +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.content.res.Resources; +import android.graphics.PointF; +import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; +import android.os.Vibrator; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; +import android.util.DisplayMetrics; +import android.view.View; +import android.view.accessibility.AccessibilityManager; + +import androidx.test.filters.SmallTest; + +import com.android.keyguard.KeyguardUpdateMonitor; +import com.android.keyguard.KeyguardViewController; +import com.android.keyguard.LockIconView; +import com.android.keyguard.LockIconViewController; +import com.android.systemui.SysuiTestCase; +import com.android.systemui.biometrics.AuthController; +import com.android.systemui.biometrics.AuthRippleController; +import com.android.systemui.dump.DumpManager; +import com.android.systemui.plugins.FalsingManager; +import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.statusbar.policy.ConfigurationController; +import com.android.systemui.statusbar.policy.KeyguardStateController; +import com.android.systemui.util.concurrency.DelayableExecutor; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.ArrayList; +import java.util.List; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper +public class LockIconViewControllerTest extends SysuiTestCase { + private @Mock LockIconView mLockIconView; + private @Mock Context mContext; + private @Mock Resources mResources; + private @Mock DisplayMetrics mDisplayMetrics; + private @Mock StatusBarStateController mStatusBarStateController; + private @Mock KeyguardUpdateMonitor mKeyguardUpdateMonitor; + private @Mock KeyguardViewController mKeyguardViewController; + private @Mock KeyguardStateController mKeyguardStateController; + private @Mock FalsingManager mFalsingManager; + private @Mock AuthController mAuthController; + private @Mock DumpManager mDumpManager; + private @Mock AccessibilityManager mAccessibilityManager; + private @Mock ConfigurationController mConfigurationController; + private @Mock DelayableExecutor mDelayableExecutor; + private @Mock Vibrator mVibrator; + private @Mock AuthRippleController mAuthRippleController; + + private LockIconViewController mLockIconViewController; + + // Capture listeners so that they can be used to send events + @Captor private ArgumentCaptor<View.OnAttachStateChangeListener> mAttachCaptor = + ArgumentCaptor.forClass(View.OnAttachStateChangeListener.class); + private View.OnAttachStateChangeListener mAttachListener; + + @Captor private ArgumentCaptor<AuthController.Callback> mAuthControllerCallbackCaptor; + private AuthController.Callback mAuthControllerCallback; + + @Captor private ArgumentCaptor<PointF> mPointCaptor; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + + when(mLockIconView.getResources()).thenReturn(mResources); + when(mLockIconView.getContext()).thenReturn(mContext); + when(mContext.getResources()).thenReturn(mResources); + when(mResources.getDisplayMetrics()).thenReturn(mDisplayMetrics); + + mLockIconViewController = new LockIconViewController( + mLockIconView, + mStatusBarStateController, + mKeyguardUpdateMonitor, + mKeyguardViewController, + mKeyguardStateController, + mFalsingManager, + mAuthController, + mDumpManager, + mAccessibilityManager, + mConfigurationController, + mDelayableExecutor, + mVibrator, + mAuthRippleController + ); + } + + @Test + public void testUpdateFingerprintLocationOnInit() { + // GIVEN fp sensor location is available pre-init + final PointF udfpsLocation = new PointF(50, 75); + final int radius = 33; + final FingerprintSensorPropertiesInternal fpProps = + new FingerprintSensorPropertiesInternal( + /* sensorId */ 0, + /* strength */ 0, + /* max enrollments per user */ 5, + /* component info */ new ArrayList<>(), + /* sensorType */ 3, + /* resetLockoutRequiresHwToken */ false, + (int) udfpsLocation.x, (int) udfpsLocation.y, radius); + when(mAuthController.getUdfpsSensorLocation()).thenReturn(udfpsLocation); + when(mAuthController.getUdfpsProps()).thenReturn(List.of(fpProps)); + + // WHEN lock icon view controller is initialized and attached + mLockIconViewController.init(); + captureAttachListener(); + mAttachListener.onViewAttachedToWindow(null); + + // THEN lock icon view location is updated with the same coordinates as fpProps + verify(mLockIconView).setCenterLocation(mPointCaptor.capture(), eq(radius)); + assertEquals(udfpsLocation, mPointCaptor.getValue()); + } + + @Test + public void testUpdateFingerprintLocationOnAuthenticatorsRegistered() { + // GIVEN fp sensor location is not available pre-init + when(mAuthController.getFingerprintSensorLocation()).thenReturn(null); + when(mAuthController.getUdfpsProps()).thenReturn(null); + mLockIconViewController.init(); + + // GIVEN fp sensor location is available post-init + captureAuthControllerCallback(); + final PointF udfpsLocation = new PointF(50, 75); + final int radius = 33; + final FingerprintSensorPropertiesInternal fpProps = + new FingerprintSensorPropertiesInternal( + /* sensorId */ 0, + /* strength */ 0, + /* max enrollments per user */ 5, + /* component info */ new ArrayList<>(), + /* sensorType */ 3, + /* resetLockoutRequiresHwToken */ false, + (int) udfpsLocation.x, (int) udfpsLocation.y, radius); + when(mAuthController.getUdfpsSensorLocation()).thenReturn(udfpsLocation); + when(mAuthController.getUdfpsProps()).thenReturn(List.of(fpProps)); + + // WHEN all authenticators are registered + mAuthControllerCallback.onAllAuthenticatorsRegistered(); + + // THEN lock icon view location is updated with the same coordinates as fpProps + verify(mLockIconView).setCenterLocation(mPointCaptor.capture(), eq(radius)); + assertEquals(udfpsLocation, mPointCaptor.getValue()); + } + + private void captureAuthControllerCallback() { + verify(mAuthController).addCallback(mAuthControllerCallbackCaptor.capture()); + mAuthControllerCallback = mAuthControllerCallbackCaptor.getValue(); + } + + private void captureAttachListener() { + verify(mLockIconView).addOnAttachStateChangeListener(mAttachCaptor.capture()); + mAttachListener = mAttachCaptor.getValue(); + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/AutoAddTrackerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/AutoAddTrackerTest.java index de7abf866f6a..922c6b648aab 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/AutoAddTrackerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/AutoAddTrackerTest.java @@ -14,13 +14,19 @@ package com.android.systemui.qs; -import static com.android.systemui.statusbar.phone.AutoTileManager.INVERSION; import static com.android.systemui.statusbar.phone.AutoTileManager.SAVER; -import static com.android.systemui.statusbar.phone.AutoTileManager.WORK; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; - +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; + +import android.content.BroadcastReceiver; +import android.content.Intent; +import android.content.IntentFilter; import android.os.UserHandle; import android.provider.Settings.Secure; import android.testing.AndroidTestingRunner; @@ -28,13 +34,24 @@ import android.testing.TestableLooper.RunWithLooper; import androidx.test.filters.SmallTest; -import com.android.systemui.Prefs; -import com.android.systemui.Prefs.Key; import com.android.systemui.SysuiTestCase; +import com.android.systemui.broadcast.BroadcastDispatcher; +import com.android.systemui.dump.DumpManager; +import com.android.systemui.util.settings.FakeSettings; +import com.android.systemui.util.settings.SecureSettings; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.InOrder; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; + +import java.util.List; +import java.util.concurrent.Executor; @RunWith(AndroidTestingRunner.class) @RunWithLooper @@ -43,42 +60,38 @@ public class AutoAddTrackerTest extends SysuiTestCase { private static final int USER = 0; + @Mock + private BroadcastDispatcher mBroadcastDispatcher; + @Mock + private QSHost mQSHost; + @Mock + private DumpManager mDumpManager; + @Captor + private ArgumentCaptor<BroadcastReceiver> mBroadcastReceiverArgumentCaptor; + @Captor + private ArgumentCaptor<IntentFilter> mIntentFilterArgumentCaptor; + + private Executor mBackgroundExecutor = Runnable::run; // Direct executor private AutoAddTracker mAutoTracker; + private SecureSettings mSecureSettings; @Before public void setUp() { - Secure.putString(mContext.getContentResolver(), Secure.QS_AUTO_ADDED_TILES, ""); - } + MockitoAnnotations.initMocks(this); - @Test - public void testMigration() { - Prefs.putBoolean(mContext, Key.QS_DATA_SAVER_ADDED, true); - Prefs.putBoolean(mContext, Key.QS_WORK_ADDED, true); - mAutoTracker = new AutoAddTracker(mContext, USER); - mAutoTracker.initialize(); + mSecureSettings = new FakeSettings(); - assertTrue(mAutoTracker.isAdded(SAVER)); - assertTrue(mAutoTracker.isAdded(WORK)); - assertFalse(mAutoTracker.isAdded(INVERSION)); + mSecureSettings.putStringForUser(Secure.QS_AUTO_ADDED_TILES, null, USER); - // These keys have been removed; retrieving their values should always return the default. - assertTrue(Prefs.getBoolean(mContext, Key.QS_DATA_SAVER_ADDED, true )); - assertFalse(Prefs.getBoolean(mContext, Key.QS_DATA_SAVER_ADDED, false)); - assertTrue(Prefs.getBoolean(mContext, Key.QS_WORK_ADDED, true)); - assertFalse(Prefs.getBoolean(mContext, Key.QS_WORK_ADDED, false)); - - mAutoTracker.destroy(); + mAutoTracker = createAutoAddTracker(USER); + mAutoTracker.initialize(); } @Test public void testChangeFromBackup() { - mAutoTracker = new AutoAddTracker(mContext, USER); - mAutoTracker.initialize(); - assertFalse(mAutoTracker.isAdded(SAVER)); - Secure.putString(mContext.getContentResolver(), Secure.QS_AUTO_ADDED_TILES, SAVER); - mAutoTracker.mObserver.onChange(false); + mSecureSettings.putStringForUser(Secure.QS_AUTO_ADDED_TILES, SAVER, USER); assertTrue(mAutoTracker.isAdded(SAVER)); @@ -87,9 +100,6 @@ public class AutoAddTrackerTest extends SysuiTestCase { @Test public void testSetAdded() { - mAutoTracker = new AutoAddTracker(mContext, USER); - mAutoTracker.initialize(); - assertFalse(mAutoTracker.isAdded(SAVER)); mAutoTracker.setTileAdded(SAVER); @@ -100,14 +110,12 @@ public class AutoAddTrackerTest extends SysuiTestCase { @Test public void testPersist() { - mAutoTracker = new AutoAddTracker(mContext, USER); - mAutoTracker.initialize(); - assertFalse(mAutoTracker.isAdded(SAVER)); mAutoTracker.setTileAdded(SAVER); mAutoTracker.destroy(); - mAutoTracker = new AutoAddTracker(mContext, USER); + mAutoTracker = createAutoAddTracker(USER); + mAutoTracker.initialize(); assertTrue(mAutoTracker.isAdded(SAVER)); @@ -116,22 +124,158 @@ public class AutoAddTrackerTest extends SysuiTestCase { @Test public void testIndependentUsers() { - mAutoTracker = new AutoAddTracker(mContext, USER); - mAutoTracker.initialize(); mAutoTracker.setTileAdded(SAVER); - mAutoTracker = new AutoAddTracker(mContext, USER + 1); + mAutoTracker = createAutoAddTracker(USER + 1); + mAutoTracker.initialize(); assertFalse(mAutoTracker.isAdded(SAVER)); } @Test public void testChangeUser() { - mAutoTracker = new AutoAddTracker(mContext, USER); - mAutoTracker.initialize(); mAutoTracker.setTileAdded(SAVER); - mAutoTracker = new AutoAddTracker(mContext, USER + 1); + mAutoTracker = createAutoAddTracker(USER + 1); mAutoTracker.changeUser(UserHandle.of(USER)); assertTrue(mAutoTracker.isAdded(SAVER)); } + + @Test + public void testBroadcastReceiverRegistered() { + verify(mBroadcastDispatcher).registerReceiver( + any(), mIntentFilterArgumentCaptor.capture(), any(), eq(UserHandle.of(USER))); + + assertTrue( + mIntentFilterArgumentCaptor.getValue().hasAction(Intent.ACTION_SETTING_RESTORED)); + } + + @Test + public void testBroadcastReceiverChangesWithUser() { + mAutoTracker.changeUser(UserHandle.of(USER + 1)); + + InOrder inOrder = Mockito.inOrder(mBroadcastDispatcher); + inOrder.verify(mBroadcastDispatcher).unregisterReceiver(any()); + inOrder.verify(mBroadcastDispatcher) + .registerReceiver(any(), any(), any(), eq(UserHandle.of(USER + 1))); + } + + @Test + public void testSettingRestoredWithTilesNotRemovedInSource_noAutoAddedInTarget() { + verify(mBroadcastDispatcher).registerReceiver( + mBroadcastReceiverArgumentCaptor.capture(), any(), any(), any()); + + // These tiles were present in the original device + String restoredTiles = "saver,work,internet,cast"; + Intent restoreTilesIntent = makeRestoreIntent(Secure.QS_TILES, null, restoredTiles); + mBroadcastReceiverArgumentCaptor.getValue().onReceive(mContext, restoreTilesIntent); + + // And these tiles have been auto-added in the original device + // (no auto-added before restore) + String restoredAutoAddTiles = "work"; + Intent restoreAutoAddTilesIntent = + makeRestoreIntent(Secure.QS_AUTO_ADDED_TILES, null, restoredAutoAddTiles); + mBroadcastReceiverArgumentCaptor.getValue().onReceive(mContext, restoreAutoAddTilesIntent); + + // Then, don't remove any current tiles + verify(mQSHost, never()).removeTiles(any()); + assertEquals(restoredAutoAddTiles, + mSecureSettings.getStringForUser(Secure.QS_AUTO_ADDED_TILES, USER)); + } + + @Test + public void testSettingRestoredWithTilesRemovedInSource_noAutoAddedInTarget() { + verify(mBroadcastDispatcher) + .registerReceiver(mBroadcastReceiverArgumentCaptor.capture(), any(), any(), any()); + + // These tiles were present in the original device + String restoredTiles = "saver,internet,cast"; + Intent restoreTilesIntent = makeRestoreIntent(Secure.QS_TILES, null, restoredTiles); + mBroadcastReceiverArgumentCaptor.getValue().onReceive(mContext, restoreTilesIntent); + + // And these tiles have been auto-added in the original device + // (no auto-added before restore) + String restoredAutoAddTiles = "work"; + Intent restoreAutoAddTilesIntent = + makeRestoreIntent(Secure.QS_AUTO_ADDED_TILES, null, restoredAutoAddTiles); + mBroadcastReceiverArgumentCaptor.getValue().onReceive(mContext, restoreAutoAddTilesIntent); + + // Then, remove work tile + verify(mQSHost).removeTiles(List.of("work")); + assertEquals(restoredAutoAddTiles, + mSecureSettings.getStringForUser(Secure.QS_AUTO_ADDED_TILES, USER)); + } + + @Test + public void testSettingRestoredWithTilesRemovedInSource_sameAutoAddedinTarget() { + verify(mBroadcastDispatcher) + .registerReceiver(mBroadcastReceiverArgumentCaptor.capture(), any(), any(), any()); + + // These tiles were present in the original device + String restoredTiles = "saver,internet,cast"; + Intent restoreTilesIntent = + makeRestoreIntent(Secure.QS_TILES, "saver, internet, cast, work", restoredTiles); + mBroadcastReceiverArgumentCaptor.getValue().onReceive(mContext, restoreTilesIntent); + + // And these tiles have been auto-added in the original device + // (no auto-added before restore) + String restoredAutoAddTiles = "work"; + Intent restoreAutoAddTilesIntent = + makeRestoreIntent(Secure.QS_AUTO_ADDED_TILES, "work", restoredAutoAddTiles); + mBroadcastReceiverArgumentCaptor.getValue().onReceive(mContext, restoreAutoAddTilesIntent); + + // Then, remove work tile + verify(mQSHost).removeTiles(List.of("work")); + assertEquals(restoredAutoAddTiles, + mSecureSettings.getStringForUser(Secure.QS_AUTO_ADDED_TILES, USER)); + } + + @Test + public void testSettingRestoredWithTilesRemovedInSource_othersAutoAddedinTarget() { + verify(mBroadcastDispatcher) + .registerReceiver(mBroadcastReceiverArgumentCaptor.capture(), any(), any(), any()); + + // These tiles were present in the original device + String restoredTiles = "saver,internet,cast"; + Intent restoreTilesIntent = + makeRestoreIntent(Secure.QS_TILES, "saver, internet, cast, work", restoredTiles); + mBroadcastReceiverArgumentCaptor.getValue().onReceive(mContext, restoreTilesIntent); + + // And these tiles have been auto-added in the original device + // (no auto-added before restore) + String restoredAutoAddTiles = "work"; + Intent restoreAutoAddTilesIntent = + makeRestoreIntent(Secure.QS_AUTO_ADDED_TILES, "inversion", restoredAutoAddTiles); + mBroadcastReceiverArgumentCaptor.getValue().onReceive(mContext, restoreAutoAddTilesIntent); + + // Then, remove work tile + verify(mQSHost).removeTiles(List.of("work")); + + String setting = mSecureSettings.getStringForUser(Secure.QS_AUTO_ADDED_TILES, USER); + assertEquals(2, setting.split(",").length); + assertTrue(setting.contains("work")); + assertTrue(setting.contains("inversion")); + } + + + private Intent makeRestoreIntent( + String settingName, String previousValue, String restoredValue) { + Intent intent = new Intent(Intent.ACTION_SETTING_RESTORED); + intent.putExtra(Intent.EXTRA_SETTING_NAME, settingName); + intent.putExtra(Intent.EXTRA_SETTING_PREVIOUS_VALUE, previousValue); + intent.putExtra(Intent.EXTRA_SETTING_NEW_VALUE, restoredValue); + return intent; + } + + private AutoAddTracker createAutoAddTracker(int user) { + // Null handler wil dispatch sync. + return new AutoAddTracker( + mSecureSettings, + mBroadcastDispatcher, + mQSHost, + mDumpManager, + null, + mBackgroundExecutor, + user + ); + } }
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java index c9dc7ad6c5e8..6c7f770f9482 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java @@ -18,14 +18,11 @@ package com.android.systemui.qs; import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertTrue; -import static junit.framework.TestCase.assertFalse; -import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.isNull; -import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; @@ -68,12 +65,12 @@ import com.android.systemui.statusbar.phone.AutoTileManager; import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.phone.StatusBarIconController; import com.android.systemui.tuner.TunerService; +import com.android.systemui.util.settings.FakeSettings; import com.android.systemui.util.settings.SecureSettings; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @@ -121,7 +118,6 @@ public class QSTileHostTest extends SysuiTestCase { private UiEventLogger mUiEventLogger; @Mock private UserTracker mUserTracker; - @Mock private SecureSettings mSecureSettings; @Mock private CustomTileStatePersister mCustomTileStatePersister; @@ -135,14 +131,15 @@ public class QSTileHostTest extends SysuiTestCase { MockitoAnnotations.initMocks(this); mLooper = TestableLooper.get(this); mHandler = new Handler(mLooper.getLooper()); + + mSecureSettings = new FakeSettings(); + mSecureSettings.putStringForUser( + QSTileHost.TILES_SETTING, "", "", false, mUserTracker.getUserId(), false); mQSTileHost = new TestQSTileHost(mContext, mIconController, mDefaultFactory, mHandler, mLooper.getLooper(), mPluginManager, mTunerService, mAutoTiles, mDumpManager, mBroadcastDispatcher, mStatusBar, mQSLogger, mUiEventLogger, mUserTracker, mSecureSettings, mCustomTileStatePersister); setUpTileFactory(); - - when(mSecureSettings.getStringForUser(eq(QSTileHost.TILES_SETTING), anyInt())) - .thenReturn(""); } private void setUpTileFactory() { @@ -365,6 +362,16 @@ public class QSTileHostTest extends SysuiTestCase { .removeState(new TileServiceKey(CUSTOM_TILE, mQSTileHost.getUserId())); } + @Test + public void testRemoveTiles() { + List<String> tiles = List.of("spec1", "spec2", "spec3"); + mQSTileHost.saveTilesToSettings(tiles); + + mQSTileHost.removeTiles(List.of("spec1", "spec2")); + + assertEquals(List.of("spec3"), mQSTileHost.mTileSpecs); + } + private class TestQSTileHost extends QSTileHost { TestQSTileHost(Context context, StatusBarIconController iconController, QSFactory defaultFactory, Handler mainHandler, Looper bgLooper, @@ -390,14 +397,11 @@ public class QSTileHostTest extends SysuiTestCase { @Override void saveTilesToSettings(List<String> tileSpecs) { super.saveTilesToSettings(tileSpecs); - - ArgumentCaptor<String> specs = ArgumentCaptor.forClass(String.class); - verify(mSecureSettings, atLeastOnce()).putStringForUser(eq(QSTileHost.TILES_SETTING), - specs.capture(), isNull(), eq(false), anyInt(), eq(true)); - // After tiles are changed, make sure to call onTuningChanged with the new setting if it // changed - onTuningChanged(TILES_SETTING, specs.getValue()); + String specs = mSecureSettings.getStringForUser( + QSTileHost.TILES_SETTING, mUserTracker.getUserId()); + onTuningChanged(TILES_SETTING, specs); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt index b3e00f826db9..92b9f75936b9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt @@ -37,6 +37,8 @@ import com.android.systemui.qs.carrier.QSCarrierGroupController import com.android.systemui.statusbar.phone.StatusBarIconController import com.android.systemui.statusbar.phone.StatusIconContainer import com.android.systemui.statusbar.policy.Clock +import com.android.systemui.statusbar.policy.VariableDateView +import com.android.systemui.statusbar.policy.VariableDateViewController import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.argumentCaptor import com.android.systemui.util.mockito.capture @@ -88,10 +90,16 @@ class QuickStatusBarHeaderControllerTest : SysuiTestCase() { @Mock private lateinit var privacyDialogController: PrivacyDialogController @Mock + private lateinit var variableDateViewControllerFactory: VariableDateViewController.Factory + @Mock + private lateinit var variableDateViewController: VariableDateViewController + @Mock private lateinit var batteryMeterViewController: BatteryMeterViewController @Mock private lateinit var clock: Clock @Mock + private lateinit var variableDateView: VariableDateView + @Mock private lateinit var mockView: View @Mock(answer = Answers.RETURNS_DEEP_STUBS) private lateinit var context: Context @@ -112,6 +120,8 @@ class QuickStatusBarHeaderControllerTest : SysuiTestCase() { stubViews() `when`(iconContainer.context).thenReturn(context) `when`(qsCarrierGroupControllerBuilder.build()).thenReturn(qsCarrierGroupController) + `when`(variableDateViewControllerFactory.create(any())) + .thenReturn(variableDateViewController) `when`(view.resources).thenReturn(mContext.resources) `when`(view.isAttachedToWindow).thenReturn(true) `when`(view.context).thenReturn(context) @@ -137,7 +147,8 @@ class QuickStatusBarHeaderControllerTest : SysuiTestCase() { privacyDialogController, qsExpansionPathInterpolator, batteryMeterViewController, - featureFlags + featureFlags, + variableDateViewControllerFactory ) } @@ -278,6 +289,8 @@ class QuickStatusBarHeaderControllerTest : SysuiTestCase() { `when`(view.findViewById<StatusIconContainer>(R.id.statusIcons)).thenReturn(iconContainer) `when`(view.findViewById<OngoingPrivacyChip>(R.id.privacy_chip)).thenReturn(privacyChip) `when`(view.findViewById<Clock>(R.id.clock)).thenReturn(clock) + `when`(view.requireViewById<VariableDateView>(R.id.date)).thenReturn(variableDateView) + `when`(view.requireViewById<VariableDateView>(R.id.date_clock)).thenReturn(variableDateView) } private fun setPrivacyController(micCamera: Boolean, location: Boolean) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java index d44a52607707..e939411e4a2a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java @@ -17,6 +17,7 @@ package com.android.systemui.qs.tiles; import static junit.framework.Assert.assertTrue; import static junit.framework.TestCase.assertEquals; +import static org.junit.Assert.assertFalse; import static org.mockito.ArgumentMatchers.same; import static org.mockito.Mockito.any; import static org.mockito.Mockito.mock; @@ -327,4 +328,77 @@ public class CastTileTest extends SysuiTestCase { assertEquals(Tile.STATE_ACTIVE, mCastTile.getState().state); assertTrue(mCastTile.getState().secondaryLabel.toString().startsWith(connected.name)); } + + @Test + public void testExpandView_wifiNotConnected() { + mCastTile.refreshState(); + mTestableLooper.processAllMessages(); + + assertFalse(mCastTile.getState().forceExpandIcon); + } + + @Test + public void testExpandView_wifiEnabledNotCasting() { + enableWifiAndProcessMessages(); + + assertTrue(mCastTile.getState().forceExpandIcon); + } + + @Test + public void testExpandView_casting_projection() { + CastController.CastDevice device = new CastController.CastDevice(); + device.state = CastController.CastDevice.STATE_CONNECTED; + List<CastDevice> devices = new ArrayList<>(); + devices.add(device); + when(mController.getCastDevices()).thenReturn(devices); + + enableWifiAndProcessMessages(); + + assertFalse(mCastTile.getState().forceExpandIcon); + } + + @Test + public void testExpandView_connecting_projection() { + CastController.CastDevice connecting = new CastController.CastDevice(); + connecting.state = CastDevice.STATE_CONNECTING; + connecting.name = "Test Casting Device"; + + List<CastDevice> devices = new ArrayList<>(); + devices.add(connecting); + when(mController.getCastDevices()).thenReturn(devices); + + enableWifiAndProcessMessages(); + + assertFalse(mCastTile.getState().forceExpandIcon); + } + + @Test + public void testExpandView_casting_mediaRoute() { + CastController.CastDevice device = new CastController.CastDevice(); + device.state = CastDevice.STATE_CONNECTED; + device.tag = mock(MediaRouter.RouteInfo.class); + List<CastDevice> devices = new ArrayList<>(); + devices.add(device); + when(mController.getCastDevices()).thenReturn(devices); + + enableWifiAndProcessMessages(); + + assertTrue(mCastTile.getState().forceExpandIcon); + } + + @Test + public void testExpandView_connecting_mediaRoute() { + CastController.CastDevice connecting = new CastController.CastDevice(); + connecting.state = CastDevice.STATE_CONNECTING; + connecting.tag = mock(RouteInfo.class); + connecting.name = "Test Casting Device"; + + List<CastDevice> devices = new ArrayList<>(); + devices.add(connecting); + when(mController.getCastDevices()).thenReturn(devices); + + enableWifiAndProcessMessages(); + + assertTrue(mCastTile.getState().forceExpandIcon); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java index a70c2be4954e..8922b43b7447 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java @@ -228,7 +228,9 @@ public class QuickAccessWalletTileTest extends SysuiTestCase { mTile.handleClick(null /* view */); mTestableLooper.processAllMessages(); - verify(mSpiedContext).startActivity(mIntentCaptor.capture()); + verify(mActivityStarter).startActivity(mIntentCaptor.capture(), eq(true) /* dismissShade */, + (ActivityLaunchAnimator.Controller) eq(null), + eq(true) /* showOverLockscreenWhenLocked */); Intent nextStartedIntent = mIntentCaptor.getValue(); String walletClassName = "com.android.systemui.wallet.ui.WalletActivity"; @@ -246,7 +248,8 @@ public class QuickAccessWalletTileTest extends SysuiTestCase { mTestableLooper.processAllMessages(); verify(mActivityStarter).startActivity(mIntentCaptor.capture(), eq(true) /* dismissShade */, - (ActivityLaunchAnimator.Controller) eq(null)); + (ActivityLaunchAnimator.Controller) eq(null), + eq(true) /* showOverLockscreenWhenLocked */); Intent nextStartedIntent = mIntentCaptor.getValue(); String walletClassName = "com.android.systemui.wallet.ui.WalletActivity"; diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java new file mode 100644 index 000000000000..83e4d00b3a8f --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java @@ -0,0 +1,269 @@ +package com.android.systemui.qs.tiles.dialog; + +import static com.google.common.truth.Truth.assertThat; + +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.net.ConnectivityManager; +import android.net.wifi.ScanResult; +import android.net.wifi.WifiInfo; +import android.net.wifi.WifiManager; +import android.os.Handler; +import android.telephony.ServiceState; +import android.telephony.SubscriptionManager; +import android.telephony.TelephonyManager; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; +import android.text.TextUtils; + +import com.android.internal.logging.UiEventLogger; +import com.android.keyguard.KeyguardUpdateMonitor; +import com.android.systemui.SysuiTestCase; +import com.android.systemui.broadcast.BroadcastDispatcher; +import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.plugins.ActivityStarter; +import com.android.systemui.statusbar.policy.NetworkController; +import com.android.systemui.statusbar.policy.NetworkController.AccessPointController; +import com.android.systemui.util.concurrency.FakeExecutor; +import com.android.systemui.util.settings.GlobalSettings; +import com.android.systemui.util.time.FakeSystemClock; +import com.android.wifitrackerlib.WifiEntry; + +import androidx.annotation.Nullable; +import androidx.test.filters.SmallTest; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.Executor; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper(setAsMainLooper = true) +public class InternetDialogControllerTest extends SysuiTestCase { + + private static final int SUB_ID = 1; + private static final String CONNECTED_TITLE = "Connected Wi-Fi Title"; + private static final String CONNECTED_SUMMARY = "Connected Wi-Fi Summary"; + + private final UiEventLogger mUiEventLogger = mock(UiEventLogger.class); + private MockInternetDialogController mInternetDialogController; + private InternetDialogController.InternetDialogCallback mCallback = + mock(InternetDialogController.InternetDialogCallback.class); + private ActivityStarter mStarter = mock(ActivityStarter.class); + private WifiManager mWifiManager = mock(WifiManager.class); + private ConnectivityManager mConnectivityManager = mock(ConnectivityManager.class); + private TelephonyManager mTelephonyManager = mock(TelephonyManager.class); + private SubscriptionManager mSubscriptionManager = mock(SubscriptionManager.class); + private FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock()); + @Mock + private Handler mHandler; + @Mock + private GlobalSettings mGlobalSettings; + @Mock + private KeyguardUpdateMonitor mKeyguardUpdateMonitor; + @Mock + private NetworkController.AccessPointController mAccessPointController; + @Mock + private WifiEntry mWifiEntryConnected = mock(WifiEntry.class); + @Mock + private WifiInfo mWifiInfo; + @Mock + private ServiceState mServiceState; + @Mock + private BroadcastDispatcher mBroadcastDispatcher; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + doReturn(mTelephonyManager).when(mTelephonyManager).createForSubscriptionId(SUB_ID); + when(mWifiManager.getConnectionInfo()).thenReturn(mWifiInfo); + mInternetDialogController = new MockInternetDialogController(mContext, mUiEventLogger, + mStarter, mAccessPointController, mSubscriptionManager, mTelephonyManager, + mWifiManager, mConnectivityManager, mHandler, mExecutor, mBroadcastDispatcher, + mKeyguardUpdateMonitor, mGlobalSettings); + mSubscriptionManager.addOnSubscriptionsChangedListener(mExecutor, + mInternetDialogController.mOnSubscriptionsChangedListener); + mInternetDialogController.onStart(mCallback); + } + + @Test + public void getDialogTitleText_withAirplaneModeOn_returnAirplaneMode() { + mInternetDialogController.setAirplaneModeEnabled(true); + + assertTrue(TextUtils.equals(mInternetDialogController.getDialogTitleText(), + getResourcesString("airplane_mode"))); + } + + @Test + public void getDialogTitleText_withAirplaneModeOff_returnInternet() { + mInternetDialogController.setAirplaneModeEnabled(false); + + assertTrue(TextUtils.equals(mInternetDialogController.getDialogTitleText(), + getResourcesString("quick_settings_internet_label"))); + } + + @Test + public void getSubtitleText_withAirplaneModeOn_returnNull() { + mInternetDialogController.setAirplaneModeEnabled(true); + + assertThat(mInternetDialogController.getSubtitleText(false)).isNull(); + } + + @Test + public void getSubtitleText_withWifiOff_returnWifiIsOff() { + mInternetDialogController.setAirplaneModeEnabled(false); + when(mWifiManager.isWifiEnabled()).thenReturn(false); + + assertTrue(TextUtils.equals(mInternetDialogController.getSubtitleText(false), + getResourcesString("wifi_is_off"))); + } + + @Test + public void getSubtitleText_withWifiOn_returnSearchWifi() { + mInternetDialogController.setAirplaneModeEnabled(false); + when(mWifiManager.isWifiEnabled()).thenReturn(true); + + assertTrue(TextUtils.equals(mInternetDialogController.getSubtitleText(true), + getResourcesString("wifi_empty_list_wifi_on"))); + } + + @Test + public void getSubtitleText_withWifiEntry_returnTapToConnect() { + mInternetDialogController.setAirplaneModeEnabled(false); + when(mWifiManager.isWifiEnabled()).thenReturn(true); + List<ScanResult> wifiScanResults = mock(ArrayList.class); + doReturn(1).when(wifiScanResults).size(); + when(mWifiManager.getScanResults()).thenReturn(wifiScanResults); + + assertTrue(TextUtils.equals(mInternetDialogController.getSubtitleText(false), + getResourcesString("tap_a_network_to_connect"))); + } + + @Test + public void getSubtitleText_withNoService_returnNoNetworksAvailable() { + mInternetDialogController.setAirplaneModeEnabled(false); + when(mWifiManager.isWifiEnabled()).thenReturn(true); + List<ScanResult> wifiScanResults = new ArrayList<>(); + doReturn(wifiScanResults).when(mWifiManager).getScanResults(); + when(mWifiManager.getScanResults()).thenReturn(wifiScanResults); + when(mSubscriptionManager.getActiveSubscriptionIdList()) + .thenReturn(new int[] {SUB_ID}); + + doReturn(ServiceState.STATE_OUT_OF_SERVICE).when(mServiceState).getState(); + doReturn(mServiceState).when(mTelephonyManager).getServiceState(); + doReturn(TelephonyManager.DATA_DISCONNECTED).when(mTelephonyManager).getDataState(); + + assertTrue(TextUtils.equals(mInternetDialogController.getSubtitleText(false), + getResourcesString("all_network_unavailable"))); + } + + @Test + public void getSubtitleText_withMobileDataDisabled_returnNoOtherAvailable() { + mInternetDialogController.setAirplaneModeEnabled(false); + when(mWifiManager.isWifiEnabled()).thenReturn(true); + List<ScanResult> wifiScanResults = new ArrayList<>(); + doReturn(wifiScanResults).when(mWifiManager).getScanResults(); + when(mWifiManager.getScanResults()).thenReturn(wifiScanResults); + when(mSubscriptionManager.getActiveSubscriptionIdList()) + .thenReturn(new int[] {SUB_ID}); + + doReturn(ServiceState.STATE_IN_SERVICE).when(mServiceState).getState(); + doReturn(mServiceState).when(mTelephonyManager).getServiceState(); + + when(mTelephonyManager.isDataEnabled()).thenReturn(false); + + assertTrue(TextUtils.equals(mInternetDialogController.getSubtitleText(false), + getResourcesString("non_carrier_network_unavailable"))); + } + + @Test + public void getConnectedWifiTitle_withNoConnectedEntry_returnNull() { + mInternetDialogController.setConnectedWifiEntry(null); + + assertTrue(TextUtils.equals(mInternetDialogController.getConnectedWifiTitle(), + "")); + } + + @Test + public void getConnectedWifiTitle_withConnectedEntry_returnTitle() { + mInternetDialogController.setConnectedWifiEntry(mWifiEntryConnected); + when(mWifiEntryConnected.getTitle()).thenReturn(CONNECTED_TITLE); + + assertTrue(TextUtils.equals(mInternetDialogController.getConnectedWifiTitle(), + CONNECTED_TITLE)); + } + + @Test + public void getConnectedWifiSummary_withNoConnectedEntry_returnNull() { + mInternetDialogController.setConnectedWifiEntry(null); + + assertTrue(TextUtils.equals(mInternetDialogController.getConnectedWifiSummary(), + "")); + } + + @Test + public void getConnectedWifiSummary_withConnectedEntry_returnSummary() { + mInternetDialogController.setConnectedWifiEntry(mWifiEntryConnected); + when(mWifiEntryConnected.getSummary(false)).thenReturn(CONNECTED_SUMMARY); + + assertTrue(TextUtils.equals(mInternetDialogController.getConnectedWifiSummary(), + CONNECTED_SUMMARY)); + } + + private String getResourcesString(String name) { + return mContext.getResources().getString(getResourcesId(name)); + } + + private int getResourcesId(String name) { + return mContext.getResources().getIdentifier(name, "string", + mContext.getPackageName()); + } + + private class MockInternetDialogController extends InternetDialogController { + + private WifiEntry mConnectedEntry; + private GlobalSettings mGlobalSettings; + private boolean mIsAirplaneModeOn; + + MockInternetDialogController(Context context, UiEventLogger uiEventLogger, + ActivityStarter starter, AccessPointController accessPointController, + SubscriptionManager subscriptionManager, TelephonyManager telephonyManager, + @Nullable WifiManager wifiManager, ConnectivityManager connectivityManager, + @Main Handler handler, @Main Executor mainExecutor, + BroadcastDispatcher broadcastDispatcher, + KeyguardUpdateMonitor keyguardUpdateMonitor, GlobalSettings globalSettings) { + super(context, uiEventLogger, starter, accessPointController, subscriptionManager, + telephonyManager, wifiManager, connectivityManager, handler, mainExecutor, + broadcastDispatcher, keyguardUpdateMonitor, globalSettings); + mGlobalSettings = globalSettings; + } + + @Override + boolean isAirplaneModeEnabled() { + return mIsAirplaneModeOn; + } + + public void setAirplaneModeEnabled(boolean enabled) { + mIsAirplaneModeOn = enabled; + } + + @Override + WifiEntry getConnectedWifiEntry() { + return mConnectedEntry; + } + + public void setConnectedWifiEntry(WifiEntry connectedEntry) { + mConnectedEntry = connectedEntry; + } + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureControllerTest.java index 10c878a92745..6f081c759df7 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureControllerTest.java @@ -34,6 +34,7 @@ import android.view.ScrollCaptureResponse; import androidx.test.filters.SmallTest; +import com.android.internal.logging.testing.UiEventLoggerFake; import com.android.systemui.SysuiTestCase; import com.android.systemui.screenshot.ScrollCaptureClient.Session; @@ -274,7 +275,8 @@ public class ScrollCaptureControllerTest extends SysuiTestCase { when(client.start(/* response */ any(), /* maxPages */ anyFloat())) .thenReturn(immediateFuture(session)); return new ScrollCaptureController(context, context.getMainExecutor(), - client, new ImageTileSet(context.getMainThreadHandler())); + client, new ImageTileSet(context.getMainThreadHandler()), + new UiEventLoggerFake()); } } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceManagerTest.java index 325d540ad741..790b4dd11825 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceManagerTest.java @@ -15,7 +15,6 @@ package com.android.systemui.shared.plugins; import static junit.framework.Assert.assertFalse; -import static junit.framework.Assert.assertNotNull; import static junit.framework.Assert.assertTrue; import static org.mockito.Matchers.any; @@ -32,33 +31,32 @@ import android.app.NotificationManager; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; -import android.content.ContextWrapper; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; -import android.os.HandlerThread; import android.test.suitebuilder.annotation.SmallTest; -import androidx.test.annotation.UiThreadTest; import androidx.test.runner.AndroidJUnit4; import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; import com.android.systemui.SysuiTestCase; +import com.android.systemui.SysuiTestableContext; import com.android.systemui.plugins.Plugin; import com.android.systemui.plugins.PluginListener; import com.android.systemui.plugins.annotations.Requires; -import com.android.systemui.shared.plugins.PluginInstanceManager.PluginInfo; import com.android.systemui.shared.plugins.VersionInfo.InvalidVersionException; +import com.android.systemui.util.concurrency.FakeExecutor; +import com.android.systemui.util.time.FakeSystemClock; -import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Mockito; +import org.mockito.stubbing.Answer; import java.util.ArrayList; import java.util.Collections; @@ -68,63 +66,55 @@ import java.util.List; @RunWith(AndroidJUnit4.class) public class PluginInstanceManagerTest extends SysuiTestCase { - private static final String WHITELISTED_PACKAGE = "com.android.systemui"; - // Static since the plugin needs to be generated by the PluginInstanceManager using newInstance. - private static Plugin sMockPlugin; + private static final String PRIVILEGED_PACKAGE = "com.android.systemui.shared.plugins"; + private TestPlugin mMockPlugin; - private HandlerThread mHandlerThread; - private Context mContextWrapper; private PackageManager mMockPm; - private PluginListener mMockListener; - private PluginInstanceManager mPluginInstanceManager; - private PluginManagerImpl mMockManager; + private PluginListener<Plugin> mMockListener; + private PluginInstanceManager<Plugin> mPluginInstanceManager; private VersionInfo mMockVersionInfo; private PluginEnabler mMockEnabler; ComponentName mTestPluginComponentName = - new ComponentName(WHITELISTED_PACKAGE, TestPlugin.class.getName()); + new ComponentName(PRIVILEGED_PACKAGE, TestPlugin.class.getName()); + private PluginInitializer mInitializer; + private final FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock()); + NotificationManager mNotificationManager; + private PluginInstanceManager.Factory mInstanceManagerFactory; + private final PluginInstanceManager.InstanceFactory<Plugin> mPluginInstanceFactory = + new PluginInstanceManager.InstanceFactory<Plugin>() { + @Override + Plugin create(Class cls) { + return mMockPlugin; + } + }; @Before public void setup() throws Exception { - mHandlerThread = new HandlerThread("test_thread"); - mHandlerThread.start(); - mContextWrapper = new MyContextWrapper(getContext()); + mContext = new MyContextWrapper(mContext); mMockPm = mock(PackageManager.class); mMockListener = mock(PluginListener.class); - mMockManager = mock(PluginManagerImpl.class); - when(mMockManager.getClassLoader(any())).thenReturn(getClass().getClassLoader()); mMockEnabler = mock(PluginEnabler.class); - when(mMockManager.getPluginEnabler()).thenReturn(mMockEnabler); mMockVersionInfo = mock(VersionInfo.class); - mPluginInstanceManager = new PluginInstanceManager(mContextWrapper, mMockPm, "myAction", - mMockListener, true, mHandlerThread.getLooper(), mMockVersionInfo, - mMockManager, true, new String[0]); - sMockPlugin = mock(Plugin.class); - when(sMockPlugin.getVersion()).thenReturn(1); - } - - @After - public void tearDown() throws Exception { - mHandlerThread.quit(); - sMockPlugin = null; - } - - @UiThreadTest - @Test - public void testGetPlugin() throws Exception { - setupFakePmQuery(); - PluginInfo p = mPluginInstanceManager.getPlugin(); - assertNotNull(p.mPlugin); - verify(sMockPlugin).onCreate(any(), any()); + mInitializer = mock(PluginInitializer.class); + mNotificationManager = mock(NotificationManager.class); + mMockPlugin = mock(TestPlugin.class); + mInstanceManagerFactory = new PluginInstanceManager.Factory(getContext(), mMockPm, + mFakeExecutor, mFakeExecutor, mInitializer, mNotificationManager, mMockEnabler, + new ArrayList<>()) + .setInstanceFactory(mPluginInstanceFactory); + + mPluginInstanceManager = mInstanceManagerFactory.create("myAction", mMockListener, + true, mMockVersionInfo, true); + when(mMockPlugin.getVersion()).thenReturn(1); } @Test - public void testNoPlugins() throws Exception { + public void testNoPlugins() { when(mMockPm.queryIntentServices(any(), anyInt())).thenReturn( Collections.emptyList()); mPluginInstanceManager.loadAll(); - waitForIdleSync(mPluginInstanceManager.mPluginHandler); - waitForIdleSync(mPluginInstanceManager.mMainHandler); + mFakeExecutor.runAllReady(); verify(mMockListener, never()).onPluginConnected(any(), any()); } @@ -134,7 +124,7 @@ public class PluginInstanceManagerTest extends SysuiTestCase { createPlugin(); // Verify startup lifecycle - verify(sMockPlugin).onCreate(ArgumentCaptor.forClass(Context.class).capture(), + verify(mMockPlugin).onCreate(ArgumentCaptor.forClass(Context.class).capture(), ArgumentCaptor.forClass(Context.class).capture()); verify(mMockListener).onPluginConnected(any(), any()); } @@ -145,45 +135,41 @@ public class PluginInstanceManagerTest extends SysuiTestCase { mPluginInstanceManager.destroy(); - waitForIdleSync(mPluginInstanceManager.mPluginHandler); - waitForIdleSync(mPluginInstanceManager.mMainHandler); + mFakeExecutor.runAllReady(); + // Verify shutdown lifecycle verify(mMockListener).onPluginDisconnected(ArgumentCaptor.forClass(Plugin.class).capture()); - verify(sMockPlugin).onDestroy(); + verify(mMockPlugin).onDestroy(); } @Test public void testIncorrectVersion() throws Exception { - NotificationManager nm = mock(NotificationManager.class); - mContext.addMockSystemService(Context.NOTIFICATION_SERVICE, nm); setupFakePmQuery(); doThrow(new InvalidVersionException("", false)).when(mMockVersionInfo).checkVersion(any()); mPluginInstanceManager.loadAll(); - waitForIdleSync(mPluginInstanceManager.mPluginHandler); - waitForIdleSync(mPluginInstanceManager.mMainHandler); + mFakeExecutor.runAllReady(); // Plugin shouldn't be connected because it is the wrong version. verify(mMockListener, never()).onPluginConnected(any(), any()); - verify(nm).notify(eq(SystemMessage.NOTE_PLUGIN), any()); + verify(mNotificationManager).notify(eq(SystemMessage.NOTE_PLUGIN), any()); } @Test public void testReloadOnChange() throws Exception { createPlugin(); // Get into valid created state. - mPluginInstanceManager.onPackageChange("com.android.systemui"); + mPluginInstanceManager.onPackageChange(PRIVILEGED_PACKAGE); - waitForIdleSync(mPluginInstanceManager.mPluginHandler); - waitForIdleSync(mPluginInstanceManager.mMainHandler); + mFakeExecutor.runAllReady(); // Verify the old one was destroyed. verify(mMockListener).onPluginDisconnected(ArgumentCaptor.forClass(Plugin.class).capture()); - verify(sMockPlugin).onDestroy(); + verify(mMockPlugin).onDestroy(); // Also verify we got a second onCreate. - verify(sMockPlugin, Mockito.times(2)).onCreate( + verify(mMockPlugin, Mockito.times(2)).onCreate( ArgumentCaptor.forClass(Context.class).capture(), ArgumentCaptor.forClass(Context.class).capture()); verify(mMockListener, Mockito.times(2)).onPluginConnected(any(), any()); @@ -192,35 +178,35 @@ public class PluginInstanceManagerTest extends SysuiTestCase { @Test public void testNonDebuggable() throws Exception { // Create a version that thinks the build is not debuggable. - mPluginInstanceManager = new PluginInstanceManager(mContextWrapper, mMockPm, "myAction", - mMockListener, true, mHandlerThread.getLooper(), mMockVersionInfo, - mMockManager, false, new String[0]); + mPluginInstanceManager = mInstanceManagerFactory.create("myAction", mMockListener, + true, mMockVersionInfo, false); setupFakePmQuery(); mPluginInstanceManager.loadAll(); - waitForIdleSync(mPluginInstanceManager.mPluginHandler); - waitForIdleSync(mPluginInstanceManager.mMainHandler);; + mFakeExecutor.runAllReady(); // Non-debuggable build should receive no plugins. verify(mMockListener, never()).onPluginConnected(any(), any()); } @Test - public void testNonDebuggable_whitelist() throws Exception { + public void testNonDebuggable_privileged() throws Exception { // Create a version that thinks the build is not debuggable. - mPluginInstanceManager = new PluginInstanceManager(mContextWrapper, mMockPm, "myAction", - mMockListener, true, mHandlerThread.getLooper(), mMockVersionInfo, - mMockManager, false, new String[] {WHITELISTED_PACKAGE}); + PluginInstanceManager.Factory factory = new PluginInstanceManager.Factory(getContext(), + mMockPm, mFakeExecutor, mFakeExecutor, mInitializer, mNotificationManager, + mMockEnabler, Collections.singletonList(PRIVILEGED_PACKAGE)); + factory.setInstanceFactory(mPluginInstanceFactory); + mPluginInstanceManager = factory.create("myAction", mMockListener, + true, mMockVersionInfo, false); setupFakePmQuery(); mPluginInstanceManager.loadAll(); - waitForIdleSync(mPluginInstanceManager.mPluginHandler); - waitForIdleSync(mPluginInstanceManager.mMainHandler); + mFakeExecutor.runAllReady(); // Verify startup lifecycle - verify(sMockPlugin).onCreate(ArgumentCaptor.forClass(Context.class).capture(), + verify(mMockPlugin).onCreate(ArgumentCaptor.forClass(Context.class).capture(), ArgumentCaptor.forClass(Context.class).capture()); verify(mMockListener).onPluginConnected(any(), any()); } @@ -253,9 +239,13 @@ public class PluginInstanceManagerTest extends SysuiTestCase { @Test public void testDisableWhitelisted() throws Exception { - mPluginInstanceManager = new PluginInstanceManager(mContextWrapper, mMockPm, "myAction", - mMockListener, true, mHandlerThread.getLooper(), mMockVersionInfo, - mMockManager, false, new String[] {WHITELISTED_PACKAGE}); + PluginInstanceManager.Factory factory = new PluginInstanceManager.Factory(getContext(), + mMockPm, mFakeExecutor, mFakeExecutor, mInitializer, mNotificationManager, + mMockEnabler, Collections.singletonList(PRIVILEGED_PACKAGE)); + factory.setInstanceFactory(mPluginInstanceFactory); + mPluginInstanceManager = factory.create("myAction", mMockListener, + true, mMockVersionInfo, false); + createPlugin(); // Get into valid created state. mPluginInstanceManager.disableAll(); @@ -280,9 +270,12 @@ public class PluginInstanceManagerTest extends SysuiTestCase { when(mMockPm.checkPermission(Mockito.anyString(), Mockito.anyString())).thenReturn( PackageManager.PERMISSION_GRANTED); - ApplicationInfo appInfo = getContext().getApplicationInfo(); - when(mMockPm.getApplicationInfo(Mockito.anyString(), Mockito.anyInt())).thenReturn( - appInfo); + when(mMockPm.getApplicationInfo(Mockito.anyString(), anyInt())).thenAnswer( + (Answer<ApplicationInfo>) invocation -> { + ApplicationInfo appInfo = getContext().getApplicationInfo(); + appInfo.packageName = invocation.getArgument(0); + return appInfo; + }); when(mMockEnabler.isEnabled(mTestPluginComponentName)).thenReturn(true); } @@ -291,12 +284,11 @@ public class PluginInstanceManagerTest extends SysuiTestCase { mPluginInstanceManager.loadAll(); - waitForIdleSync(mPluginInstanceManager.mPluginHandler); - waitForIdleSync(mPluginInstanceManager.mMainHandler); + mFakeExecutor.runAllReady(); } // Real context with no registering/unregistering of receivers. - private static class MyContextWrapper extends ContextWrapper { + private static class MyContextWrapper extends SysuiTestableContext { public MyContextWrapper(Context base) { super(base); } @@ -322,17 +314,15 @@ public class PluginInstanceManagerTest extends SysuiTestCase { public static class TestPlugin implements Plugin { @Override public int getVersion() { - return sMockPlugin.getVersion(); + return 1; } @Override public void onCreate(Context sysuiContext, Context pluginContext) { - sMockPlugin.onCreate(sysuiContext, pluginContext); } @Override public void onDestroy() { - sMockPlugin.onDestroy(); } } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginManagerTest.java index 536c043bd7ae..4590dd829550 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginManagerTest.java @@ -13,10 +13,8 @@ */ package com.android.systemui.shared.plugins; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertSame; -import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -30,19 +28,13 @@ import android.content.pm.PackageManager; import android.net.Uri; import android.test.suitebuilder.annotation.SmallTest; import android.testing.AndroidTestingRunner; -import android.testing.TestableLooper; import android.testing.TestableLooper.RunWithLooper; import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; -import com.android.systemui.Dependency; import com.android.systemui.SysuiTestCase; import com.android.systemui.plugins.Plugin; -import com.android.systemui.plugins.PluginEnablerImpl; -import com.android.systemui.plugins.PluginInitializerImpl; import com.android.systemui.plugins.PluginListener; import com.android.systemui.plugins.annotations.ProvidesInterface; -import com.android.systemui.shared.plugins.PluginInstanceManager.PluginInfo; -import com.android.systemui.shared.plugins.PluginManagerImpl.PluginInstanceManagerFactory; import org.junit.Before; import org.junit.Test; @@ -51,19 +43,24 @@ import org.mockito.ArgumentCaptor; import org.mockito.Mockito; import java.lang.Thread.UncaughtExceptionHandler; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Optional; @SmallTest @RunWith(AndroidTestingRunner.class) @RunWithLooper public class PluginManagerTest extends SysuiTestCase { - private static final String WHITELISTED_PACKAGE = "com.android.systemui"; + private static final String PRIVILEGED_PACKAGE = "com.android.systemui"; - private PluginInstanceManagerFactory mMockFactory; - private PluginInstanceManager mMockPluginInstance; + private PluginInstanceManager.Factory mMockFactory; + private PluginInstanceManager<Plugin> mMockPluginInstance; private PluginManagerImpl mPluginManager; - private PluginListener mMockListener; + private PluginListener<?> mMockListener; private PackageManager mMockPackageManager; + private PluginEnabler mPluginEnabler; + private PluginPrefs mPluginPrefs; private UncaughtExceptionHandler mRealExceptionHandler; private UncaughtExceptionHandler mMockExceptionHandler; @@ -71,44 +68,25 @@ public class PluginManagerTest extends SysuiTestCase { @Before public void setup() throws Exception { - mDependency.injectTestDependency(Dependency.BG_LOOPER, - TestableLooper.get(this).getLooper()); mRealExceptionHandler = Thread.getUncaughtExceptionPreHandler(); mMockExceptionHandler = mock(UncaughtExceptionHandler.class); - mMockFactory = mock(PluginInstanceManagerFactory.class); + mMockFactory = mock(PluginInstanceManager.Factory.class); mMockPluginInstance = mock(PluginInstanceManager.class); - when(mMockFactory.createPluginInstanceManager(Mockito.any(), Mockito.any(), Mockito.any(), - Mockito.anyBoolean(), Mockito.any(), Mockito.any(), Mockito.any())) + mPluginEnabler = mock(PluginEnabler.class); + mPluginPrefs = mock(PluginPrefs.class); + when(mMockFactory.create(any(), any(), Mockito.anyBoolean(), any(), Mockito.anyBoolean())) .thenReturn(mMockPluginInstance); mMockPackageManager = mock(PackageManager.class); mPluginManager = new PluginManagerImpl( getContext(), mMockFactory, true, - mMockExceptionHandler, new PluginInitializerImpl() { - @Override - public String[] getWhitelistedPlugins(Context context) { - return new String[0]; - } - - @Override - public PluginEnabler getPluginEnabler(Context context) { - return new PluginEnablerImpl(context, mMockPackageManager); - } - }); + Optional.of(mMockExceptionHandler), mPluginEnabler, + mPluginPrefs, new ArrayList<>()); + resetExceptionHandler(); mMockListener = mock(PluginListener.class); } - @RunWithLooper(setAsMainLooper = true) - @Test - public void testOneShot() { - Plugin mockPlugin = mock(Plugin.class); - when(mMockPluginInstance.getPlugin()).thenReturn(new PluginInfo(null, null, mockPlugin, - null, null)); - Plugin result = mPluginManager.getOneShotPlugin("myAction", TestPlugin.class); - assertSame(mockPlugin, result); - } - @Test public void testAddListener() { mPluginManager.addPluginListener("myAction", mMockListener, TestPlugin.class); @@ -126,53 +104,49 @@ public class PluginManagerTest extends SysuiTestCase { @Test @RunWithLooper(setAsMainLooper = true) - public void testNonDebuggable_noWhitelist() { - mPluginManager = new PluginManagerImpl(getContext(), mMockFactory, false, - mMockExceptionHandler, new PluginInitializerImpl() { - @Override - public String[] getWhitelistedPlugins(Context context) { - return new String[0]; - } - }); + public void testNonDebuggable_nonPrivileged() { + mPluginManager = new PluginManagerImpl( + getContext(), mMockFactory, false, + Optional.of(mMockExceptionHandler), mPluginEnabler, + mPluginPrefs, new ArrayList<>()); resetExceptionHandler(); String sourceDir = "myPlugin"; ApplicationInfo applicationInfo = new ApplicationInfo(); applicationInfo.sourceDir = sourceDir; - applicationInfo.packageName = WHITELISTED_PACKAGE; + applicationInfo.packageName = PRIVILEGED_PACKAGE; mPluginManager.addPluginListener("myAction", mMockListener, TestPlugin.class); - assertNull(mPluginManager.getOneShotPlugin(sourceDir, TestPlugin.class)); - assertNull(mPluginManager.getClassLoader(applicationInfo)); + verify(mMockFactory).create(eq("myAction"), eq(mMockListener), eq(false), + any(VersionInfo.class), eq(false)); + verify(mMockPluginInstance).loadAll(); } @Test @RunWithLooper(setAsMainLooper = true) - public void testNonDebuggable_whitelistedPkg() { - mPluginManager = new PluginManagerImpl(getContext(), mMockFactory, false, - mMockExceptionHandler, new PluginInitializerImpl() { - @Override - public String[] getWhitelistedPlugins(Context context) { - return new String[] {WHITELISTED_PACKAGE}; - } - }); + public void testNonDebuggable_privilegedPackage() { + mPluginManager = new PluginManagerImpl( + getContext(), mMockFactory, false, + Optional.of(mMockExceptionHandler), mPluginEnabler, + mPluginPrefs, Collections.singletonList(PRIVILEGED_PACKAGE)); resetExceptionHandler(); String sourceDir = "myPlugin"; - ApplicationInfo whiteListedApplicationInfo = new ApplicationInfo(); - whiteListedApplicationInfo.sourceDir = sourceDir; - whiteListedApplicationInfo.packageName = WHITELISTED_PACKAGE; + ApplicationInfo privilegedApplicationInfo = new ApplicationInfo(); + privilegedApplicationInfo.sourceDir = sourceDir; + privilegedApplicationInfo.packageName = PRIVILEGED_PACKAGE; ApplicationInfo invalidApplicationInfo = new ApplicationInfo(); invalidApplicationInfo.sourceDir = sourceDir; invalidApplicationInfo.packageName = "com.android.invalidpackage"; mPluginManager.addPluginListener("myAction", mMockListener, TestPlugin.class); - assertNotNull(mPluginManager.getClassLoader(whiteListedApplicationInfo)); - assertNull(mPluginManager.getClassLoader(invalidApplicationInfo)); + verify(mMockFactory).create(eq("myAction"), eq(mMockListener), eq(false), + any(VersionInfo.class), eq(false)); + verify(mMockPluginInstance).loadAll(); } @Test public void testExceptionHandler_foundPlugin() { mPluginManager.addPluginListener("myAction", mMockListener, TestPlugin.class); - when(mMockPluginInstance.checkAndDisable(Mockito.any())).thenReturn(true); + when(mMockPluginInstance.checkAndDisable(any())).thenReturn(true); mPluginExceptionHandler.uncaughtException(Thread.currentThread(), new Throwable()); @@ -187,7 +161,7 @@ public class PluginManagerTest extends SysuiTestCase { @Test public void testExceptionHandler_noFoundPlugin() { mPluginManager.addPluginListener("myAction", mMockListener, TestPlugin.class); - when(mMockPluginInstance.checkAndDisable(Mockito.any())).thenReturn(false); + when(mMockPluginInstance.checkAndDisable(any())).thenReturn(false); mPluginExceptionHandler.uncaughtException(Thread.currentThread(), new Throwable()); @@ -211,9 +185,7 @@ public class PluginManagerTest extends SysuiTestCase { intent.setData(Uri.parse("package://" + testComponent.flattenToString())); mPluginManager.onReceive(mContext, intent); verify(nm).cancel(eq(testComponent.getClassName()), eq(SystemMessage.NOTE_PLUGIN)); - verify(mMockPackageManager).setComponentEnabledSetting(eq(testComponent), - eq(PackageManager.COMPONENT_ENABLED_STATE_DISABLED), - eq(PackageManager.DONT_KILL_APP)); + verify(mPluginEnabler).setDisabled(testComponent, PluginEnabler.DISABLED_INVALID_VERSION); } private void resetExceptionHandler() { @@ -223,8 +195,8 @@ public class PluginManagerTest extends SysuiTestCase { } @ProvidesInterface(action = TestPlugin.ACTION, version = TestPlugin.VERSION) - public static interface TestPlugin extends Plugin { - public static final String ACTION = "testAction"; - public static final int VERSION = 1; + public interface TestPlugin extends Plugin { + String ACTION = "testAction"; + int VERSION = 1; } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java index f5ce673c249e..4c9006316175 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java @@ -111,6 +111,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { private static final ComponentName DEVICE_OWNER_COMPONENT = new ComponentName("com.android.foo", "bar"); + private String mKeyguardTryFingerprintMsg; private String mDisclosureWithOrganization; private String mDisclosureGeneric; private String mFinancedDisclosureWithOrganization; @@ -182,6 +183,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { mContext.addMockSystemService(UserManager.class, mUserManager); mContext.addMockSystemService(Context.TRUST_SERVICE, mock(TrustManager.class)); mContext.addMockSystemService(Context.FINGERPRINT_SERVICE, mock(FingerprintManager.class)); + mKeyguardTryFingerprintMsg = mContext.getString(R.string.keyguard_try_fingerprint); mDisclosureWithOrganization = mContext.getString(R.string.do_disclosure_with_name, ORGANIZATION_NAME); mDisclosureGeneric = mContext.getString(R.string.do_disclosure_generic); @@ -677,6 +679,34 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { verifyTransientMessage(message); } + @Test + public void faceAuthMessageSuppressed() { + createController(); + String faceHelpMsg = "Face auth help message"; + + // GIVEN state of showing message when keyguard screen is on + when(mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(true); + when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(false); + when(mKeyguardUpdateMonitor.isScreenOn()).thenReturn(true); + + // GIVEN fingerprint is also running (not udfps) + when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(true); + when(mKeyguardUpdateMonitor.isUdfpsAvailable()).thenReturn(false); + + mController.setVisible(true); + + // WHEN a face help message comes in + mController.getKeyguardCallback().onBiometricHelp( + KeyguardUpdateMonitor.BIOMETRIC_HELP_FACE_NOT_RECOGNIZED, faceHelpMsg, + BiometricSourceType.FACE); + + // THEN "try fingerprint" message appears (and not the face help message) + verifyTransientMessage(mKeyguardTryFingerprintMsg); + + // THEN the face help message is still announced for a11y + verify(mIndicationAreaBottom).announceForAccessibility(eq(faceHelpMsg)); + } + private void sendUpdateDisclosureBroadcast() { mBroadcastReceiver.onReceive(mContext, new Intent()); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LightRevealScrimTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LightRevealScrimTest.kt new file mode 100644 index 000000000000..97fe25d9a619 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LightRevealScrimTest.kt @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.systemui.statusbar + +import android.testing.AndroidTestingRunner +import android.view.View +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import java.util.function.Consumer + +@RunWith(AndroidTestingRunner::class) +@SmallTest +class LightRevealScrimTest : SysuiTestCase() { + + private lateinit var scrim: LightRevealScrim + private var isOpaque = false + + @Before + fun setUp() { + scrim = LightRevealScrim(context, null) + scrim.isScrimOpaqueChangedListener = Consumer { opaque -> + isOpaque = opaque + } + scrim.revealAmount = 0f + assertTrue("Scrim is not opaque in initial setup", scrim.isScrimOpaque) + } + + @Test + fun testAlphaSetsOpaque() { + scrim.alpha = 0.5f + assertFalse("Scrim is opaque even though alpha is set", scrim.isScrimOpaque) + } + + @Test + fun testVisibilitySetsOpaque() { + scrim.visibility = View.INVISIBLE + assertFalse("Scrim is opaque even though it's invisible", scrim.isScrimOpaque) + scrim.visibility = View.GONE + assertFalse("Scrim is opaque even though it's gone", scrim.isScrimOpaque) + } + + @Test + fun testRevealSetsOpaque() { + scrim.revealAmount = 0.5f + assertFalse("Scrim is opaque even though it's revealed", scrim.isScrimOpaque) + } +}
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt index 7c045c1f2894..a7b14460f925 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt @@ -69,7 +69,6 @@ class NotificationShadeDepthControllerTest : SysuiTestCase() { @Mock private lateinit var root: View @Mock private lateinit var viewRootImpl: ViewRootImpl @Mock private lateinit var windowToken: IBinder - @Mock private lateinit var shadeSpring: NotificationShadeDepthController.DepthAnimation @Mock private lateinit var shadeAnimation: NotificationShadeDepthController.DepthAnimation @Mock private lateinit var brightnessSpring: NotificationShadeDepthController.DepthAnimation @Mock private lateinit var listener: NotificationShadeDepthController.DepthListener @@ -89,10 +88,10 @@ class NotificationShadeDepthControllerTest : SysuiTestCase() { `when`(root.isAttachedToWindow).thenReturn(true) `when`(statusBarStateController.state).then { statusBarState } `when`(blurUtils.blurRadiusOfRatio(anyFloat())).then { answer -> - (answer.arguments[0] as Float * maxBlur).toInt() + answer.arguments[0] as Float * maxBlur.toFloat() } - `when`(blurUtils.ratioOfBlurRadius(anyInt())).then { answer -> - answer.arguments[0] as Int / maxBlur.toFloat() + `when`(blurUtils.ratioOfBlurRadius(anyFloat())).then { answer -> + answer.arguments[0] as Float / maxBlur.toFloat() } `when`(blurUtils.supportsBlursOnWindows()).thenReturn(true) `when`(blurUtils.maxBlurRadius).thenReturn(maxBlur) @@ -102,7 +101,6 @@ class NotificationShadeDepthControllerTest : SysuiTestCase() { statusBarStateController, blurUtils, biometricUnlockController, keyguardStateController, choreographer, wallpaperManager, notificationShadeWindowController, dozeParameters, dumpManager) - notificationShadeDepthController.shadeSpring = shadeSpring notificationShadeDepthController.shadeAnimation = shadeAnimation notificationShadeDepthController.brightnessMirrorSpring = brightnessSpring notificationShadeDepthController.root = root @@ -123,7 +121,6 @@ class NotificationShadeDepthControllerTest : SysuiTestCase() { fun onPanelExpansionChanged_apliesBlur_ifShade() { notificationShadeDepthController.onPanelExpansionChanged(1f /* expansion */, false /* tracking */) - verify(shadeSpring).animateTo(eq(maxBlur), any()) verify(shadeAnimation).animateTo(eq(maxBlur), any()) } @@ -172,12 +169,10 @@ class NotificationShadeDepthControllerTest : SysuiTestCase() { @Test fun onStateChanged_reevalutesBlurs_ifSameRadiusAndNewState() { onPanelExpansionChanged_apliesBlur_ifShade() - clearInvocations(shadeSpring) - clearInvocations(shadeAnimation) + clearInvocations(choreographer) statusBarState = StatusBarState.KEYGUARD statusBarStateListener.onStateChanged(statusBarState) - verify(shadeSpring).animateTo(eq(0), any()) verify(shadeAnimation).animateTo(eq(0), any()) } @@ -186,7 +181,7 @@ class NotificationShadeDepthControllerTest : SysuiTestCase() { notificationShadeDepthController.qsPanelExpansion = 1f notificationShadeDepthController.onPanelExpansionChanged(0.5f, tracking = false) notificationShadeDepthController.updateBlurCallback.doFrame(0) - verify(blurUtils).applyBlur(any(), eq(maxBlur / 2), eq(false)) + verify(blurUtils).applyBlur(any(), anyInt(), eq(false)) } @Test @@ -207,10 +202,10 @@ class NotificationShadeDepthControllerTest : SysuiTestCase() { fun setFullShadeTransition_appliesBlur_onlyIfSupported() { reset(blurUtils) `when`(blurUtils.blurRadiusOfRatio(anyFloat())).then { answer -> - (answer.arguments[0] as Float * maxBlur).toInt() + answer.arguments[0] as Float * maxBlur } - `when`(blurUtils.ratioOfBlurRadius(anyInt())).then { answer -> - answer.arguments[0] as Int / maxBlur.toFloat() + `when`(blurUtils.ratioOfBlurRadius(anyFloat())).then { answer -> + answer.arguments[0] as Float / maxBlur.toFloat() } `when`(blurUtils.maxBlurRadius).thenReturn(maxBlur) `when`(blurUtils.maxBlurRadius).thenReturn(maxBlur) @@ -239,16 +234,16 @@ class NotificationShadeDepthControllerTest : SysuiTestCase() { @Test fun updateBlurCallback_setsBlur_whenExpanded() { - `when`(shadeSpring.radius).thenReturn(maxBlur) - `when`(shadeAnimation.radius).thenReturn(maxBlur) + notificationShadeDepthController.onPanelExpansionChanged(1f, false) + `when`(shadeAnimation.radius).thenReturn(maxBlur.toFloat()) notificationShadeDepthController.updateBlurCallback.doFrame(0) verify(blurUtils).applyBlur(any(), eq(maxBlur), eq(false)) } @Test fun updateBlurCallback_ignoreShadeBlurUntilHidden_overridesZoom() { - `when`(shadeSpring.radius).thenReturn(maxBlur) - `when`(shadeAnimation.radius).thenReturn(maxBlur) + notificationShadeDepthController.onPanelExpansionChanged(1f, false) + `when`(shadeAnimation.radius).thenReturn(maxBlur.toFloat()) notificationShadeDepthController.blursDisabledForAppLaunch = true notificationShadeDepthController.updateBlurCallback.doFrame(0) verify(blurUtils).applyBlur(any(), eq(0), eq(false)) @@ -293,8 +288,8 @@ class NotificationShadeDepthControllerTest : SysuiTestCase() { // Brightness mirror is fully visible `when`(brightnessSpring.ratio).thenReturn(1f) // And shade is blurred - `when`(shadeSpring.radius).thenReturn(maxBlur) - `when`(shadeAnimation.radius).thenReturn(maxBlur) + notificationShadeDepthController.onPanelExpansionChanged(1f, false) + `when`(shadeAnimation.radius).thenReturn(maxBlur.toFloat()) notificationShadeDepthController.updateBlurCallback.doFrame(0) verify(notificationShadeWindowController).setBackgroundBlurRadius(eq(0)) @@ -304,10 +299,8 @@ class NotificationShadeDepthControllerTest : SysuiTestCase() { @Test fun ignoreShadeBlurUntilHidden_whennNull_ignoresIfShadeHasNoBlur() { - `when`(shadeSpring.radius).thenReturn(0) - `when`(shadeAnimation.radius).thenReturn(0) + `when`(shadeAnimation.radius).thenReturn(0f) notificationShadeDepthController.blursDisabledForAppLaunch = true - verify(shadeSpring, never()).animateTo(anyInt(), any()) verify(shadeAnimation, never()).animateTo(anyInt(), any()) } }
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java index b051a5987fbc..5f4670045ad6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java @@ -17,6 +17,7 @@ package com.android.systemui.statusbar.phone; import static android.view.Display.DEFAULT_DISPLAY; import static org.junit.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.atLeast; import static org.mockito.Mockito.mock; @@ -40,6 +41,7 @@ import com.android.systemui.SysuiBaseFragmentTest; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.CommandQueue; +import com.android.systemui.statusbar.OperatorNameViewController; import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler; import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController; import com.android.systemui.statusbar.policy.KeyguardStateController; @@ -71,6 +73,8 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { private final StatusBar mStatusBar = mock(StatusBar.class); private final CommandQueue mCommandQueue = mock(CommandQueue.class); + private OperatorNameViewController.Factory mOperatorNameViewControllerFactory; + private OperatorNameViewController mOperatorNameViewController; public CollapsedStatusBarFragmentTest() { super(CollapsedStatusBarFragment.class); @@ -241,6 +245,11 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { mNetworkController = mock(NetworkController.class); mStatusBarStateController = mock(StatusBarStateController.class); mKeyguardStateController = mock(KeyguardStateController.class); + mOperatorNameViewController = mock(OperatorNameViewController.class); + mOperatorNameViewControllerFactory = mock(OperatorNameViewController.Factory.class); + when(mOperatorNameViewControllerFactory.create(any())) + .thenReturn(mOperatorNameViewController); + setUpNotificationIconAreaController(); return new CollapsedStatusBarFragment( mOngoingCallController, @@ -253,7 +262,8 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { mKeyguardStateController, mNetworkController, mStatusBarStateController, - mCommandQueue); + mCommandQueue, + mOperatorNameViewControllerFactory); } private void setUpNotificationIconAreaController() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java index 9cc762c8302e..85e17ba441fa 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java @@ -17,11 +17,14 @@ package com.android.systemui.statusbar.phone; +import static com.google.common.truth.Truth.assertThat; + import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; -import android.view.ViewGroup; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; +import android.view.LayoutInflater; import androidx.test.filters.SmallTest; @@ -37,16 +40,15 @@ import com.android.systemui.statusbar.policy.UserInfoController; import org.junit.Before; import org.junit.Test; +import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @SmallTest +@RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper public class KeyguardStatusBarViewControllerTest extends SysuiTestCase { @Mock - private KeyguardStatusBarView mKeyguardStatusBarView; - @Mock - private ViewGroup mViewGroup; - @Mock private CarrierTextController mCarrierTextController; @Mock private ConfigurationController mConfigurationController; @@ -63,15 +65,19 @@ public class KeyguardStatusBarViewControllerTest extends SysuiTestCase { @Mock private BatteryMeterViewController mBatteryMeterViewController; + private KeyguardStatusBarView mKeyguardStatusBarView; private KeyguardStatusBarViewController mController; @Before public void setup() throws Exception { MockitoAnnotations.initMocks(this); - when(mKeyguardStatusBarView.getResources()).thenReturn(mContext.getResources()); - when(mKeyguardStatusBarView.findViewById(R.id.statusIcons)).thenReturn(mViewGroup); - when(mViewGroup.getContext()).thenReturn(mContext); + allowTestableLooperAsMainThread(); + TestableLooper.get(this).runWithLooper(() -> { + mKeyguardStatusBarView = + (KeyguardStatusBarView) LayoutInflater.from(mContext) + .inflate(R.layout.keyguard_status_bar, null); + }); mController = new KeyguardStatusBarViewController( mKeyguardStatusBarView, @@ -133,4 +139,27 @@ public class KeyguardStatusBarViewControllerTest extends SysuiTestCase { verify(mBatteryController).addCallback(any()); } + + @Test + public void updateTopClipping_viewClippingUpdated() { + int viewTop = 20; + mKeyguardStatusBarView.setTop(viewTop); + int notificationPanelTop = 30; + + mController.updateTopClipping(notificationPanelTop); + + assertThat(mKeyguardStatusBarView.getClipBounds().top).isEqualTo( + notificationPanelTop - viewTop); + } + + @Test + public void setNotTopClipping_viewClippingUpdatedToZero() { + // Start out with some amount of top clipping. + mController.updateTopClipping(50); + assertThat(mKeyguardStatusBarView.getClipBounds().top).isGreaterThan(0); + + mController.setNoTopClipping(); + + assertThat(mKeyguardStatusBarView.getClipBounds().top).isEqualTo(0); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewTest.java new file mode 100644 index 000000000000..3108ed9e7b98 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewTest.java @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.phone; + +import static com.google.common.truth.Truth.assertThat; + +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; +import android.view.LayoutInflater; + +import androidx.test.filters.SmallTest; + +import com.android.systemui.R; +import com.android.systemui.SysuiTestCase; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper +public class KeyguardStatusBarViewTest extends SysuiTestCase { + + private KeyguardStatusBarView mKeyguardStatusBarView; + + @Before + public void setup() throws Exception { + allowTestableLooperAsMainThread(); + TestableLooper.get(this).runWithLooper(() -> { + mKeyguardStatusBarView = + (KeyguardStatusBarView) LayoutInflater.from(mContext) + .inflate(R.layout.keyguard_status_bar, null); + }); + } + + @Test + public void setTopClipping_clippingUpdated() { + int topClipping = 40; + + mKeyguardStatusBarView.setTopClipping(topClipping); + + assertThat(mKeyguardStatusBarView.getClipBounds().top).isEqualTo(topClipping); + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java index 1387b8e1f1b9..0ea619dfadc8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java @@ -306,6 +306,8 @@ public class NotificationPanelViewTest extends SysuiTestCase { private LockscreenSmartspaceController mLockscreenSmartspaceController; @Mock private FrameLayout mSplitShadeSmartspaceContainer; + @Mock + private LockscreenGestureLogger mLockscreenGestureLogger; private SysuiStatusBarStateController mStatusBarStateController; private NotificationPanelViewController mNotificationPanelViewController; @@ -452,6 +454,7 @@ public class NotificationPanelViewTest extends SysuiTestCase { mSplitShadeHeaderController, mLockscreenSmartspaceController, mUnlockedScreenOffAnimationController, + mLockscreenGestureLogger, mNotificationRemoteInputManager, mControlsComponent); mNotificationPanelViewController.initDependencies( diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImplTest.java index ddd78541d113..90b8a74d88be 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImplTest.java @@ -146,7 +146,7 @@ public class NotificationShadeWindowControllerImplTest extends SysuiTestCase { mNotificationShadeWindowController.attach(); clearInvocations(mWindowManager); - mNotificationShadeWindowController.setLightRevealScrimAmount(0f); + mNotificationShadeWindowController.setLightRevealScrimOpaque(true); verify(mWindowManager).updateViewLayout(any(), mLayoutParameters.capture()); assertThat((mLayoutParameters.getValue().flags & FLAG_SHOW_WALLPAPER) == 0).isTrue(); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java index 6c1a3c90d83d..bcc257dfa3d9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java @@ -16,6 +16,11 @@ package com.android.systemui.statusbar.phone; +import static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.assertTrue; + +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -55,6 +60,8 @@ import com.android.systemui.util.InjectionInflationController; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @@ -91,6 +98,10 @@ public class NotificationShadeWindowViewTest extends SysuiTestCase { @Mock private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; @Mock private LockscreenShadeTransitionController mLockscreenShadeTransitionController; + @Captor private ArgumentCaptor<NotificationShadeWindowView.InteractionEventHandler> + mInteractionEventHandlerCaptor; + private NotificationShadeWindowView.InteractionEventHandler mInteractionEventHandler; + @Before public void setUp() { MockitoAnnotations.initMocks(this); @@ -147,4 +158,49 @@ public class NotificationShadeWindowViewTest extends SysuiTestCase { verify(mDragDownHelper).onTouchEvent(ev); ev.recycle(); } + + @Test + public void testInterceptTouchWhenShowingAltAuth() { + captureInteractionEventHandler(); + + // WHEN showing alt auth, not dozing, drag down helper doesn't want to intercept + when(mStatusBarStateController.isDozing()).thenReturn(false); + when(mStatusBarKeyguardViewManager.isShowingAlternateAuthOrAnimating()).thenReturn(true); + when(mDragDownHelper.onInterceptTouchEvent(any())).thenReturn(false); + + // THEN we should intercept touch + assertTrue(mInteractionEventHandler.shouldInterceptTouchEvent(mock(MotionEvent.class))); + } + + @Test + public void testNoInterceptTouch() { + captureInteractionEventHandler(); + + // WHEN not showing alt auth, not dozing, drag down helper doesn't want to intercept + when(mStatusBarStateController.isDozing()).thenReturn(false); + when(mStatusBarKeyguardViewManager.isShowingAlternateAuthOrAnimating()).thenReturn(false); + when(mDragDownHelper.onInterceptTouchEvent(any())).thenReturn(false); + + // THEN we shouldn't intercept touch + assertFalse(mInteractionEventHandler.shouldInterceptTouchEvent(mock(MotionEvent.class))); + } + + @Test + public void testHandleTouchEventWhenShowingAltAuth() { + captureInteractionEventHandler(); + + // WHEN showing alt auth, not dozing, drag down helper doesn't want to intercept + when(mStatusBarStateController.isDozing()).thenReturn(false); + when(mStatusBarKeyguardViewManager.isShowingAlternateAuthOrAnimating()).thenReturn(true); + when(mDragDownHelper.onInterceptTouchEvent(any())).thenReturn(false); + + // THEN we should handle the touch + assertTrue(mInteractionEventHandler.handleTouchEvent(mock(MotionEvent.class))); + } + + private void captureInteractionEventHandler() { + verify(mView).setInteractionEventHandler(mInteractionEventHandlerCaptor.capture()); + mInteractionEventHandler = mInteractionEventHandlerCaptor.getValue(); + + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt index 50cea073c701..d63730d596d0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt @@ -38,7 +38,6 @@ class PhoneStatusBarViewControllerTest : SysuiTestCase() { @Before fun setUp() { MockitoAnnotations.initMocks(this) - view = PhoneStatusBarView(mContext, null) controller = PhoneStatusBarViewController(view, commandQueue) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java index 30fc13b168f3..195390347d1c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java @@ -1139,6 +1139,19 @@ public class ScrimControllerTest extends SysuiTestCase { } @Test + public void testNotificationTransparency_unnocclusion() { + mScrimController.transitionTo(ScrimState.KEYGUARD); + mScrimController.setUnocclusionAnimationRunning(true); + + assertAlphaAfterExpansion(mNotificationsScrim, /* alpha */ 0.0f, /* expansion */ 0.0f); + assertAlphaAfterExpansion(mNotificationsScrim, /* alpha */ 0.0f, /* expansion */ 1.0f); + + // Verify normal behavior after + mScrimController.setUnocclusionAnimationRunning(false); + assertAlphaAfterExpansion(mNotificationsScrim, /* alpha */ 0.2f, /* expansion */ 0.4f); + } + + @Test public void testNotificationTransparency_inKeyguardState() { mScrimController.transitionTo(ScrimState.KEYGUARD); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java index c39a9061f95d..38f36bfa8cfb 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java @@ -64,6 +64,8 @@ import org.mockito.MockitoAnnotations; import java.util.Optional; +import dagger.Lazy; + @SmallTest @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper @@ -103,6 +105,8 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { private UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController; @Mock private KeyguardMessageArea mKeyguardMessageArea; + @Mock + private Lazy<ShadeController> mShadeController; private WakefulnessLifecycle mWakefulnessLifecycle; private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; @@ -133,7 +137,8 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { mKeyguardBouncerFactory, mWakefulnessLifecycle, mUnlockedScreenOffAnimationController, - mKeyguardMessageAreaFactory); + mKeyguardMessageAreaFactory, + mShadeController); mStatusBarKeyguardViewManager.registerStatusBar(mStatusBar, mContainer, mNotificationPanelView, mBiometrucUnlockController, mNotificationContainer, mBypassController); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java index fd85c4485404..c80c07249cc5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java @@ -31,9 +31,9 @@ import android.testing.TestableLooper.RunWithLooper; import androidx.test.filters.SmallTest; -import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.logging.testing.FakeMetricsLogger; +import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.ForegroundServiceNotificationListener; import com.android.systemui.InitController; import com.android.systemui.SysuiTestCase; @@ -51,7 +51,6 @@ import com.android.systemui.statusbar.notification.DynamicPrivacyController; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder; -import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager; import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider; import com.android.systemui.statusbar.notification.interruption.NotificationInterruptSuppressor; import com.android.systemui.statusbar.notification.row.ActivatableNotificationView; @@ -59,6 +58,7 @@ import com.android.systemui.statusbar.notification.row.NotificationGutsManager; import com.android.systemui.statusbar.notification.stack.NotificationListContainer; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController; +import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.KeyguardStateController; import org.junit.Before; @@ -66,14 +66,10 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; -import java.util.ArrayList; - @SmallTest @RunWith(AndroidTestingRunner.class) @RunWithLooper() public class StatusBarNotificationPresenterTest extends SysuiTestCase { - - private StatusBarNotificationPresenter mStatusBarNotificationPresenter; private NotificationInterruptStateProvider mNotificationInterruptStateProvider = mock(NotificationInterruptStateProvider.class); @@ -86,27 +82,16 @@ public class StatusBarNotificationPresenterTest extends SysuiTestCase { @Before public void setup() { - NotificationRemoteInputManager notificationRemoteInputManager = - mock(NotificationRemoteInputManager.class); mMetricsLogger = new FakeMetricsLogger(); - mDependency.injectTestDependency(MetricsLogger.class, mMetricsLogger); + LockscreenGestureLogger lockscreenGestureLogger = new LockscreenGestureLogger( + mMetricsLogger); mCommandQueue = new CommandQueue(mContext); mDependency.injectTestDependency(StatusBarStateController.class, mock(SysuiStatusBarStateController.class)); mDependency.injectTestDependency(ShadeController.class, mShadeController); - mDependency.injectTestDependency(NotificationRemoteInputManager.class, - notificationRemoteInputManager); - mDependency.injectMockDependency(NotificationViewHierarchyManager.class); mDependency.injectMockDependency(NotificationRemoteInputManager.Callback.class); - mDependency.injectMockDependency(NotificationLockscreenUserManager.class); - mDependency.injectMockDependency(NotificationMediaManager.class); - mDependency.injectMockDependency(VisualStabilityManager.class); - mDependency.injectMockDependency(NotificationGutsManager.class); mDependency.injectMockDependency(NotificationShadeWindowController.class); mDependency.injectMockDependency(ForegroundServiceNotificationListener.class); - NotificationEntryManager entryManager = - mDependency.injectMockDependency(NotificationEntryManager.class); - when(entryManager.getActiveNotificationsForCurrentUser()).thenReturn(new ArrayList<>()); NotificationShadeWindowView notificationShadeWindowView = mock(NotificationShadeWindowView.class); @@ -126,8 +111,19 @@ public class StatusBarNotificationPresenterTest extends SysuiTestCase { mock(KeyguardStateController.class), mock(KeyguardIndicationController.class), mStatusBar, mock(ShadeControllerImpl.class), mock(LockscreenShadeTransitionController.class), - mCommandQueue, mInitController, - mNotificationInterruptStateProvider); + mCommandQueue, + mock(NotificationViewHierarchyManager.class), + mock(NotificationLockscreenUserManager.class), + mock(SysuiStatusBarStateController.class), + mock(NotificationEntryManager.class), + mock(NotificationMediaManager.class), + mock(NotificationGutsManager.class), + mock(KeyguardUpdateMonitor.class), + lockscreenGestureLogger, + mInitController, + mNotificationInterruptStateProvider, + mock(NotificationRemoteInputManager.class), + mock(ConfigurationController.class)); mInitController.executePostInitTasks(); ArgumentCaptor<NotificationInterruptSuppressor> suppressorCaptor = ArgumentCaptor.forClass(NotificationInterruptSuppressor.class); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java index e4f6e13db607..71c61ab7f9b7 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java @@ -105,6 +105,7 @@ import com.android.systemui.statusbar.NotificationRemoteInputManager; import com.android.systemui.statusbar.NotificationShadeDepthController; import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.NotificationViewHierarchyManager; +import com.android.systemui.statusbar.OperatorNameViewController; import com.android.systemui.statusbar.PulseExpansionHandler; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.StatusBarStateControllerImpl; @@ -137,13 +138,14 @@ import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.NetworkController; import com.android.systemui.statusbar.policy.UserInfoControllerImpl; import com.android.systemui.statusbar.policy.UserSwitcherController; -import com.android.systemui.unfold.UnfoldLightRevealOverlayAnimation; import com.android.systemui.tuner.TunerService; +import com.android.systemui.unfold.UnfoldLightRevealOverlayAnimation; import com.android.systemui.util.concurrency.FakeExecutor; +import com.android.systemui.util.concurrency.MessageRouterImpl; import com.android.systemui.util.time.FakeSystemClock; import com.android.systemui.volume.VolumeComponent; import com.android.systemui.wmshell.BubblesManager; -import com.android.unfold.config.UnfoldTransitionConfig; +import com.android.systemui.unfold.config.UnfoldTransitionConfig; import com.android.wm.shell.bubbles.Bubbles; import com.android.wm.shell.legacysplitscreen.LegacySplitScreen; import com.android.wm.shell.startingsurface.StartingSurface; @@ -205,6 +207,7 @@ public class StatusBarTest extends SysuiTestCase { @Mock private NotificationShadeWindowView mNotificationShadeWindowView; @Mock private BroadcastDispatcher mBroadcastDispatcher; @Mock private AssistManager mAssistManager; + @Mock private NotificationEntryManager mNotificationEntryManager; @Mock private NotificationGutsManager mNotificationGutsManager; @Mock private NotificationMediaManager mNotificationMediaManager; @Mock private NavigationBarController mNavigationBarController; @@ -226,6 +229,7 @@ public class StatusBarTest extends SysuiTestCase { @Mock private NotificationShadeWindowViewController mNotificationShadeWindowViewController; @Mock private DozeParameters mDozeParameters; @Mock private Lazy<LockscreenWallpaper> mLockscreenWallpaperLazy; + @Mock private LockscreenGestureLogger mLockscreenGestureLogger; @Mock private LockscreenWallpaper mLockscreenWallpaper; @Mock private DozeServiceHost mDozeServiceHost; @Mock private ViewMediatorCallback mKeyguardVieMediatorCallback; @@ -265,8 +269,12 @@ public class StatusBarTest extends SysuiTestCase { @Mock private UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController; @Mock private TunerService mTunerService; @Mock private StartingSurface mStartingSurface; + @Mock private OperatorNameViewController mOperatorNameViewController; + @Mock private OperatorNameViewController.Factory mOperatorNameViewControllerFactory; private ShadeController mShadeController; - private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock()); + private final FakeSystemClock mFakeSystemClock = new FakeSystemClock(); + private FakeExecutor mMainExecutor = new FakeExecutor(mFakeSystemClock); + private FakeExecutor mUiBgExecutor = new FakeExecutor(mFakeSystemClock); private InitController mInitController = new InitController(); @Before @@ -343,6 +351,9 @@ public class StatusBarTest extends SysuiTestCase { mStatusBarKeyguardViewManager, mContext.getSystemService(WindowManager.class), () -> Optional.of(mStatusBar), () -> mAssistManager, Optional.of(mBubbles)); + when(mOperatorNameViewControllerFactory.create(any())) + .thenReturn(mOperatorNameViewController); + mStatusBar = new StatusBar( mContext, mNotificationsController, @@ -359,6 +370,7 @@ public class StatusBarTest extends SysuiTestCase { new FalsingManagerFake(), new FalsingCollectorFake(), mBroadcastDispatcher, + mNotificationEntryManager, mNotificationGutsManager, notificationLogger, mNotificationInterruptStateProvider, @@ -388,6 +400,7 @@ public class StatusBarTest extends SysuiTestCase { mDozeParameters, mScrimController, mLockscreenWallpaperLazy, + mLockscreenGestureLogger, mBiometricUnlockControllerLazy, mDozeServiceHost, mPowerManager, mScreenPinningRequest, @@ -409,6 +422,7 @@ public class StatusBarTest extends SysuiTestCase { mKeyguardDismissUtil, mExtensionController, mUserInfoControllerImpl, + mOperatorNameViewControllerFactory, mPhoneStatusBarPolicy, mKeyguardIndicationController, mDemoModeController, @@ -425,6 +439,9 @@ public class StatusBarTest extends SysuiTestCase { mLockscreenTransitionController, mFeatureFlags, mKeyguardUnlockAnimationController, + new Handler(TestableLooper.get(this).getLooper()), + mMainExecutor, + new MessageRouterImpl(mMainExecutor), mWallpaperManager, mUnlockedScreenOffAnimationController, Optional.of(mStartingSurface), @@ -691,7 +708,7 @@ public class StatusBarTest extends SysuiTestCase { } catch (RemoteException e) { fail(); } - TestableLooper.get(this).processAllMessages(); + mMainExecutor.runAllReady(); } @Test @@ -710,7 +727,7 @@ public class StatusBarTest extends SysuiTestCase { } catch (RemoteException e) { fail(); } - TestableLooper.get(this).processAllMessages(); + mMainExecutor.runAllReady(); } @Test @@ -728,7 +745,7 @@ public class StatusBarTest extends SysuiTestCase { } catch (RemoteException e) { fail(); } - TestableLooper.get(this).processAllMessages(); + mMainExecutor.runAllReady(); } @Test @@ -743,15 +760,6 @@ public class StatusBarTest extends SysuiTestCase { } @Test - @RunWithLooper(setAsMainLooper = true) - public void testUpdateKeyguardState_DoesNotCrash() { - mStatusBar.setBarStateForTest(StatusBarState.KEYGUARD); - when(mLockscreenUserManager.getCurrentProfiles()).thenReturn( - new SparseArray<>()); - mStatusBar.onStateChanged(StatusBarState.SHADE); - } - - @Test public void testFingerprintNotification_UpdatesScrims() { mStatusBar.notifyBiometricAuthModeChanged(); verify(mScrimController).transitionTo(any(), any()); @@ -767,6 +775,30 @@ public class StatusBarTest extends SysuiTestCase { } @Test + public void testTransitionLaunch_goesToUnlocked() { + mStatusBar.setBarStateForTest(StatusBarState.KEYGUARD); + mStatusBar.showKeyguardImpl(); + + // Starting a pulse should change the scrim controller to the pulsing state + when(mNotificationPanelViewController.isLaunchTransitionRunning()).thenReturn(true); + when(mNotificationPanelViewController.isLaunchingAffordanceWithPreview()).thenReturn(true); + mStatusBar.updateScrimController(); + verify(mScrimController).transitionTo(eq(ScrimState.UNLOCKED), any()); + } + + @Test + public void testTransitionLaunch_noPreview_doesntGoUnlocked() { + mStatusBar.setBarStateForTest(StatusBarState.KEYGUARD); + mStatusBar.showKeyguardImpl(); + + // Starting a pulse should change the scrim controller to the pulsing state + when(mNotificationPanelViewController.isLaunchTransitionRunning()).thenReturn(true); + when(mNotificationPanelViewController.isLaunchingAffordanceWithPreview()).thenReturn(false); + mStatusBar.updateScrimController(); + verify(mScrimController).transitionTo(eq(ScrimState.KEYGUARD)); + } + + @Test public void testSetOccluded_propagatesToScrimController() { mStatusBar.setOccluded(true); verify(mScrimController).setKeyguardOccluded(eq(true)); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java index 57198dbe18bb..4a5770d12239 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java @@ -238,7 +238,7 @@ public class NetworkControllerWifiTest extends NetworkControllerBaseTest { mNetworkController.setNoNetworksAvailable(false); setWifiStateForVcn(true, testSsid); setWifiLevelForVcn(0); - verifyLastWifiIcon(true, WifiIcons.WIFI_SIGNAL_STRENGTH[0][0]); + verifyLastMobileDataIndicatorsForVcn(true, 0, TelephonyIcons.ICON_CWF, false); mNetworkController.setNoNetworksAvailable(true); for (int testLevel = 0; testLevel < WifiIcons.WIFI_LEVEL_COUNT; testLevel++) { @@ -246,11 +246,11 @@ public class NetworkControllerWifiTest extends NetworkControllerBaseTest { setConnectivityViaCallbackInNetworkControllerForVcn( NetworkCapabilities.TRANSPORT_CELLULAR, true, true, mVcnTransportInfo); - verifyLastWifiIcon(true, WifiIcons.WIFI_SIGNAL_STRENGTH[1][testLevel]); + verifyLastMobileDataIndicatorsForVcn(true, testLevel, TelephonyIcons.ICON_CWF, true); setConnectivityViaCallbackInNetworkControllerForVcn( NetworkCapabilities.TRANSPORT_CELLULAR, false, true, mVcnTransportInfo); - verifyLastWifiIcon(true, WifiIcons.WIFI_SIGNAL_STRENGTH[0][testLevel]); + verifyLastMobileDataIndicatorsForVcn(true, testLevel, TelephonyIcons.ICON_CWF, false); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/VariableDateViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/VariableDateViewControllerTest.kt new file mode 100644 index 000000000000..871a48c503be --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/VariableDateViewControllerTest.kt @@ -0,0 +1,176 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.policy + +import android.os.Handler +import android.testing.AndroidTestingRunner +import android.testing.TestableLooper +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.broadcast.BroadcastDispatcher +import com.android.systemui.util.mockito.any +import com.android.systemui.util.mockito.capture +import com.android.systemui.util.time.FakeSystemClock +import com.google.common.truth.Truth.assertThat +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentCaptor +import org.mockito.Captor +import org.mockito.Mock +import org.mockito.Mockito.`when` +import org.mockito.Mockito.anyString +import org.mockito.Mockito.verify +import org.mockito.MockitoAnnotations +import java.util.Date + +@RunWith(AndroidTestingRunner::class) +@TestableLooper.RunWithLooper +@SmallTest +class VariableDateViewControllerTest : SysuiTestCase() { + + companion object { + private const val TIME_STAMP = 1_500_000_000_000 + private const val LONG_PATTERN = "EEEMMMd" + private const val SHORT_PATTERN = "MMMd" + private const val CHAR_WIDTH = 10f + } + + @Mock + private lateinit var broadcastDispatcher: BroadcastDispatcher + @Mock + private lateinit var view: VariableDateView + @Captor + private lateinit var onMeasureListenerCaptor: ArgumentCaptor<VariableDateView.OnMeasureListener> + + private var lastText: String? = null + + private lateinit var systemClock: FakeSystemClock + private lateinit var testableLooper: TestableLooper + private lateinit var testableHandler: Handler + private lateinit var controller: VariableDateViewController + + private lateinit var longText: String + private lateinit var shortText: String + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + testableLooper = TestableLooper.get(this) + testableHandler = Handler(testableLooper.looper) + + systemClock = FakeSystemClock() + systemClock.setCurrentTimeMillis(TIME_STAMP) + + `when`(view.longerPattern).thenReturn(LONG_PATTERN) + `when`(view.shorterPattern).thenReturn(SHORT_PATTERN) + `when`(view.handler).thenReturn(testableHandler) + + `when`(view.setText(anyString())).thenAnswer { + lastText = it.arguments[0] as? String + Unit + } + `when`(view.isAttachedToWindow).thenReturn(true) + + val date = Date(TIME_STAMP) + longText = getTextForFormat(date, getFormatFromPattern(LONG_PATTERN)) + shortText = getTextForFormat(date, getFormatFromPattern(SHORT_PATTERN)) + + // Assume some sizes for the text, the controller doesn't need to know if these sizes are + // the true ones + `when`(view.getDesiredWidthForText(any())).thenAnswer { + getTextLength(it.arguments[0] as CharSequence) + } + + controller = VariableDateViewController( + systemClock, + broadcastDispatcher, + testableHandler, + view + ) + + controller.init() + testableLooper.processAllMessages() + + verify(view).onAttach(capture(onMeasureListenerCaptor)) + } + + @Test + fun testViewStartsWithLongText() { + assertThat(lastText).isEqualTo(longText) + } + + @Test + fun testListenerNotNull() { + assertThat(onMeasureListenerCaptor.value).isNotNull() + } + + @Test + fun testLotsOfSpaceUseLongText() { + onMeasureListenerCaptor.value.onMeasureAction(10000) + + testableLooper.processAllMessages() + assertThat(lastText).isEqualTo(longText) + } + + @Test + fun testSmallSpaceUseEmpty() { + onMeasureListenerCaptor.value.onMeasureAction(1) + testableLooper.processAllMessages() + + assertThat(lastText).isEmpty() + } + + @Test + fun testSpaceInBetweenUseShortText() { + val average = ((getTextLength(longText) + getTextLength(shortText)) / 2).toInt() + + onMeasureListenerCaptor.value.onMeasureAction(average) + testableLooper.processAllMessages() + + assertThat(lastText).isEqualTo(shortText) + } + + @Test + fun testSwitchBackToLonger() { + onMeasureListenerCaptor.value.onMeasureAction(1) + testableLooper.processAllMessages() + + onMeasureListenerCaptor.value.onMeasureAction(10000) + testableLooper.processAllMessages() + + assertThat(lastText).isEqualTo(longText) + } + + @Test + fun testNoSwitchingWhenFrozen() { + `when`(view.freezeSwitching).thenReturn(true) + + val average = ((getTextLength(longText) + getTextLength(shortText)) / 2).toInt() + onMeasureListenerCaptor.value.onMeasureAction(average) + testableLooper.processAllMessages() + assertThat(lastText).isEqualTo(longText) + + onMeasureListenerCaptor.value.onMeasureAction(1) + testableLooper.processAllMessages() + assertThat(lastText).isEqualTo(longText) + } + + private fun getTextLength(text: CharSequence): Float { + return text.length * CHAR_WIDTH + } +}
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/usb/UsbPermissionActivityTest.kt b/packages/SystemUI/tests/src/com/android/systemui/usb/UsbPermissionActivityTest.kt new file mode 100644 index 000000000000..eebcbe63d004 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/usb/UsbPermissionActivityTest.kt @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ +package com.android.systemui.usb + +import android.app.PendingIntent +import android.content.Intent +import android.hardware.usb.IUsbSerialReader +import android.hardware.usb.UsbAccessory +import android.hardware.usb.UsbManager +import android.testing.AndroidTestingRunner +import android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS +import androidx.test.filters.SmallTest +import androidx.test.rule.ActivityTestRule +import com.android.systemui.SysuiTestCase +import com.google.common.truth.Truth.assertThat +import org.junit.After +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import java.lang.Exception + +/** + * UsbPermissionActivityTest + */ +@RunWith(AndroidTestingRunner::class) +@SmallTest +class UsbPermissionActivityTest : SysuiTestCase() { + + class UsbPermissionActivityTestable : UsbPermissionActivity() + + @Rule + @JvmField + var activityRule = ActivityTestRule<UsbPermissionActivityTestable>( + UsbPermissionActivityTestable::class.java, false, false) + + private val activityIntent = Intent(mContext, UsbPermissionActivityTestable::class.java) + .apply { + flags = Intent.FLAG_ACTIVITY_NEW_TASK + putExtra(UsbManager.EXTRA_PACKAGE, "com.android.systemui") + putExtra(Intent.EXTRA_INTENT, PendingIntent.getBroadcast( + mContext, + 334, + Intent("NO_ACTION"), + PendingIntent.FLAG_MUTABLE)) + putExtra(UsbManager.EXTRA_ACCESSORY, UsbAccessory( + "manufacturer", + "model", + "description", + "version", + "uri", + object : IUsbSerialReader.Stub() { + override fun getSerial(packageName: String): String { + return "serial" + } + })) + } + + @Before + fun setUp() { + activityRule.launchActivity(activityIntent) + } + + @After + fun tearDown() { + activityRule.finishActivity() + } + + @Test + @Throws(Exception::class) + fun testHideNonSystemOverlay() { + assertThat(activityRule.activity.window.attributes.privateFlags and + SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS) + .isEqualTo(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS) + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorDualTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorDualTest.java index a34c5986f36c..0e9d96c61e54 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorDualTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorDualTest.java @@ -16,6 +16,8 @@ package com.android.systemui.util.sensors; +import static com.google.common.truth.Truth.assertThat; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; @@ -338,30 +340,25 @@ public class ProximitySensorDualTest extends SysuiTestCase { @Test public void testSecondaryCancelsSecondary() { TestableListener listener = new TestableListener(); - ThresholdSensor.Listener cancelingListener = new ThresholdSensor.Listener() { - @Override - public void onThresholdCrossed(ThresholdSensor.ThresholdSensorEvent event) { - mProximitySensor.pause(); - } - }; + ThresholdSensor.Listener cancelingListener = event -> mProximitySensor.pause(); mProximitySensor.register(listener); mProximitySensor.register(cancelingListener); - assertNull(listener.mLastEvent); - assertEquals(0, listener.mCallCount); + assertThat(listener.mLastEvent).isNull(); + assertThat(listener.mCallCount).isEqualTo(0); mThresholdSensorPrimary.triggerEvent(true, 0); - assertNull(listener.mLastEvent); - assertEquals(0, listener.mCallCount); + assertThat(listener.mLastEvent).isNull(); + assertThat(listener.mCallCount).isEqualTo(0); mThresholdSensorSecondary.triggerEvent(true, 0); - assertTrue(listener.mLastEvent.getBelow()); - assertEquals(1, listener.mCallCount); + assertThat(listener.mLastEvent.getBelow()).isTrue(); + assertThat(listener.mCallCount).isEqualTo(1); // The proximity sensor should now be canceled. Advancing the clock should do nothing. - assertEquals(0, mFakeExecutor.numPending()); + assertThat(mFakeExecutor.numPending()).isEqualTo(0); mThresholdSensorSecondary.triggerEvent(false, 1); - assertTrue(listener.mLastEvent.getBelow()); - assertEquals(1, listener.mCallCount); + assertThat(listener.mLastEvent.getBelow()).isTrue(); + assertThat(listener.mCallCount).isEqualTo(1); mProximitySensor.unregister(listener); } @@ -372,33 +369,66 @@ public class ProximitySensorDualTest extends SysuiTestCase { TestableListener listener = new TestableListener(); - // WE immediately register the secondary sensor. + // We immediately register the secondary sensor. mProximitySensor.register(listener); - assertFalse(mThresholdSensorPrimary.isPaused()); - assertFalse(mThresholdSensorSecondary.isPaused()); - assertNull(listener.mLastEvent); - assertEquals(0, listener.mCallCount); + assertThat(mThresholdSensorPrimary.isPaused()).isTrue(); + assertThat(mThresholdSensorSecondary.isPaused()).isFalse(); + assertThat(listener.mLastEvent).isNull(); + assertThat(listener.mCallCount).isEqualTo(0); mThresholdSensorPrimary.triggerEvent(true, 0); - assertNull(listener.mLastEvent); - assertEquals(0, listener.mCallCount); + assertThat(listener.mLastEvent).isNull(); + assertThat(listener.mCallCount).isEqualTo(0); mThresholdSensorSecondary.triggerEvent(true, 0); - assertTrue(listener.mLastEvent.getBelow()); - assertEquals(1, listener.mCallCount); + assertThat(listener.mLastEvent.getBelow()).isTrue(); + assertThat(listener.mCallCount).isEqualTo(1); // The secondary sensor should now remain resumed indefinitely. - assertFalse(mThresholdSensorSecondary.isPaused()); + assertThat(mThresholdSensorSecondary.isPaused()).isFalse(); mThresholdSensorSecondary.triggerEvent(false, 1); - assertFalse(listener.mLastEvent.getBelow()); - assertEquals(2, listener.mCallCount); + assertThat(listener.mLastEvent.getBelow()).isFalse(); + assertThat(listener.mCallCount).isEqualTo(2); // The secondary is still running, and not polling with the executor. - assertFalse(mThresholdSensorSecondary.isPaused()); - assertEquals(0, mFakeExecutor.numPending()); + assertThat(mThresholdSensorSecondary.isPaused()).isFalse(); + assertThat(mFakeExecutor.numPending()).isEqualTo(0); mProximitySensor.unregister(listener); } + @Test + public void testSecondaryPausesPrimary() { + TestableListener listener = new TestableListener(); + + mProximitySensor.register(listener); + + assertThat(mThresholdSensorPrimary.isPaused()).isFalse(); + assertThat(mThresholdSensorSecondary.isPaused()).isTrue(); + + mProximitySensor.setSecondarySafe(true); + + assertThat(mThresholdSensorPrimary.isPaused()).isTrue(); + assertThat(mThresholdSensorSecondary.isPaused()).isFalse(); + } + + @Test + public void testSecondaryResumesPrimary() { + mProximitySensor.setSecondarySafe(true); + + TestableListener listener = new TestableListener(); + mProximitySensor.register(listener); + + assertThat(mThresholdSensorPrimary.isPaused()).isTrue(); + assertThat(mThresholdSensorSecondary.isPaused()).isFalse(); + + mProximitySensor.setSecondarySafe(false); + + assertThat(mThresholdSensorPrimary.isPaused()).isFalse(); + assertThat(mThresholdSensorSecondary.isPaused()).isTrue(); + + + } + private static class TestableListener implements ThresholdSensor.Listener { ThresholdSensor.ThresholdSensorEvent mLastEvent; int mCallCount = 0; diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakePluginManager.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakePluginManager.java index 6d1e6ce9ea33..8e1c0f7301e1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakePluginManager.java +++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakePluginManager.java @@ -22,7 +22,7 @@ import com.android.systemui.shared.plugins.PluginManager; public class FakePluginManager implements PluginManager { - private final BaseLeakChecker<PluginListener> mLeakChecker; + private final BaseLeakChecker<PluginListener<?>> mLeakChecker; public FakePluginManager(LeakCheck test) { mLeakChecker = new BaseLeakChecker<>(test, "Plugin"); @@ -30,7 +30,7 @@ public class FakePluginManager implements PluginManager { @Override public <T extends Plugin> void addPluginListener(String action, PluginListener<T> listener, - Class cls, boolean allowMultiple) { + Class<?> cls, boolean allowMultiple) { mLeakChecker.addCallback(listener); } @@ -62,17 +62,7 @@ public class FakePluginManager implements PluginManager { } @Override - public String[] getWhitelistedPlugins() { + public String[] getPrivilegedPlugins() { return new String[0]; } - - @Override - public <T extends Plugin> T getOneShotPlugin(Class<T> cls) { - return null; - } - - @Override - public <T extends Plugin> T getOneShotPlugin(String action, Class<?> cls) { - return null; - } } diff --git a/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java b/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java index 561d079cb984..b0893cc360b7 100644 --- a/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java +++ b/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java @@ -970,14 +970,14 @@ public class CameraExtensionsProxyService extends Service { } @Override - public boolean submit(Request request, Callback callback) { + public int submit(Request request, Callback callback) { ArrayList<Request> requests = new ArrayList<>(); requests.add(request); return submit(requests, callback); } @Override - public boolean submit(List<Request> requests, Callback callback) { + public int submit(List<Request> requests, Callback callback) { ArrayList<android.hardware.camera2.extension.Request> captureRequests = new ArrayList<>(); int requestId = 0; @@ -992,11 +992,11 @@ public class CameraExtensionsProxyService extends Service { } catch (RemoteException e) { Log.e(TAG, "Failed to submit request due to remote exception!"); } - return false; + return -1; } @Override - public boolean setRepeating(Request request, Callback callback) { + public int setRepeating(Request request, Callback callback) { try { ArrayList<Request> requests = new ArrayList<>(); requests.add(request); @@ -1007,7 +1007,7 @@ public class CameraExtensionsProxyService extends Service { Log.e(TAG, "Failed to submit repeating request due to remote exception!"); } - return false; + return -1; } @Override diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java index a56b1db1494c..c32543ae5c13 100644 --- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java +++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java @@ -3306,11 +3306,12 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku Slog.w(TAG, "Failed to retrieve app info for " + packageName + " userId=" + userId, e); } - if (newAppInfo == null) { + if (newAppInfo == null || provider.info == null + || provider.info.providerInfo == null) { continue; } ApplicationInfo oldAppInfo = provider.info.providerInfo.applicationInfo; - if (!newAppInfo.sourceDir.equals(oldAppInfo.sourceDir)) { + if (oldAppInfo == null || !newAppInfo.sourceDir.equals(oldAppInfo.sourceDir)) { // Overlay paths are generated against a particular version of an application. // The overlays paths of a newly upgraded application are incompatible with the // old version of the application. diff --git a/services/core/java/com/android/server/SensorPrivacyService.java b/services/core/java/com/android/server/SensorPrivacyService.java index 422e8ae14862..91b2440f71fe 100644 --- a/services/core/java/com/android/server/SensorPrivacyService.java +++ b/services/core/java/com/android/server/SensorPrivacyService.java @@ -1021,7 +1021,15 @@ public final class SensorPrivacyService extends SystemService { } } - return upgradeAndInit(version, map); + try { + return upgradeAndInit(version, map); + } catch (Exception e) { + Log.wtf(TAG, "Failed to upgrade and set sensor privacy state," + + " resetting to default.", e); + mEnabled = new SparseBooleanArray(); + mIndividualEnabled = new SparseArray<>(); + return true; + } } private boolean upgradeAndInit(int version, SparseArray map) { @@ -1037,22 +1045,22 @@ public final class SensorPrivacyService extends SystemService { final int[] users = getLocalService(UserManagerInternal.class).getUserIds(); if (version == 0) { final boolean enabled = (boolean) map.get(VER0_ENABLED); - final SparseBooleanArray individualEnabled = - (SparseBooleanArray) map.get(VER0_INDIVIDUAL_ENABLED); + final SparseArray<SensorState> individualEnabled = + (SparseArray<SensorState>) map.get(VER0_INDIVIDUAL_ENABLED); final SparseBooleanArray perUserEnabled = new SparseBooleanArray(); - final SparseArray<SparseBooleanArray> perUserIndividualEnabled = + final SparseArray<SparseArray<SensorState>> perUserIndividualEnabled = new SparseArray<>(); // Copy global state to each user for (int i = 0; i < users.length; i++) { int user = users[i]; perUserEnabled.put(user, enabled); - SparseBooleanArray userIndividualSensorEnabled = new SparseBooleanArray(); + SparseArray<SensorState> userIndividualSensorEnabled = new SparseArray<>(); perUserIndividualEnabled.put(user, userIndividualSensorEnabled); for (int j = 0; j < individualEnabled.size(); j++) { final int sensor = individualEnabled.keyAt(j); - final boolean isSensorEnabled = individualEnabled.valueAt(j); + final SensorState isSensorEnabled = individualEnabled.valueAt(j); userIndividualSensorEnabled.put(sensor, isSensorEnabled); } } diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index f41036cde433..a2fec2753340 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -6314,10 +6314,17 @@ public final class ActiveServices { final String msg = "Background started FGS: " + ((r.mAllowStartForeground != REASON_DENIED) ? "Allowed " : "Disallowed ") + r.mInfoAllowStartForeground; - Slog.wtfQuiet(TAG, msg); if (r.mAllowStartForeground != REASON_DENIED) { + if (ActivityManagerUtils.shouldSamplePackageForAtom(r.packageName, + mAm.mConstants.mFgsStartAllowedLogSampleRate)) { + Slog.wtfQuiet(TAG, msg); + } Slog.i(TAG, msg); } else { + if (ActivityManagerUtils.shouldSamplePackageForAtom(r.packageName, + mAm.mConstants.mFgsStartDeniedLogSampleRate)) { + Slog.wtfQuiet(TAG, msg); + } Slog.w(TAG, msg); } r.mLoggedInfoAllowStartForeground = true; diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java index ac0a1985ac89..eeb41a3df969 100644 --- a/services/core/java/com/android/server/am/ActivityManagerConstants.java +++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java @@ -108,6 +108,8 @@ final class ActivityManagerConstants extends ContentObserver { static final String KEY_FG_TO_BG_FGS_GRACE_DURATION = "fg_to_bg_fgs_grace_duration"; static final String KEY_FGS_START_FOREGROUND_TIMEOUT = "fgs_start_foreground_timeout"; static final String KEY_FGS_ATOM_SAMPLE_RATE = "fgs_atom_sample_rate"; + static final String KEY_FGS_START_ALLOWED_LOG_SAMPLE_RATE = "fgs_start_allowed_log_sample_rate"; + static final String KEY_FGS_START_DENIED_LOG_SAMPLE_RATE = "fgs_start_denied_log_sample_rate"; static final String KEY_FGS_ALLOW_OPT_OUT = "fgs_allow_opt_out"; private static final int DEFAULT_MAX_CACHED_PROCESSES = 32; @@ -152,6 +154,8 @@ final class ActivityManagerConstants extends ContentObserver { private static final long DEFAULT_FG_TO_BG_FGS_GRACE_DURATION = 5 * 1000; private static final int DEFAULT_FGS_START_FOREGROUND_TIMEOUT_MS = 10 * 1000; private static final float DEFAULT_FGS_ATOM_SAMPLE_RATE = 1; // 100 % + private static final float DEFAULT_FGS_START_ALLOWED_LOG_SAMPLE_RATE = 0.25f; // 25% + private static final float DEFAULT_FGS_START_DENIED_LOG_SAMPLE_RATE = 1; // 100% /** * Same as {@link TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED} */ @@ -496,6 +500,20 @@ final class ActivityManagerConstants extends ContentObserver { volatile float mFgsAtomSampleRate = DEFAULT_FGS_ATOM_SAMPLE_RATE; /** + * Sample rate for the allowed FGS start WTF logs. + * + * If the value is 0.1, 10% of the logs would be sampled. + */ + volatile float mFgsStartAllowedLogSampleRate = DEFAULT_FGS_START_ALLOWED_LOG_SAMPLE_RATE; + + /** + * Sample rate for the denied FGS start WTF logs. + * + * If the value is 0.1, 10% of the logs would be sampled. + */ + volatile float mFgsStartDeniedLogSampleRate = DEFAULT_FGS_START_DENIED_LOG_SAMPLE_RATE; + + /** * Whether to allow "opt-out" from the foreground service restrictions. * (https://developer.android.com/about/versions/12/foreground-services) */ @@ -711,6 +729,12 @@ final class ActivityManagerConstants extends ContentObserver { case KEY_FGS_ATOM_SAMPLE_RATE: updateFgsAtomSamplePercent(); break; + case KEY_FGS_START_ALLOWED_LOG_SAMPLE_RATE: + updateFgsStartAllowedLogSamplePercent(); + break; + case KEY_FGS_START_DENIED_LOG_SAMPLE_RATE: + updateFgsStartDeniedLogSamplePercent(); + break; case KEY_FGS_ALLOW_OPT_OUT: updateFgsAllowOptOut(); break; @@ -1057,6 +1081,20 @@ final class ActivityManagerConstants extends ContentObserver { DEFAULT_FGS_ATOM_SAMPLE_RATE); } + private void updateFgsStartAllowedLogSamplePercent() { + mFgsStartAllowedLogSampleRate = DeviceConfig.getFloat( + DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + KEY_FGS_START_ALLOWED_LOG_SAMPLE_RATE, + DEFAULT_FGS_START_ALLOWED_LOG_SAMPLE_RATE); + } + + private void updateFgsStartDeniedLogSamplePercent() { + mFgsStartDeniedLogSampleRate = DeviceConfig.getFloat( + DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + KEY_FGS_START_DENIED_LOG_SAMPLE_RATE, + DEFAULT_FGS_START_DENIED_LOG_SAMPLE_RATE); + } + private void updateFgsAllowOptOut() { mFgsAllowOptOut = DeviceConfig.getBoolean( DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, @@ -1285,6 +1323,10 @@ final class ActivityManagerConstants extends ContentObserver { pw.print("="); pw.println(mFgsStartRestrictionCheckCallerTargetSdk); pw.print(" "); pw.print(KEY_FGS_ATOM_SAMPLE_RATE); pw.print("="); pw.println(mFgsAtomSampleRate); + pw.print(" "); pw.print(KEY_FGS_START_ALLOWED_LOG_SAMPLE_RATE); + pw.print("="); pw.println(mFgsStartAllowedLogSampleRate); + pw.print(" "); pw.print(KEY_FGS_START_DENIED_LOG_SAMPLE_RATE); + pw.print("="); pw.println(mFgsStartDeniedLogSampleRate); pw.print(" "); pw.print(KEY_PUSH_MESSAGING_OVER_QUOTA_BEHAVIOR); pw.print("="); pw.println(mPushMessagingOverQuotaBehavior); pw.print(" "); pw.print(KEY_FGS_ALLOW_OPT_OUT); diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java index 8961a5a05546..5ecdfe49cdf7 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java +++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java @@ -1719,11 +1719,10 @@ import java.util.concurrent.atomic.AtomicBoolean; if (client == null) { return; } - Log.w(TAG, "Speaker client died"); - if (removeCommunicationRouteClient(client.getBinder(), false) - != null) { - onUpdateCommunicationRoute("onCommunicationRouteClientDied"); - } + Log.w(TAG, "Communication client died"); + setCommunicationRouteForClient( + client.getBinder(), client.getPid(), null, BtHelper.SCO_MODE_UNDEFINED, + "onCommunicationRouteClientDied"); } /** diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index 34e2578f7855..50a758e66dcc 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -93,11 +93,13 @@ import android.media.ICommunicationDeviceDispatcher; import android.media.IPlaybackConfigDispatcher; import android.media.IRecordingConfigDispatcher; import android.media.IRingtonePlayer; +import android.media.ISpatializerCallback; import android.media.IStrategyPreferredDevicesDispatcher; import android.media.IVolumeController; import android.media.MediaMetrics; import android.media.MediaRecorder.AudioSource; import android.media.PlayerBase; +import android.media.Spatializer; import android.media.VolumePolicy; import android.media.audiofx.AudioEffect; import android.media.audiopolicy.AudioMix; @@ -8228,6 +8230,82 @@ public class AudioService extends IAudioService.Stub } //========================================================================================== + private final SpatializerHelper mSpatializerHelper = new SpatializerHelper(); + + /** @see AudioManager#getSpatializerImmersiveAudioLevel() */ + public int getSpatializerImmersiveAudioLevel() { + return Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_NONE; + } + + /** @see Spatializer#isEnabled() */ + public boolean isSpatializerEnabled() { + return false; + } + + /** @see Spatializer#canBeSpatialized() */ + public boolean canBeSpatialized( + @NonNull AudioAttributes attributes, @NonNull AudioFormat format) { + Objects.requireNonNull(attributes); + Objects.requireNonNull(format); + return mSpatializerHelper.canBeSpatialized(attributes, format); + } + + /** @see Spatializer.SpatializerInfoDispatcherStub */ + public void registerSpatializerCallback( + @NonNull ISpatializerCallback dispatcher) { + Objects.requireNonNull(dispatcher); + mSpatializerHelper.registerStateCallback(dispatcher); + } + + /** @see Spatializer.SpatializerInfoDispatcherStub */ + public void unregisterSpatializerCallback( + @NonNull ISpatializerCallback dispatcher) { + Objects.requireNonNull(dispatcher); + mSpatializerHelper.unregisterStateCallback(dispatcher); + } + + /** @see Spatializer#getSpatializerCompatibleAudioDevices() */ + public @NonNull List<AudioDeviceAttributes> getSpatializerCompatibleAudioDevices() { + enforceModifyAudioRoutingPermission(); + return mSpatializerHelper.getCompatibleAudioDevices(); + } + + /** @see Spatializer#addSpatializerCompatibleAudioDevice(AudioDeviceAttributes) */ + public void addSpatializerCompatibleAudioDevice(@NonNull AudioDeviceAttributes ada) { + enforceModifyAudioRoutingPermission(); + Objects.requireNonNull(ada); + mSpatializerHelper.addCompatibleAudioDevice(ada); + } + + /** @see Spatializer#removeSpatializerCompatibleAudioDevice(AudioDeviceAttributes) */ + public void removeSpatializerCompatibleAudioDevice(@NonNull AudioDeviceAttributes ada) { + enforceModifyAudioRoutingPermission(); + Objects.requireNonNull(ada); + mSpatializerHelper.removeCompatibleAudioDevice(ada); + } + + /** @see AudioManager#setSpatializerFeatureEnabled(boolean) */ + public void setSpatializerFeatureEnabled(boolean enabled) { + if (mContext.checkCallingOrSelfPermission( + android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Missing MODIFY_DEFAULT_AUDIO_EFFECTS permission"); + } + mSpatializerHelper.setEnabled(enabled); + } + + /** @see Spatializer#setEnabledForDevice(boolean, AudioDeviceAttributes) + * @see Spatializer#setEnabled(boolean) + */ + public void setSpatializerEnabledForDevice(boolean enabled, + @NonNull AudioDeviceAttributes ada) { + enforceModifyAudioRoutingPermission(); + Objects.requireNonNull(ada); + mSpatializerHelper.setEnabledForDevice(enabled, ada); + } + + + //========================================================================================== private boolean readCameraSoundForced() { return SystemProperties.getBoolean("audio.camerasound.force", false) || mContext.getResources().getBoolean( diff --git a/services/core/java/com/android/server/audio/SpatializerHelper.java b/services/core/java/com/android/server/audio/SpatializerHelper.java new file mode 100644 index 000000000000..1b68f840e298 --- /dev/null +++ b/services/core/java/com/android/server/audio/SpatializerHelper.java @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.audio; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.media.AudioAttributes; +import android.media.AudioDeviceAttributes; +import android.media.AudioFormat; +import android.media.ISpatializerCallback; +import android.os.RemoteCallbackList; +import android.os.RemoteException; +import android.util.Log; + +import java.util.ArrayList; +import java.util.List; + +/** + * A helper class to manage Spatializer related functionality + */ +public class SpatializerHelper { + + private static final String TAG = "AS.NotificationHelper"; + + //--------------------------------------------------------------- + // audio device compatibility / enabled + + private final ArrayList<AudioDeviceAttributes> mCompatibleAudioDevices = new ArrayList<>(0); + private final ArrayList<AudioDeviceAttributes> mEnabledAudioDevices = new ArrayList<>(0); + + /** + * @return a shallow copy of the list of compatible audio devices + */ + synchronized @NonNull List<AudioDeviceAttributes> getCompatibleAudioDevices() { + return (List<AudioDeviceAttributes>) mCompatibleAudioDevices.clone(); + } + + synchronized void addCompatibleAudioDevice(@NonNull AudioDeviceAttributes ada) { + if (!mCompatibleAudioDevices.contains(ada)) { + mCompatibleAudioDevices.add(ada); + } + // by default, adding a compatible device enables it + if (!mEnabledAudioDevices.contains(ada)) { + mEnabledAudioDevices.add(ada); + } + } + + synchronized void removeCompatibleAudioDevice(@NonNull AudioDeviceAttributes ada) { + mCompatibleAudioDevices.remove(ada); + mEnabledAudioDevices.remove(ada); + } + + synchronized void setEnabledForDevice(boolean enabled, @NonNull AudioDeviceAttributes ada) { + if (enabled) { + if (!mEnabledAudioDevices.contains(ada)) { + mEnabledAudioDevices.add(ada); + } + } else { + mEnabledAudioDevices.remove(ada); + } + } + + //------------------------------------------------------ + // enabled state + + // global state of feature + boolean mFeatureEnabled = false; + + synchronized void setEnabled(boolean enabled) { + final boolean oldState = mFeatureEnabled; + mFeatureEnabled = enabled; + if (oldState != enabled) { + dispatchState(); + } + } + + final RemoteCallbackList<ISpatializerCallback> mStateCallbacks = + new RemoteCallbackList<ISpatializerCallback>(); + + synchronized void registerStateCallback( + @NonNull ISpatializerCallback callback) { + mStateCallbacks.register(callback); + } + + synchronized void unregisterStateCallback( + @NonNull ISpatializerCallback callback) { + mStateCallbacks.unregister(callback); + } + + private synchronized void dispatchState() { + // TODO check enabled state based on available devices + // (current implementation takes state as-is and dispatches it to listeners + final int nbCallbacks = mStateCallbacks.beginBroadcast(); + for (int i = 0; i < nbCallbacks; i++) { + try { + mStateCallbacks.getBroadcastItem(i) + .dispatchSpatializerStateChanged(mFeatureEnabled); + } catch (RemoteException e) { + Log.e(TAG, "Error in dispatchSpatializerStateChanged", e); + } + } + mStateCallbacks.finishBroadcast(); + } + + //------------------------------------------------------ + // virtualization capabilities + synchronized boolean canBeSpatialized( + @NonNull AudioAttributes attributes, @NonNull AudioFormat format) { + // TODO hook up to spatializer effect for query + return false; + } +} diff --git a/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java b/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java index e5e1385fa605..3eb6f4ae48a2 100644 --- a/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java +++ b/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java @@ -206,7 +206,6 @@ public abstract class BaseClientMonitor extends LoggableMonitor } mToken = null; } - mListener = null; } @Override diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java index b20316e4c6df..feb9e2a5b03f 100644 --- a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java +++ b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java @@ -295,6 +295,7 @@ public class BiometricScheduler { @Override public void onClientFinished(@NonNull BaseClientMonitor clientMonitor, boolean success) { mHandler.post(() -> { + clientMonitor.destroy(); if (mCurrentOperation == null) { Slog.e(getTag(), "[Finishing] " + clientMonitor + " but current operation is null, success: " + success diff --git a/services/core/java/com/android/server/broadcastradio/BroadcastRadioService.java b/services/core/java/com/android/server/broadcastradio/BroadcastRadioService.java index 54a4ad42fd99..23f0ffbbf0b8 100644 --- a/services/core/java/com/android/server/broadcastradio/BroadcastRadioService.java +++ b/services/core/java/com/android/server/broadcastradio/BroadcastRadioService.java @@ -52,11 +52,11 @@ public class BroadcastRadioService extends SystemService { public BroadcastRadioService(Context context) { super(context); - mHal1 = new com.android.server.broadcastradio.hal1.BroadcastRadioService(); + mHal1 = new com.android.server.broadcastradio.hal1.BroadcastRadioService(mLock); mV1Modules = mHal1.loadModules(); OptionalInt max = mV1Modules.stream().mapToInt(RadioManager.ModuleProperties::getId).max(); mHal2 = new com.android.server.broadcastradio.hal2.BroadcastRadioService( - max.isPresent() ? max.getAsInt() + 1 : 0); + max.isPresent() ? max.getAsInt() + 1 : 0, mLock); } @Override @@ -111,7 +111,7 @@ public class BroadcastRadioService extends SystemService { synchronized (mLock) { if (!mHal2.hasAnyModules()) { Slog.i(TAG, "There are no HAL 2.x modules registered"); - return new AnnouncementAggregator(listener); + return new AnnouncementAggregator(listener, mLock); } return mHal2.addAnnouncementListener(enabledTypes, listener); diff --git a/services/core/java/com/android/server/broadcastradio/OWNERS b/services/core/java/com/android/server/broadcastradio/OWNERS index ea4421eae96a..3e360e7e992c 100644 --- a/services/core/java/com/android/server/broadcastradio/OWNERS +++ b/services/core/java/com/android/server/broadcastradio/OWNERS @@ -1,2 +1,3 @@ +keunyoung@google.com +oscarazu@google.com twasilczyk@google.com -randolphs@google.com diff --git a/services/core/java/com/android/server/broadcastradio/hal1/BroadcastRadioService.java b/services/core/java/com/android/server/broadcastradio/hal1/BroadcastRadioService.java index e8ac5477469b..5da60328cd70 100644 --- a/services/core/java/com/android/server/broadcastradio/hal1/BroadcastRadioService.java +++ b/services/core/java/com/android/server/broadcastradio/hal1/BroadcastRadioService.java @@ -17,16 +17,9 @@ package com.android.server.broadcastradio.hal1; import android.annotation.NonNull; -import android.Manifest; -import android.content.Context; -import android.content.pm.PackageManager; -import android.hardware.radio.IRadioService; import android.hardware.radio.ITuner; import android.hardware.radio.ITunerCallback; import android.hardware.radio.RadioManager; -import android.os.ParcelableException; - -import com.android.server.SystemService; import java.util.List; import java.util.Objects; @@ -37,7 +30,7 @@ public class BroadcastRadioService { */ private final long mNativeContext = nativeInit(); - private final Object mLock = new Object(); + private final Object mLock; @Override protected void finalize() throws Throwable { @@ -51,6 +44,14 @@ public class BroadcastRadioService { private native Tuner nativeOpenTuner(long nativeContext, int moduleId, RadioManager.BandConfig config, boolean withAudio, ITunerCallback callback); + /** + * Constructor. should pass + * {@code com.android.server.broadcastradio.BroadcastRadioService#mLock} for lock. + */ + public BroadcastRadioService(@NonNull Object lock) { + mLock = lock; + } + public @NonNull List<RadioManager.ModuleProperties> loadModules() { synchronized (mLock) { return Objects.requireNonNull(nativeLoadModules(mNativeContext)); diff --git a/services/core/java/com/android/server/broadcastradio/hal2/AnnouncementAggregator.java b/services/core/java/com/android/server/broadcastradio/hal2/AnnouncementAggregator.java index 53076975849b..42e296f3e4ec 100644 --- a/services/core/java/com/android/server/broadcastradio/hal2/AnnouncementAggregator.java +++ b/services/core/java/com/android/server/broadcastradio/hal2/AnnouncementAggregator.java @@ -35,7 +35,7 @@ import java.util.Objects; public class AnnouncementAggregator extends ICloseHandle.Stub { private static final String TAG = "BcRadio2Srv.AnnAggr"; - private final Object mLock = new Object(); + private final Object mLock; @NonNull private final IAnnouncementListener mListener; private final IBinder.DeathRecipient mDeathRecipient = new DeathRecipient(); @@ -45,8 +45,9 @@ public class AnnouncementAggregator extends ICloseHandle.Stub { @GuardedBy("mLock") private boolean mIsClosed = false; - public AnnouncementAggregator(@NonNull IAnnouncementListener listener) { + public AnnouncementAggregator(@NonNull IAnnouncementListener listener, @NonNull Object lock) { mListener = Objects.requireNonNull(listener); + mLock = Objects.requireNonNull(lock); try { listener.asBinder().linkToDeath(mDeathRecipient, 0); } catch (RemoteException ex) { diff --git a/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java b/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java index 5e79c5943d7b..5c07f76e5011 100644 --- a/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java +++ b/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java @@ -42,7 +42,7 @@ import java.util.stream.Collectors; public class BroadcastRadioService { private static final String TAG = "BcRadio2Srv"; - private final Object mLock = new Object(); + private final Object mLock; @GuardedBy("mLock") private int mNextModuleId = 0; @@ -68,7 +68,7 @@ public class BroadcastRadioService { moduleId = mNextModuleId; } - RadioModule module = RadioModule.tryLoadingModule(moduleId, serviceName); + RadioModule module = RadioModule.tryLoadingModule(moduleId, serviceName, mLock); if (module == null) { return; } @@ -116,8 +116,9 @@ public class BroadcastRadioService { } }; - public BroadcastRadioService(int nextModuleId) { + public BroadcastRadioService(int nextModuleId, Object lock) { mNextModuleId = nextModuleId; + mLock = lock; try { IServiceManager manager = IServiceManager.getService(); if (manager == null) { @@ -174,7 +175,7 @@ public class BroadcastRadioService { public ICloseHandle addAnnouncementListener(@NonNull int[] enabledTypes, @NonNull IAnnouncementListener listener) { - AnnouncementAggregator aggregator = new AnnouncementAggregator(listener); + AnnouncementAggregator aggregator = new AnnouncementAggregator(listener, mLock); boolean anySupported = false; synchronized (mLock) { for (RadioModule module : mModules.values()) { diff --git a/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java b/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java index b7e188c73eab..ef7f4c9fc919 100644 --- a/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java +++ b/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java @@ -58,7 +58,7 @@ class RadioModule { @NonNull private final IBroadcastRadio mService; @NonNull public final RadioManager.ModuleProperties mProperties; - private final Object mLock = new Object(); + private final Object mLock; @NonNull private final Handler mHandler; @GuardedBy("mLock") @@ -132,13 +132,15 @@ class RadioModule { @VisibleForTesting RadioModule(@NonNull IBroadcastRadio service, - @NonNull RadioManager.ModuleProperties properties) { + @NonNull RadioManager.ModuleProperties properties, @NonNull Object lock) { mProperties = Objects.requireNonNull(properties); mService = Objects.requireNonNull(service); + mLock = Objects.requireNonNull(lock); mHandler = new Handler(Looper.getMainLooper()); } - public static @Nullable RadioModule tryLoadingModule(int idx, @NonNull String fqName) { + public static @Nullable RadioModule tryLoadingModule(int idx, @NonNull String fqName, + Object lock) { try { IBroadcastRadio service = IBroadcastRadio.getService(fqName); if (service == null) return null; @@ -156,7 +158,7 @@ class RadioModule { RadioManager.ModuleProperties prop = Convert.propertiesFromHal(idx, fqName, service.getProperties(), amfmConfig.value, dabConfig.value); - return new RadioModule(service, prop); + return new RadioModule(service, prop, lock); } catch (RemoteException ex) { Slog.e(TAG, "failed to load module " + fqName, ex); return null; @@ -178,7 +180,8 @@ class RadioModule { }); mHalTunerSession = Objects.requireNonNull(hwSession.value); } - TunerSession tunerSession = new TunerSession(this, mHalTunerSession, userCb); + TunerSession tunerSession = new TunerSession(this, mHalTunerSession, userCb, + mLock); mAidlTunerSessions.add(tunerSession); // Propagate state to new client. Note: These callbacks are invoked while holding mLock @@ -377,7 +380,7 @@ class RadioModule { } }; - synchronized (mService) { + synchronized (mLock) { mService.registerAnnouncementListener(enabledList, hwListener, (result, closeHnd) -> { halResult.value = result; hwCloseHandle.value = closeHnd; @@ -401,7 +404,7 @@ class RadioModule { if (id == 0) throw new IllegalArgumentException("Image ID is missing"); byte[] rawImage; - synchronized (mService) { + synchronized (mLock) { List<Byte> rawList = Utils.maybeRethrow(() -> mService.getImage(id)); rawImage = new byte[rawList.size()]; for (int i = 0; i < rawList.size(); i++) { diff --git a/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java b/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java index 7ab3bdd859e4..200af2fb1da7 100644 --- a/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java +++ b/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java @@ -40,7 +40,7 @@ class TunerSession extends ITuner.Stub { private static final String TAG = "BcRadio2Srv.session"; private static final String kAudioDeviceName = "Radio tuner source"; - private final Object mLock = new Object(); + private final Object mLock; private final RadioModule mModule; private final ITunerSession mHwSession; @@ -53,10 +53,12 @@ class TunerSession extends ITuner.Stub { private RadioManager.BandConfig mDummyConfig = null; TunerSession(@NonNull RadioModule module, @NonNull ITunerSession hwSession, - @NonNull android.hardware.radio.ITunerCallback callback) { + @NonNull android.hardware.radio.ITunerCallback callback, + @NonNull Object lock) { mModule = Objects.requireNonNull(module); mHwSession = Objects.requireNonNull(hwSession); mCallback = Objects.requireNonNull(callback); + mLock = Objects.requireNonNull(lock); } @Override diff --git a/services/core/java/com/android/server/devicestate/DeviceState.java b/services/core/java/com/android/server/devicestate/DeviceState.java index e693bcc93f8f..7fe24ff1f069 100644 --- a/services/core/java/com/android/server/devicestate/DeviceState.java +++ b/services/core/java/com/android/server/devicestate/DeviceState.java @@ -19,11 +19,14 @@ package com.android.server.devicestate; import static android.hardware.devicestate.DeviceStateManager.MAXIMUM_DEVICE_STATE; import static android.hardware.devicestate.DeviceStateManager.MINIMUM_DEVICE_STATE; +import android.annotation.IntDef; import android.annotation.IntRange; import android.annotation.NonNull; import com.android.internal.util.Preconditions; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.Objects; /** @@ -39,6 +42,19 @@ import java.util.Objects; * @see DeviceStateManagerService */ public final class DeviceState { + /** + * Flag that indicates sticky requests should be cancelled when this device state becomes the + * base device state. + */ + public static final int FLAG_CANCEL_STICKY_REQUESTS = 1 << 0; + + /** @hide */ + @IntDef(prefix = {"FLAG_"}, flag = true, value = { + FLAG_CANCEL_STICKY_REQUESTS, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface DeviceStateFlags {} + /** Unique identifier for the device state. */ @IntRange(from = MINIMUM_DEVICE_STATE, to = MAXIMUM_DEVICE_STATE) private final int mIdentifier; @@ -47,14 +63,19 @@ public final class DeviceState { @NonNull private final String mName; + @DeviceStateFlags + private final int mFlags; + public DeviceState( @IntRange(from = MINIMUM_DEVICE_STATE, to = MAXIMUM_DEVICE_STATE) int identifier, - @NonNull String name) { + @NonNull String name, + @DeviceStateFlags int flags) { Preconditions.checkArgumentInRange(identifier, MINIMUM_DEVICE_STATE, MAXIMUM_DEVICE_STATE, "identifier"); mIdentifier = identifier; mName = name; + mFlags = flags; } /** Returns the unique identifier for the device state. */ @@ -69,6 +90,11 @@ public final class DeviceState { return mName; } + @DeviceStateFlags + public int getFlags() { + return mFlags; + } + @Override public String toString() { return "DeviceState{" + "identifier=" + mIdentifier + ", name='" + mName + '\'' + '}'; @@ -80,11 +106,12 @@ public final class DeviceState { if (o == null || getClass() != o.getClass()) return false; DeviceState that = (DeviceState) o; return mIdentifier == that.mIdentifier - && Objects.equals(mName, that.mName); + && Objects.equals(mName, that.mName) + && mFlags == that.mFlags; } @Override public int hashCode() { - return Objects.hash(mIdentifier, mName); + return Objects.hash(mIdentifier, mName, mFlags); } } diff --git a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java index a8b0994402e8..383b3928e5e6 100644 --- a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java +++ b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java @@ -19,8 +19,13 @@ package com.android.server.devicestate; import static android.Manifest.permission.CONTROL_DEVICE_STATE; import static android.hardware.devicestate.DeviceStateManager.MAXIMUM_DEVICE_STATE; import static android.hardware.devicestate.DeviceStateManager.MINIMUM_DEVICE_STATE; -import static android.hardware.devicestate.DeviceStateRequest.FLAG_CANCEL_WHEN_BASE_CHANGES; +import static android.os.Process.THREAD_PRIORITY_DISPLAY; +import static com.android.server.devicestate.OverrideRequestController.STATUS_ACTIVE; +import static com.android.server.devicestate.OverrideRequestController.STATUS_CANCELED; +import static com.android.server.devicestate.OverrideRequestController.STATUS_SUSPENDED; + +import android.annotation.IntDef; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; @@ -30,12 +35,12 @@ import android.hardware.devicestate.DeviceStateManager; import android.hardware.devicestate.IDeviceStateManager; import android.hardware.devicestate.IDeviceStateManagerCallback; import android.os.Binder; +import android.os.Handler; +import android.os.HandlerThread; import android.os.IBinder; import android.os.RemoteException; import android.os.ResultReceiver; import android.os.ShellCallback; -import android.util.ArrayMap; -import android.util.ArraySet; import android.util.Slog; import android.util.SparseArray; @@ -43,14 +48,21 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.DumpUtils; import com.android.internal.util.FrameworkStatsLog; +import com.android.server.LocalServices; +import com.android.server.ServiceThread; import com.android.server.SystemService; import com.android.server.policy.DeviceStatePolicyImpl; +import com.android.server.wm.ActivityTaskManagerInternal; +import com.android.server.wm.WindowProcessController; import java.io.FileDescriptor; import java.io.PrintWriter; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Arrays; import java.util.Optional; +import java.util.WeakHashMap; /** * A system service that manages the state of a device with user-configurable hardware like a @@ -81,10 +93,20 @@ public final class DeviceStateManagerService extends SystemService { private static final boolean DEBUG = false; private final Object mLock = new Object(); + // Internal system service thread used to dispatch calls to the policy and to registered + // callbacks though its handler (mHandler). Provides a guarantee of callback order when + // leveraging mHandler and also enables posting messages with the service lock held. + private final HandlerThread mHandlerThread; + private final Handler mHandler; @NonNull private final DeviceStatePolicy mDeviceStatePolicy; @NonNull private final BinderService mBinderService; + @NonNull + private final OverrideRequestController mOverrideRequestController; + @VisibleForTesting + @NonNull + public ActivityTaskManagerInternal mActivityTaskManagerInternal; // All supported device states keyed by identifier. @GuardedBy("mLock") @@ -109,17 +131,16 @@ public final class DeviceStateManagerService extends SystemService { @NonNull private Optional<DeviceState> mBaseState = Optional.empty(); + // The current active override request. When set the device state specified here will take + // precedence over mBaseState. + @GuardedBy("mLock") + @NonNull + private Optional<OverrideRequest> mActiveOverride = Optional.empty(); + // List of processes registered to receive notifications about changes to device state and // request status indexed by process id. @GuardedBy("mLock") private final SparseArray<ProcessRecord> mProcessRecords = new SparseArray<>(); - // List of override requests with the highest precedence request at the end. - @GuardedBy("mLock") - private final ArrayList<OverrideRequestRecord> mRequestRecords = new ArrayList<>(); - // Set of override requests that are pending a call to notifyStatusIfNeeded() to be notified - // of a change in status. - @GuardedBy("mLock") - private final ArraySet<OverrideRequestRecord> mRequestsPendingStatusChange = new ArraySet<>(); public DeviceStateManagerService(@NonNull Context context) { this(context, new DeviceStatePolicyImpl(context)); @@ -128,9 +149,17 @@ public final class DeviceStateManagerService extends SystemService { @VisibleForTesting DeviceStateManagerService(@NonNull Context context, @NonNull DeviceStatePolicy policy) { super(context); + // Service thread assigned THREAD_PRIORITY_DISPLAY because this service indirectly drives + // display (on/off) and window (position) events through its callbacks. + mHandlerThread = new ServiceThread(TAG, THREAD_PRIORITY_DISPLAY, false /* allowIo */); + mHandlerThread.start(); + mHandler = mHandlerThread.getThreadHandler(); + mOverrideRequestController = new OverrideRequestController( + this::onOverrideRequestStatusChangedLocked); mDeviceStatePolicy = policy; mDeviceStatePolicy.getDeviceStateProvider().setListener(new DeviceStateProviderListener()); mBinderService = new BinderService(); + mActivityTaskManagerInternal = LocalServices.getService(ActivityTaskManagerInternal.class); } @Override @@ -138,6 +167,11 @@ public final class DeviceStateManagerService extends SystemService { publishBinderService(Context.DEVICE_STATE_SERVICE, mBinderService); } + @VisibleForTesting + Handler getHandler() { + return mHandler; + } + /** * Returns the current state the system is in. Note that the system may be in the process of * configuring a different state. @@ -191,12 +225,10 @@ public final class DeviceStateManagerService extends SystemService { @NonNull Optional<DeviceState> getOverrideState() { synchronized (mLock) { - if (mRequestRecords.isEmpty()) { - return Optional.empty(); + if (mActiveOverride.isPresent()) { + return getStateLocked(mActiveOverride.get().getRequestedState()); } - - OverrideRequestRecord topRequest = mRequestRecords.get(mRequestRecords.size() - 1); - return Optional.of(topRequest.mRequestedState); + return Optional.empty(); } } @@ -247,43 +279,41 @@ public final class DeviceStateManagerService extends SystemService { } private void updateSupportedStates(DeviceState[] supportedDeviceStates) { - boolean updatedPendingState; - boolean hasBaseState; synchronized (mLock) { final int[] oldStateIdentifiers = getSupportedStateIdentifiersLocked(); + // Whether or not at least one device state has the flag FLAG_CANCEL_STICKY_REQUESTS + // set. If set to true, the OverrideRequestController will be configured to allow sticky + // requests. + boolean hasTerminalDeviceState = false; mDeviceStates.clear(); for (int i = 0; i < supportedDeviceStates.length; i++) { DeviceState state = supportedDeviceStates[i]; + if ((state.getFlags() & DeviceState.FLAG_CANCEL_STICKY_REQUESTS) != 0) { + hasTerminalDeviceState = true; + } mDeviceStates.put(state.getIdentifier(), state); } + mOverrideRequestController.setStickyRequestsAllowed(hasTerminalDeviceState); + final int[] newStateIdentifiers = getSupportedStateIdentifiersLocked(); if (Arrays.equals(oldStateIdentifiers, newStateIdentifiers)) { return; } - final int requestSize = mRequestRecords.size(); - for (int i = 0; i < requestSize; i++) { - OverrideRequestRecord request = mRequestRecords.get(i); - if (!isSupportedStateLocked(request.mRequestedState.getIdentifier())) { - request.setStatusLocked(OverrideRequestRecord.STATUS_CANCELED); - } - } + mOverrideRequestController.handleNewSupportedStates(newStateIdentifiers); + updatePendingStateLocked(); - updatedPendingState = updatePendingStateLocked(); - hasBaseState = mBaseState.isPresent(); - } + if (!mPendingState.isPresent()) { + // If the change in the supported states didn't result in a change of the pending + // state commitPendingState() will never be called and the callbacks will never be + // notified of the change. + notifyDeviceStateInfoChangedAsync(); + } - if (hasBaseState && !updatedPendingState) { - // If the change in the supported states didn't result in a change of the pending state - // commitPendingState() will never be called and the callbacks will never be notified - // of the change. - notifyDeviceStateInfoChanged(); + mHandler.post(this::notifyPolicyIfNeeded); } - - notifyRequestsOfStatusChangeIfNeeded(); - notifyPolicyIfNeeded(); } /** @@ -311,7 +341,6 @@ public final class DeviceStateManagerService extends SystemService { * @see #isSupportedStateLocked(int) */ private void setBaseState(int identifier) { - boolean updatedPendingState; synchronized (mLock) { final Optional<DeviceState> baseStateOptional = getStateLocked(identifier); if (!baseStateOptional.isPresent()) { @@ -325,26 +354,21 @@ public final class DeviceStateManagerService extends SystemService { } mBaseState = Optional.of(baseState); - final int requestSize = mRequestRecords.size(); - for (int i = 0; i < requestSize; i++) { - OverrideRequestRecord request = mRequestRecords.get(i); - if ((request.mFlags & FLAG_CANCEL_WHEN_BASE_CHANGES) > 0) { - request.setStatusLocked(OverrideRequestRecord.STATUS_CANCELED); - } + if ((baseState.getFlags() & DeviceState.FLAG_CANCEL_STICKY_REQUESTS) != 0) { + mOverrideRequestController.cancelStickyRequests(); } + mOverrideRequestController.handleBaseStateChanged(); + updatePendingStateLocked(); - updatedPendingState = updatePendingStateLocked(); - } + if (!mPendingState.isPresent()) { + // If the change in base state didn't result in a change of the pending state + // commitPendingState() will never be called and the callbacks will never be + // notified of the change. + notifyDeviceStateInfoChangedAsync(); + } - if (!updatedPendingState) { - // If the change in base state didn't result in a change of the pending state - // commitPendingState() will never be called and the callbacks will never be notified - // of the change. - notifyDeviceStateInfoChanged(); + mHandler.post(this::notifyPolicyIfNeeded); } - - notifyRequestsOfStatusChangeIfNeeded(); - notifyPolicyIfNeeded(); } /** @@ -362,8 +386,8 @@ public final class DeviceStateManagerService extends SystemService { } final DeviceState stateToConfigure; - if (!mRequestRecords.isEmpty()) { - stateToConfigure = mRequestRecords.get(mRequestRecords.size() - 1).mRequestedState; + if (mActiveOverride.isPresent()) { + stateToConfigure = getStateLocked(mActiveOverride.get().getRequestedState()).get(); } else if (mBaseState.isPresent() && isSupportedStateLocked(mBaseState.get().getIdentifier())) { // Base state could have recently become unsupported after a change in supported states. @@ -429,108 +453,106 @@ public final class DeviceStateManagerService extends SystemService { * </p> */ private void commitPendingState() { - // Update the current state. synchronized (mLock) { final DeviceState newState = mPendingState.get(); if (DEBUG) { Slog.d(TAG, "Committing state: " + newState); } - if (!mRequestRecords.isEmpty()) { - final OverrideRequestRecord topRequest = - mRequestRecords.get(mRequestRecords.size() - 1); - if (topRequest.mRequestedState.getIdentifier() == newState.getIdentifier()) { - // The top request could have come in while the service was awaiting callback - // from the policy. In that case we only set it to active if it matches the - // current committed state, otherwise it will be set to active when its - // requested state is committed. - topRequest.setStatusLocked(OverrideRequestRecord.STATUS_ACTIVE); - } - } - FrameworkStatsLog.write(FrameworkStatsLog.DEVICE_STATE_CHANGED, newState.getIdentifier(), !mCommittedState.isPresent()); mCommittedState = Optional.of(newState); mPendingState = Optional.empty(); updatePendingStateLocked(); - } - - // Notify callbacks of a change. - notifyDeviceStateInfoChanged(); - // Notify the top request that it's active. - notifyRequestsOfStatusChangeIfNeeded(); - - // Try to configure the next state if needed. - notifyPolicyIfNeeded(); - } + // Notify callbacks of a change. + notifyDeviceStateInfoChangedAsync(); + + // The top request could have come in while the service was awaiting callback + // from the policy. In that case we only set it to active if it matches the + // current committed state, otherwise it will be set to active when its + // requested state is committed. + OverrideRequest activeRequest = mActiveOverride.orElse(null); + if (activeRequest != null + && activeRequest.getRequestedState() == newState.getIdentifier()) { + ProcessRecord processRecord = mProcessRecords.get(activeRequest.getPid()); + if (processRecord != null) { + processRecord.notifyRequestActiveAsync(activeRequest.getToken()); + } + } - private void notifyDeviceStateInfoChanged() { - if (Thread.holdsLock(mLock)) { - throw new IllegalStateException( - "Attempting to notify callbacks with service lock held."); + // Try to configure the next state if needed. + mHandler.post(this::notifyPolicyIfNeeded); } + } - // Grab the lock and copy the process records and the current info. - ArrayList<ProcessRecord> registeredProcesses; - DeviceStateInfo info; + private void notifyDeviceStateInfoChangedAsync() { synchronized (mLock) { if (mProcessRecords.size() == 0) { return; } - registeredProcesses = new ArrayList<>(); + ArrayList<ProcessRecord> registeredProcesses = new ArrayList<>(); for (int i = 0; i < mProcessRecords.size(); i++) { registeredProcesses.add(mProcessRecords.valueAt(i)); } - info = getDeviceStateInfoLocked(); - } + DeviceStateInfo info = getDeviceStateInfoLocked(); - // After releasing the lock, send the notifications out. - for (int i = 0; i < registeredProcesses.size(); i++) { - registeredProcesses.get(i).notifyDeviceStateInfoAsync(info); + for (int i = 0; i < registeredProcesses.size(); i++) { + registeredProcesses.get(i).notifyDeviceStateInfoAsync(info); + } } } - /** - * Notifies all dirty requests (requests that have a change in status, but have not yet been - * notified) that their status has changed. - */ - private void notifyRequestsOfStatusChangeIfNeeded() { - if (Thread.holdsLock(mLock)) { - throw new IllegalStateException( - "Attempting to notify requests with service lock held."); + private void onOverrideRequestStatusChangedLocked(@NonNull OverrideRequest request, + @OverrideRequestController.RequestStatus int status) { + if (status == STATUS_ACTIVE) { + mActiveOverride = Optional.of(request); + } else if (status == STATUS_SUSPENDED || status == STATUS_CANCELED) { + if (mActiveOverride.isPresent() && mActiveOverride.get() == request) { + mActiveOverride = Optional.empty(); + } + } else { + throw new IllegalArgumentException("Unknown request status: " + status); } - ArraySet<OverrideRequestRecord> dirtyRequests; - synchronized (mLock) { - if (mRequestsPendingStatusChange.isEmpty()) { - return; - } + boolean updatedPendingState = updatePendingStateLocked(); - dirtyRequests = new ArraySet<>(mRequestsPendingStatusChange); - mRequestsPendingStatusChange.clear(); + ProcessRecord processRecord = mProcessRecords.get(request.getPid()); + if (processRecord == null) { + // If the process is no longer registered with the service, for example if it has died, + // there is no need to notify it of a change in request status. + mHandler.post(this::notifyPolicyIfNeeded); + return; } - // After releasing the lock, send the notifications out. - for (int i = 0; i < dirtyRequests.size(); i++) { - dirtyRequests.valueAt(i).notifyStatusIfNeeded(); + if (status == STATUS_ACTIVE) { + if (!updatedPendingState && !mPendingState.isPresent()) { + // If the pending state was not updated and there is not currently a pending state + // then this newly active request will never be notified of a change in state. + // Schedule the notification now. + processRecord.notifyRequestActiveAsync(request.getToken()); + } + } else if (status == STATUS_SUSPENDED) { + processRecord.notifyRequestSuspendedAsync(request.getToken()); + } else { + processRecord.notifyRequestCanceledAsync(request.getToken()); } + + mHandler.post(this::notifyPolicyIfNeeded); } private void registerProcess(int pid, IDeviceStateManagerCallback callback) { - DeviceStateInfo currentInfo; - ProcessRecord record; - // Grab the lock to register the callback and get the current state. synchronized (mLock) { if (mProcessRecords.contains(pid)) { throw new SecurityException("The calling process has already registered an" + " IDeviceStateManagerCallback."); } - record = new ProcessRecord(callback, pid); + ProcessRecord record = new ProcessRecord(callback, pid, this::handleProcessDied, + mHandlerThread.getThreadHandler()); try { callback.asBinder().linkToDeath(record, 0); } catch (RemoteException ex) { @@ -538,34 +560,21 @@ public final class DeviceStateManagerService extends SystemService { } mProcessRecords.put(pid, record); - currentInfo = mCommittedState.isPresent() ? getDeviceStateInfoLocked() : null; - } - - if (currentInfo != null) { - // If there is not a committed state we'll wait to notify the process of the initial - // value. - record.notifyDeviceStateInfoAsync(currentInfo); + DeviceStateInfo currentInfo = mCommittedState.isPresent() + ? getDeviceStateInfoLocked() : null; + if (currentInfo != null) { + // If there is not a committed state we'll wait to notify the process of the initial + // value. + record.notifyDeviceStateInfoAsync(currentInfo); + } } } private void handleProcessDied(ProcessRecord processRecord) { synchronized (mLock) { - // Cancel all requests from this process. - final int requestCount = processRecord.mRequestRecords.size(); - for (int i = 0; i < requestCount; i++) { - final OverrideRequestRecord request = processRecord.mRequestRecords.valueAt(i); - // Cancel the request but don't mark it as dirty since there's no need to send - // notifications if the process has died. - request.setStatusLocked(OverrideRequestRecord.STATUS_CANCELED, - false /* markDirty */); - } - mProcessRecords.remove(processRecord.mPid); - - updatePendingStateLocked(); + mOverrideRequestController.handleProcessDied(processRecord.mPid); } - - notifyPolicyIfNeeded(); } private void requestStateInternal(int state, int flags, int callingPid, @@ -577,7 +586,7 @@ public final class DeviceStateManagerService extends SystemService { + " has no registered callback."); } - if (processRecord.mRequestRecords.get(token) != null) { + if (mOverrideRequestController.hasRequest(token)) { throw new IllegalStateException("Request has already been made for the supplied" + " token: " + token); } @@ -588,27 +597,9 @@ public final class DeviceStateManagerService extends SystemService { + " is not supported."); } - OverrideRequestRecord topRecord = mRequestRecords.isEmpty() - ? null : mRequestRecords.get(mRequestRecords.size() - 1); - if (topRecord != null) { - topRecord.setStatusLocked(OverrideRequestRecord.STATUS_SUSPENDED); - } - - final OverrideRequestRecord request = - new OverrideRequestRecord(processRecord, token, deviceState.get(), flags); - mRequestRecords.add(request); - processRecord.mRequestRecords.put(request.mToken, request); - - final boolean updatedPendingState = updatePendingStateLocked(); - if (!updatedPendingState && !mPendingState.isPresent()) { - // We don't set the status of the new request to ACTIVE if the request updated the - // pending state as it will be set in commitPendingState(). - request.setStatusLocked(OverrideRequestRecord.STATUS_ACTIVE, true /* markDirty */); - } + OverrideRequest request = new OverrideRequest(token, callingPid, state, flags); + mOverrideRequestController.addRequest(request); } - - notifyRequestsOfStatusChangeIfNeeded(); - notifyPolicyIfNeeded(); } private void cancelRequestInternal(int callingPid, @NonNull IBinder token) { @@ -619,18 +610,8 @@ public final class DeviceStateManagerService extends SystemService { + " has no registered callback."); } - OverrideRequestRecord request = processRecord.mRequestRecords.get(token); - if (request == null) { - throw new IllegalStateException("No known request for the given token"); - } - - request.setStatusLocked(OverrideRequestRecord.STATUS_CANCELED); - - updatePendingStateLocked(); + mOverrideRequestController.cancelRequest(token); } - - notifyRequestsOfStatusChangeIfNeeded(); - notifyPolicyIfNeeded(); } private void dumpInternal(PrintWriter pw) { @@ -650,16 +631,7 @@ public final class DeviceStateManagerService extends SystemService { pw.println(" " + i + ": mPid=" + processRecord.mPid); } - final int requestCount = mRequestRecords.size(); - pw.println(); - pw.println("Override requests: size=" + requestCount); - for (int i = 0; i < requestCount; i++) { - OverrideRequestRecord requestRecord = mRequestRecords.get(i); - pw.println(" " + i + ": mPid=" + requestRecord.mProcessRecord.mPid - + ", mRequestedState=" + requestRecord.mRequestedState - + ", mFlags=" + requestRecord.mFlags - + ", mStatus=" + requestRecord.statusToString(requestRecord.mStatus)); - } + mOverrideRequestController.dumpInternal(pw); } } @@ -683,142 +655,107 @@ public final class DeviceStateManagerService extends SystemService { } } - private final class ProcessRecord implements IBinder.DeathRecipient { + private static final class ProcessRecord implements IBinder.DeathRecipient { + public interface DeathListener { + void onProcessDied(ProcessRecord record); + } + + private static final int STATUS_ACTIVE = 0; + + private static final int STATUS_SUSPENDED = 1; + + private static final int STATUS_CANCELED = 2; + + @IntDef(prefix = {"STATUS_"}, value = { + STATUS_ACTIVE, + STATUS_SUSPENDED, + STATUS_CANCELED + }) + @Retention(RetentionPolicy.SOURCE) + private @interface RequestStatus {} + private final IDeviceStateManagerCallback mCallback; private final int mPid; + private final DeathListener mDeathListener; + private final Handler mHandler; - private final ArrayMap<IBinder, OverrideRequestRecord> mRequestRecords = new ArrayMap<>(); + private final WeakHashMap<IBinder, Integer> mLastNotifiedStatus = new WeakHashMap<>(); - ProcessRecord(IDeviceStateManagerCallback callback, int pid) { + ProcessRecord(IDeviceStateManagerCallback callback, int pid, DeathListener deathListener, + Handler handler) { mCallback = callback; mPid = pid; + mDeathListener = deathListener; + mHandler = handler; } @Override public void binderDied() { - handleProcessDied(this); + mDeathListener.onProcessDied(this); } public void notifyDeviceStateInfoAsync(@NonNull DeviceStateInfo info) { - try { - mCallback.onDeviceStateInfoChanged(info); - } catch (RemoteException ex) { - Slog.w(TAG, "Failed to notify process " + mPid + " that device state changed.", - ex); - } + mHandler.post(() -> { + try { + mCallback.onDeviceStateInfoChanged(info); + } catch (RemoteException ex) { + Slog.w(TAG, "Failed to notify process " + mPid + " that device state changed.", + ex); + } + }); } - public void notifyRequestActiveAsync(OverrideRequestRecord request) { - try { - mCallback.onRequestActive(request.mToken); - } catch (RemoteException ex) { - Slog.w(TAG, "Failed to notify process " + mPid + " that request state changed.", - ex); + public void notifyRequestActiveAsync(IBinder token) { + @RequestStatus Integer lastStatus = mLastNotifiedStatus.get(token); + if (lastStatus != null + && (lastStatus == STATUS_ACTIVE || lastStatus == STATUS_CANCELED)) { + return; } - } - public void notifyRequestSuspendedAsync(OverrideRequestRecord request) { - try { - mCallback.onRequestSuspended(request.mToken); - } catch (RemoteException ex) { - Slog.w(TAG, "Failed to notify process " + mPid + " that request state changed.", - ex); - } + mLastNotifiedStatus.put(token, STATUS_ACTIVE); + mHandler.post(() -> { + try { + mCallback.onRequestActive(token); + } catch (RemoteException ex) { + Slog.w(TAG, "Failed to notify process " + mPid + " that request state changed.", + ex); + } + }); } - public void notifyRequestCanceledAsync(OverrideRequestRecord request) { - try { - mCallback.onRequestCanceled(request.mToken); - } catch (RemoteException ex) { - Slog.w(TAG, "Failed to notify process " + mPid + " that request state changed.", - ex); + public void notifyRequestSuspendedAsync(IBinder token) { + @RequestStatus Integer lastStatus = mLastNotifiedStatus.get(token); + if (lastStatus != null + && (lastStatus == STATUS_SUSPENDED || lastStatus == STATUS_CANCELED)) { + return; } - } - } - - /** A record describing a request to override the state of the device. */ - private final class OverrideRequestRecord { - public static final int STATUS_UNKNOWN = 0; - public static final int STATUS_ACTIVE = 1; - public static final int STATUS_SUSPENDED = 2; - public static final int STATUS_CANCELED = 3; - - @Nullable - public String statusToString(int status) { - switch (status) { - case STATUS_ACTIVE: - return "ACTIVE"; - case STATUS_SUSPENDED: - return "SUSPENDED"; - case STATUS_CANCELED: - return "CANCELED"; - case STATUS_UNKNOWN: - return "UNKNOWN"; - default: - return null; - } - } - - private final ProcessRecord mProcessRecord; - @NonNull - private final IBinder mToken; - @NonNull - private final DeviceState mRequestedState; - private final int mFlags; - - private int mStatus = STATUS_UNKNOWN; - private int mLastNotifiedStatus = STATUS_UNKNOWN; - - OverrideRequestRecord(@NonNull ProcessRecord processRecord, @NonNull IBinder token, - @NonNull DeviceState requestedState, int flags) { - mProcessRecord = processRecord; - mToken = token; - mRequestedState = requestedState; - mFlags = flags; - } - - public void setStatusLocked(int status) { - setStatusLocked(status, true /* markDirty */); - } - - public void setStatusLocked(int status, boolean markDirty) { - if (mStatus != status) { - if (mStatus == STATUS_CANCELED) { - throw new IllegalStateException( - "Can not alter the status of a request after set to CANCELED."); - } - - mStatus = status; - - if (mStatus == STATUS_CANCELED) { - mRequestRecords.remove(this); - mProcessRecord.mRequestRecords.remove(mToken); - } - if (markDirty) { - mRequestsPendingStatusChange.add(this); + mLastNotifiedStatus.put(token, STATUS_SUSPENDED); + mHandler.post(() -> { + try { + mCallback.onRequestSuspended(token); + } catch (RemoteException ex) { + Slog.w(TAG, "Failed to notify process " + mPid + " that request state changed.", + ex); } - } + }); } - public void notifyStatusIfNeeded() { - int stateToReport; - synchronized (mLock) { - if (mLastNotifiedStatus == mStatus) { - return; - } - - stateToReport = mStatus; - mLastNotifiedStatus = mStatus; + public void notifyRequestCanceledAsync(IBinder token) { + @RequestStatus Integer lastStatus = mLastNotifiedStatus.get(token); + if (lastStatus != null && lastStatus == STATUS_CANCELED) { + return; } - if (stateToReport == STATUS_ACTIVE) { - mProcessRecord.notifyRequestActiveAsync(this); - } else if (stateToReport == STATUS_SUSPENDED) { - mProcessRecord.notifyRequestSuspendedAsync(this); - } else if (stateToReport == STATUS_CANCELED) { - mProcessRecord.notifyRequestCanceledAsync(this); - } + mLastNotifiedStatus.put(token, STATUS_CANCELED); + mHandler.post(() -> { + try { + mCallback.onRequestCanceled(token); + } catch (RemoteException ex) { + Slog.w(TAG, "Failed to notify process " + mPid + " that request state changed.", + ex); + } + }); } } @@ -848,14 +785,21 @@ public final class DeviceStateManagerService extends SystemService { @Override // Binder call public void requestState(IBinder token, int state, int flags) { - getContext().enforceCallingOrSelfPermission(CONTROL_DEVICE_STATE, - "Permission required to request device state."); + final int callingPid = Binder.getCallingPid(); + // Allow top processes to request a device state change + // If the calling process ID is not the top app, then we check if this process + // holds a permission to CONTROL_DEVICE_STATE + final WindowProcessController topApp = mActivityTaskManagerInternal.getTopApp(); + if (topApp.getPid() != callingPid) { + getContext().enforceCallingOrSelfPermission(CONTROL_DEVICE_STATE, + "Permission required to request device state, " + + "or the call must come from the top focused app."); + } if (token == null) { throw new IllegalArgumentException("Request token must not be null."); } - final int callingPid = Binder.getCallingPid(); final long callingIdentity = Binder.clearCallingIdentity(); try { requestStateInternal(state, flags, callingPid, token); @@ -866,14 +810,21 @@ public final class DeviceStateManagerService extends SystemService { @Override // Binder call public void cancelRequest(IBinder token) { - getContext().enforceCallingOrSelfPermission(CONTROL_DEVICE_STATE, - "Permission required to clear requested device state."); + final int callingPid = Binder.getCallingPid(); + // Allow top processes to cancel a device state change + // If the calling process ID is not the top app, then we check if this process + // holds a permission to CONTROL_DEVICE_STATE + final WindowProcessController topApp = mActivityTaskManagerInternal.getTopApp(); + if (topApp.getPid() != callingPid) { + getContext().enforceCallingOrSelfPermission(CONTROL_DEVICE_STATE, + "Permission required to cancel device state, " + + "or the call must come from the top focused app."); + } if (token == null) { throw new IllegalArgumentException("Request token must not be null."); } - final int callingPid = Binder.getCallingPid(); final long callingIdentity = Binder.clearCallingIdentity(); try { cancelRequestInternal(callingPid, token); diff --git a/services/core/java/com/android/server/devicestate/OverrideRequest.java b/services/core/java/com/android/server/devicestate/OverrideRequest.java new file mode 100644 index 000000000000..35a4c844c710 --- /dev/null +++ b/services/core/java/com/android/server/devicestate/OverrideRequest.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.devicestate; + +import android.hardware.devicestate.DeviceStateRequest; +import android.os.IBinder; + +/** + * A request to override the state managed by {@link DeviceStateManagerService}. + * + * @see OverrideRequestController + */ +final class OverrideRequest { + private final IBinder mToken; + private final int mPid; + private final int mRequestedState; + @DeviceStateRequest.RequestFlags + private final int mFlags; + + OverrideRequest(IBinder token, int pid, int requestedState, + @DeviceStateRequest.RequestFlags int flags) { + mToken = token; + mPid = pid; + mRequestedState = requestedState; + mFlags = flags; + } + + IBinder getToken() { + return mToken; + } + + int getPid() { + return mPid; + } + + int getRequestedState() { + return mRequestedState; + } + + @DeviceStateRequest.RequestFlags + int getFlags() { + return mFlags; + } +} diff --git a/services/core/java/com/android/server/devicestate/OverrideRequestController.java b/services/core/java/com/android/server/devicestate/OverrideRequestController.java new file mode 100644 index 000000000000..05c9eb2c5bbe --- /dev/null +++ b/services/core/java/com/android/server/devicestate/OverrideRequestController.java @@ -0,0 +1,322 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.devicestate; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.hardware.devicestate.DeviceStateRequest; +import android.os.IBinder; + +import java.io.PrintWriter; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; +import java.util.List; + +/** + * Manages the lifecycle of override requests. + * <p> + * New requests are added with {@link #addRequest(OverrideRequest)} and are kept active until + * either: + * <ul> + * <li>A new request is added with {@link #addRequest(OverrideRequest)}, in which case the + * request will become suspended.</li> + * <li>The request is cancelled with {@link #cancelRequest(IBinder)} or as a side effect + * of other methods calls, such as {@link #handleProcessDied(int)}.</li> + * </ul> + */ +final class OverrideRequestController { + static final int STATUS_UNKNOWN = 0; + /** + * The request is the top-most request. + */ + static final int STATUS_ACTIVE = 1; + /** + * The request is still present but is being superseded by another request. + */ + static final int STATUS_SUSPENDED = 2; + /** + * The request is not longer valid. + */ + static final int STATUS_CANCELED = 3; + + @IntDef(prefix = {"STATUS_"}, value = { + STATUS_UNKNOWN, + STATUS_ACTIVE, + STATUS_SUSPENDED, + STATUS_CANCELED + }) + @Retention(RetentionPolicy.SOURCE) + @interface RequestStatus {} + + static String statusToString(@RequestStatus int status) { + switch (status) { + case STATUS_ACTIVE: + return "ACTIVE"; + case STATUS_SUSPENDED: + return "SUSPENDED"; + case STATUS_CANCELED: + return "CANCELED"; + case STATUS_UNKNOWN: + return "UNKNOWN"; + } + throw new IllegalArgumentException("Unknown status: " + status); + } + + private final StatusChangeListener mListener; + private final List<OverrideRequest> mTmpRequestsToCancel = new ArrayList<>(); + + // List of override requests with the most recent override request at the end. + private final ArrayList<OverrideRequest> mRequests = new ArrayList<>(); + + private boolean mStickyRequestsAllowed; + // List of override requests that have outlived their process and will only be cancelled through + // a call to cancelStickyRequests(). + private final ArrayList<OverrideRequest> mStickyRequests = new ArrayList<>(); + + OverrideRequestController(@NonNull StatusChangeListener listener) { + mListener = listener; + } + + /** + * Sets sticky requests as either allowed or disallowed. When sticky requests are allowed a call + * to {@link #handleProcessDied(int)} will not result in the request being cancelled + * immediately. Instead, the request will be marked sticky and must be cancelled with a call + * to {@link #cancelStickyRequests()}. + */ + void setStickyRequestsAllowed(boolean stickyRequestsAllowed) { + mStickyRequestsAllowed = stickyRequestsAllowed; + if (!mStickyRequestsAllowed) { + cancelStickyRequests(); + } + } + + /** + * Adds a request to the top of the stack and notifies the listener of all changes to request + * status as a result of this operation. + */ + void addRequest(@NonNull OverrideRequest request) { + mRequests.add(request); + mListener.onStatusChanged(request, STATUS_ACTIVE); + + if (mRequests.size() > 1) { + OverrideRequest prevRequest = mRequests.get(mRequests.size() - 2); + mListener.onStatusChanged(prevRequest, STATUS_SUSPENDED); + } + } + + /** + * Cancels the request with the specified {@code token} and notifies the listener of all changes + * to request status as a result of this operation. + */ + void cancelRequest(@NonNull IBinder token) { + int index = getRequestIndex(token); + if (index == -1) { + return; + } + + OverrideRequest request = mRequests.remove(index); + if (index == mRequests.size() && mRequests.size() > 0) { + // We removed the current active request so we need to set the new active request + // before cancelling this request. + OverrideRequest newTop = getLast(mRequests); + mListener.onStatusChanged(newTop, STATUS_ACTIVE); + } + mListener.onStatusChanged(request, STATUS_CANCELED); + } + + /** + * Cancels all requests that are currently marked sticky and notifies the listener of all + * changes to request status as a result of this operation. + * + * @see #setStickyRequestsAllowed(boolean) + */ + void cancelStickyRequests() { + mTmpRequestsToCancel.clear(); + mTmpRequestsToCancel.addAll(mStickyRequests); + cancelRequestsLocked(mTmpRequestsToCancel); + } + + /** + * Returns {@code true} if this controller is current managing a request with the specified + * {@code token}, {@code false} otherwise. + */ + boolean hasRequest(@NonNull IBinder token) { + return getRequestIndex(token) != -1; + } + + /** + * Notifies the controller that the process with the specified {@code pid} has died. The + * controller will notify the listener of all changes to request status as a result of this + * operation. + */ + void handleProcessDied(int pid) { + if (mRequests.isEmpty()) { + return; + } + + mTmpRequestsToCancel.clear(); + OverrideRequest prevActiveRequest = getLast(mRequests); + for (OverrideRequest request : mRequests) { + if (request.getPid() == pid) { + mTmpRequestsToCancel.add(request); + } + } + + if (mStickyRequestsAllowed) { + // Do not cancel the requests now because sticky requests are allowed. These + // requests will be cancelled on a call to cancelStickyRequests(). + mStickyRequests.addAll(mTmpRequestsToCancel); + return; + } + + cancelRequestsLocked(mTmpRequestsToCancel); + } + + /** + * Notifies the controller that the base state has changed. The controller will notify the + * listener of all changes to request status as a result of this change. + * + * @return {@code true} if calling this method has lead to a new active request, {@code false} + * otherwise. + */ + boolean handleBaseStateChanged() { + if (mRequests.isEmpty()) { + return false; + } + + mTmpRequestsToCancel.clear(); + OverrideRequest prevActiveRequest = getLast(mRequests); + for (int i = 0; i < mRequests.size(); i++) { + OverrideRequest request = mRequests.get(i); + if ((request.getFlags() & DeviceStateRequest.FLAG_CANCEL_WHEN_BASE_CHANGES) != 0) { + mTmpRequestsToCancel.add(request); + } + } + + final boolean newActiveRequest = cancelRequestsLocked(mTmpRequestsToCancel); + return newActiveRequest; + } + + /** + * Notifies the controller that the set of supported states has changed. The controller will + * notify the listener of all changes to request status as a result of this change. + * + * @return {@code true} if calling this method has lead to a new active request, {@code false} + * otherwise. + */ + boolean handleNewSupportedStates(int[] newSupportedStates) { + if (mRequests.isEmpty()) { + return false; + } + + mTmpRequestsToCancel.clear(); + for (int i = 0; i < mRequests.size(); i++) { + OverrideRequest request = mRequests.get(i); + if (!contains(newSupportedStates, request.getRequestedState())) { + mTmpRequestsToCancel.add(request); + } + } + + final boolean newActiveRequest = cancelRequestsLocked(mTmpRequestsToCancel); + return newActiveRequest; + } + + void dumpInternal(PrintWriter pw) { + final int requestCount = mRequests.size(); + pw.println(); + pw.println("Override requests: size=" + requestCount); + for (int i = 0; i < requestCount; i++) { + OverrideRequest overrideRequest = mRequests.get(i); + int status = (i == requestCount - 1) ? STATUS_ACTIVE : STATUS_SUSPENDED; + pw.println(" " + i + ": mPid=" + overrideRequest.getPid() + + ", mRequestedState=" + overrideRequest.getRequestedState() + + ", mFlags=" + overrideRequest.getFlags() + + ", mStatus=" + statusToString(status)); + } + } + + /** + * Handles cancelling a set of requests. If the set of requests to cancel will lead to a new + * request becoming active this request will also be notified of its change in state. + * + * @return {@code true} if calling this method has lead to a new active request, {@code false} + * otherwise. + */ + private boolean cancelRequestsLocked(List<OverrideRequest> requestsToCancel) { + if (requestsToCancel.isEmpty()) { + return false; + } + + OverrideRequest prevActiveRequest = getLast(mRequests); + boolean causedNewRequestToBecomeActive = false; + mRequests.removeAll(requestsToCancel); + mStickyRequests.removeAll(requestsToCancel); + if (!mRequests.isEmpty()) { + OverrideRequest newActiveRequest = getLast(mRequests); + if (newActiveRequest != prevActiveRequest) { + mListener.onStatusChanged(newActiveRequest, STATUS_ACTIVE); + causedNewRequestToBecomeActive = true; + } + } + + for (int i = 0; i < requestsToCancel.size(); i++) { + mListener.onStatusChanged(requestsToCancel.get(i), STATUS_CANCELED); + } + return causedNewRequestToBecomeActive; + } + + private int getRequestIndex(@NonNull IBinder token) { + final int numberOfRequests = mRequests.size(); + if (numberOfRequests == 0) { + return -1; + } + + for (int i = 0; i < numberOfRequests; i++) { + OverrideRequest request = mRequests.get(i); + if (request.getToken() == token) { + return i; + } + } + return -1; + } + + @Nullable + private static <T> T getLast(List<T> list) { + return list.size() > 0 ? list.get(list.size() - 1) : null; + } + + private static boolean contains(int[] array, int value) { + for (int i = 0; i < array.length; i++) { + if (array[i] == value) { + return true; + } + } + return false; + } + + public interface StatusChangeListener { + /** + * Notifies the listener of a change in request status. If a change within the controller + * causes one request to become active and one to become either suspended or cancelled, this + * method is guaranteed to be called with the active request first before the suspended or + * cancelled request. + */ + void onStatusChanged(@NonNull OverrideRequest request, @RequestStatus int newStatus); + } +} diff --git a/services/core/java/com/android/server/display/DisplayDeviceRepository.java b/services/core/java/com/android/server/display/DisplayDeviceRepository.java index 57f44864d2c0..2b52350f0634 100644 --- a/services/core/java/com/android/server/display/DisplayDeviceRepository.java +++ b/services/core/java/com/android/server/display/DisplayDeviceRepository.java @@ -123,6 +123,17 @@ class DisplayDeviceRepository implements DisplayAdapter.Listener { return null; } + // String uniqueId -> DisplayDevice object with that given uniqueId + public DisplayDevice getByUniqueIdLocked(@NonNull String uniqueId) { + for (int i = mDisplayDevices.size() - 1; i >= 0; i--) { + final DisplayDevice displayDevice = mDisplayDevices.get(i); + if (displayDevice.getUniqueId().equals(uniqueId)) { + return displayDevice; + } + } + return null; + } + private void handleDisplayDeviceAdded(DisplayDevice device) { synchronized (mSyncRoot) { DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked(); diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index 1adcf4074db9..d3dc72e63126 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -197,7 +197,7 @@ public final class DisplayManagerService extends SystemService { private static final int MSG_DELIVER_DISPLAY_EVENT = 3; private static final int MSG_REQUEST_TRAVERSAL = 4; private static final int MSG_UPDATE_VIEWPORT = 5; - private static final int MSG_LOAD_BRIGHTNESS_CONFIGURATION = 6; + private static final int MSG_LOAD_BRIGHTNESS_CONFIGURATIONS = 6; private static final int MSG_DELIVER_DISPLAY_EVENT_FRAME_RATE_OVERRIDE = 7; private static final int MSG_DELIVER_DISPLAY_GROUP_EVENT = 8; @@ -523,16 +523,29 @@ public final class DisplayManagerService extends SystemService { final int newUserId = to.getUserIdentifier(); final int userSerial = getUserManager().getUserSerialNumber(newUserId); synchronized (mSyncRoot) { - final DisplayPowerController displayPowerController = mDisplayPowerControllers.get( - Display.DEFAULT_DISPLAY); - if (mCurrentUserId != newUserId) { + boolean userSwitching = mCurrentUserId != newUserId; + if (userSwitching) { mCurrentUserId = newUserId; - BrightnessConfiguration config = - mPersistentDataStore.getBrightnessConfiguration(userSerial); - displayPowerController.setBrightnessConfiguration(config); - handleSettingsChange(); } - displayPowerController.onSwitchUser(newUserId); + mLogicalDisplayMapper.forEachLocked(logicalDisplay -> { + if (logicalDisplay.getDisplayInfoLocked().type != Display.TYPE_INTERNAL) { + return; + } + final DisplayPowerController dpc = mDisplayPowerControllers.get( + logicalDisplay.getDisplayIdLocked()); + if (dpc == null) { + return; + } + if (userSwitching) { + BrightnessConfiguration config = + getBrightnessConfigForDisplayWithPdsFallbackLocked( + logicalDisplay.getPrimaryDisplayDeviceLocked().getUniqueId(), + userSerial); + dpc.setBrightnessConfiguration(config); + } + dpc.onSwitchUser(newUserId); + }); + handleSettingsChange(); } } @@ -1316,6 +1329,13 @@ public final class DisplayManagerService extends SystemService { if (work != null) { mHandler.post(work); } + final int displayId = display.getDisplayIdLocked(); + DisplayPowerController dpc = mDisplayPowerControllers.get(displayId); + if (dpc != null) { + dpc.onDisplayChanged(); + } + mPersistentDataStore.saveIfNeeded(); + mHandler.sendEmptyMessage(MSG_LOAD_BRIGHTNESS_CONFIGURATIONS); handleLogicalDisplayChangedLocked(display); } @@ -1424,24 +1444,42 @@ public final class DisplayManagerService extends SystemService { return mDisplayModeDirector.getModeSwitchingType(); } - private void setBrightnessConfigurationForUserInternal( - @Nullable BrightnessConfiguration c, @UserIdInt int userId, - @Nullable String packageName) { + private void setBrightnessConfigurationForDisplayInternal( + @Nullable BrightnessConfiguration c, String uniqueId, @UserIdInt int userId, + String packageName) { validateBrightnessConfiguration(c); final int userSerial = getUserManager().getUserSerialNumber(userId); synchronized (mSyncRoot) { try { - mPersistentDataStore.setBrightnessConfigurationForUser(c, userSerial, - packageName); + DisplayDevice displayDevice = mDisplayDeviceRepo.getByUniqueIdLocked(uniqueId); + if (displayDevice == null) { + return; + } + mPersistentDataStore.setBrightnessConfigurationForDisplayLocked(c, displayDevice, + userSerial, packageName); } finally { mPersistentDataStore.saveIfNeeded(); } - if (userId == mCurrentUserId) { - mDisplayPowerControllers.get(Display.DEFAULT_DISPLAY).setBrightnessConfiguration(c); + if (userId != mCurrentUserId) { + return; + } + DisplayPowerController dpc = getDpcFromUniqueIdLocked(uniqueId); + if (dpc != null) { + dpc.setBrightnessConfiguration(c); } } } + private DisplayPowerController getDpcFromUniqueIdLocked(String uniqueId) { + final DisplayDevice displayDevice = mDisplayDeviceRepo.getByUniqueIdLocked(uniqueId); + final LogicalDisplay logicalDisplay = mLogicalDisplayMapper.getDisplayLocked(displayDevice); + if (logicalDisplay != null) { + final int displayId = logicalDisplay.getDisplayIdLocked(); + return mDisplayPowerControllers.get(displayId); + } + return null; + } + @VisibleForTesting void validateBrightnessConfiguration(BrightnessConfiguration config) { if (config == null) { @@ -1464,13 +1502,22 @@ public final class DisplayManagerService extends SystemService { return false; } - private void loadBrightnessConfiguration() { + private void loadBrightnessConfigurations() { + int userSerial = getUserManager().getUserSerialNumber(mContext.getUserId()); synchronized (mSyncRoot) { - final int userSerial = getUserManager().getUserSerialNumber(mCurrentUserId); - BrightnessConfiguration config = - mPersistentDataStore.getBrightnessConfiguration(userSerial); - mDisplayPowerControllers.get(Display.DEFAULT_DISPLAY).setBrightnessConfiguration( - config); + mLogicalDisplayMapper.forEachLocked((logicalDisplay) -> { + final String uniqueId = + logicalDisplay.getPrimaryDisplayDeviceLocked().getUniqueId(); + final BrightnessConfiguration config = + getBrightnessConfigForDisplayWithPdsFallbackLocked(uniqueId, userSerial); + if (config != null) { + final DisplayPowerController dpc = mDisplayPowerControllers.get( + logicalDisplay.getDisplayIdLocked()); + if (dpc != null) { + dpc.setBrightnessConfiguration(config); + } + } + }); } } @@ -1681,9 +1728,17 @@ public final class DisplayManagerService extends SystemService { return SurfaceControl.getDisplayedContentSample(token, maxFrames, timestamp); } - void resetBrightnessConfiguration() { - setBrightnessConfigurationForUserInternal(null, mContext.getUserId(), + void resetBrightnessConfigurations() { + mPersistentDataStore.setBrightnessConfigurationForUser(null, mContext.getUserId(), mContext.getPackageName()); + mLogicalDisplayMapper.forEachLocked((logicalDisplay -> { + if (logicalDisplay.getDisplayInfoLocked().type != Display.TYPE_INTERNAL) { + return; + } + final String uniqueId = logicalDisplay.getPrimaryDisplayDeviceLocked().getUniqueId(); + setBrightnessConfigurationForDisplayInternal(null, uniqueId, mContext.getUserId(), + mContext.getPackageName()); + })); } void setAutoBrightnessLoggingEnabled(boolean enabled) { @@ -2132,6 +2187,18 @@ public final class DisplayManagerService extends SystemService { return display == null ? null : display.getPrimaryDisplayDeviceLocked(); } + private BrightnessConfiguration getBrightnessConfigForDisplayWithPdsFallbackLocked( + String uniqueId, int userSerial) { + BrightnessConfiguration config = + mPersistentDataStore.getBrightnessConfigurationForDisplayLocked( + uniqueId, userSerial); + if (config == null) { + // Get from global configurations + config = mPersistentDataStore.getBrightnessConfiguration(userSerial); + } + return config; + } + private final class DisplayManagerHandler extends Handler { public DisplayManagerHandler(Looper looper) { super(looper, null, true /*async*/); @@ -2173,8 +2240,8 @@ public final class DisplayManagerService extends SystemService { break; } - case MSG_LOAD_BRIGHTNESS_CONFIGURATION: - loadBrightnessConfiguration(); + case MSG_LOAD_BRIGHTNESS_CONFIGURATIONS: + loadBrightnessConfigurations(); break; case MSG_DELIVER_DISPLAY_EVENT_FRAME_RATE_OVERRIDE: @@ -2789,6 +2856,19 @@ public final class DisplayManagerService extends SystemService { @Override // Binder call public void setBrightnessConfigurationForUser( BrightnessConfiguration c, @UserIdInt int userId, String packageName) { + mLogicalDisplayMapper.forEachLocked(logicalDisplay -> { + if (logicalDisplay.getDisplayInfoLocked().type != Display.TYPE_INTERNAL) { + return; + } + final DisplayDevice displayDevice = logicalDisplay.getPrimaryDisplayDeviceLocked(); + setBrightnessConfigurationForDisplay(c, displayDevice.getUniqueId(), userId, + packageName); + }); + } + + @Override // Binder call + public void setBrightnessConfigurationForDisplay(BrightnessConfiguration c, + String uniqueId, int userId, String packageName) { mContext.enforceCallingOrSelfPermission( Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS, "Permission required to change the display's brightness configuration"); @@ -2796,21 +2876,19 @@ public final class DisplayManagerService extends SystemService { mContext.enforceCallingOrSelfPermission( Manifest.permission.INTERACT_ACROSS_USERS, "Permission required to change the display brightness" - + " configuration of another user"); - } - if (packageName != null && !validatePackageName(getCallingUid(), packageName)) { - packageName = null; + + " configuration of another user"); } final long token = Binder.clearCallingIdentity(); try { - setBrightnessConfigurationForUserInternal(c, userId, packageName); + setBrightnessConfigurationForDisplayInternal(c, uniqueId, userId, packageName); } finally { Binder.restoreCallingIdentity(token); } } @Override // Binder call - public BrightnessConfiguration getBrightnessConfigurationForUser(int userId) { + public BrightnessConfiguration getBrightnessConfigurationForDisplay(String uniqueId, + int userId) { mContext.enforceCallingOrSelfPermission( Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS, "Permission required to read the display's brightness configuration"); @@ -2821,14 +2899,19 @@ public final class DisplayManagerService extends SystemService { + " configuration of another user"); } final long token = Binder.clearCallingIdentity(); + final int userSerial = getUserManager().getUserSerialNumber(userId); try { - final int userSerial = getUserManager().getUserSerialNumber(userId); synchronized (mSyncRoot) { + // Get from per-display configurations BrightnessConfiguration config = - mPersistentDataStore.getBrightnessConfiguration(userSerial); + getBrightnessConfigForDisplayWithPdsFallbackLocked( + uniqueId, userSerial); if (config == null) { - config = mDisplayPowerControllers.get(Display.DEFAULT_DISPLAY) - .getDefaultBrightnessConfiguration(); + // Get default configuration + DisplayPowerController dpc = getDpcFromUniqueIdLocked(uniqueId); + if (dpc != null) { + config = dpc.getDefaultBrightnessConfiguration(); + } } return config; } @@ -2837,6 +2920,21 @@ public final class DisplayManagerService extends SystemService { } } + + + @Override // Binder call + public BrightnessConfiguration getBrightnessConfigurationForUser(int userId) { + final String uniqueId; + synchronized (mSyncRoot) { + DisplayDevice displayDevice = mLogicalDisplayMapper.getDisplayLocked( + Display.DEFAULT_DISPLAY).getPrimaryDisplayDeviceLocked(); + uniqueId = displayDevice.getUniqueId(); + } + return getBrightnessConfigurationForDisplay(uniqueId, userId); + + + } + @Override // Binder call public BrightnessConfiguration getDefaultBrightnessConfiguration() { mContext.enforceCallingOrSelfPermission( @@ -3105,7 +3203,7 @@ public final class DisplayManagerService extends SystemService { initializeDisplayPowerControllersLocked(); } - mHandler.sendEmptyMessage(MSG_LOAD_BRIGHTNESS_CONFIGURATION); + mHandler.sendEmptyMessage(MSG_LOAD_BRIGHTNESS_CONFIGURATIONS); } @Override diff --git a/services/core/java/com/android/server/display/DisplayManagerShellCommand.java b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java index 94e64f070ef8..9412c938f934 100644 --- a/services/core/java/com/android/server/display/DisplayManagerShellCommand.java +++ b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java @@ -120,7 +120,7 @@ class DisplayManagerShellCommand extends ShellCommand { } private int resetBrightnessConfiguration() { - mService.resetBrightnessConfiguration(); + mService.resetBrightnessConfigurations(); return 0; } diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java index 110893765cbd..abbe13ac260f 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController.java +++ b/services/core/java/com/android/server/display/DisplayPowerController.java @@ -2122,7 +2122,6 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call private void ignoreProximitySensorUntilChangedInternal() { if (!mIgnoreProximityUntilChanged - && mPowerRequest.useProximitySensor && mProximity == PROXIMITY_POSITIVE) { // Only ignore if it is still reporting positive (near) mIgnoreProximityUntilChanged = true; diff --git a/services/core/java/com/android/server/display/DisplayPowerState.java b/services/core/java/com/android/server/display/DisplayPowerState.java index 6af192371e3d..147050cd271f 100644 --- a/services/core/java/com/android/server/display/DisplayPowerState.java +++ b/services/core/java/com/android/server/display/DisplayPowerState.java @@ -411,7 +411,7 @@ final class DisplayPowerState { * Updates the state of the screen and backlight asynchronously on a separate thread. */ private final class PhotonicModulator extends Thread { - private static final int INITIAL_SCREEN_STATE = Display.STATE_OFF; // unknown, assume off + private static final int INITIAL_SCREEN_STATE = Display.STATE_UNKNOWN; private static final float INITIAL_BACKLIGHT_FLOAT = PowerManager.BRIGHTNESS_INVALID_FLOAT; private final Object mLock = new Object(); @@ -494,7 +494,9 @@ final class DisplayPowerState { if (!backlightChanged) { mBacklightChangeInProgress = false; } - if (!stateChanged && !backlightChanged) { + boolean valid = state != Display.STATE_UNKNOWN && !Float.isNaN(brightnessState); + boolean changed = stateChanged || backlightChanged; + if (!valid || !changed) { try { mLock.wait(); } catch (InterruptedException ex) { diff --git a/services/core/java/com/android/server/display/LogicalDisplayMapper.java b/services/core/java/com/android/server/display/LogicalDisplayMapper.java index 4c9a2d702114..a93171835365 100644 --- a/services/core/java/com/android/server/display/LogicalDisplayMapper.java +++ b/services/core/java/com/android/server/display/LogicalDisplayMapper.java @@ -195,6 +195,9 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { } public LogicalDisplay getDisplayLocked(DisplayDevice device) { + if (device == null) { + return null; + } final int count = mLogicalDisplays.size(); for (int i = 0; i < count; i++) { final LogicalDisplay display = mLogicalDisplays.valueAt(i); diff --git a/services/core/java/com/android/server/display/PersistentDataStore.java b/services/core/java/com/android/server/display/PersistentDataStore.java index c90ddf48a091..4b0d43b3d1d4 100644 --- a/services/core/java/com/android/server/display/PersistentDataStore.java +++ b/services/core/java/com/android/server/display/PersistentDataStore.java @@ -63,6 +63,15 @@ import java.util.Objects; * <display unique-id="XXXXXXX"> * <color-mode>0</color-mode> * <brightness-value>0</brightness-value> + * <brightness-configurations> + * <brightness-configuration user-serial="0" package-name="com.example" + * timestamp="1234"> + * <brightness-curve description="some text"> + * <brightness-point lux="0" nits="13.25"/> + * <brightness-point lux="20" nits="35.94"/> + * </brightness-curve> + * </brightness-configuration> + * </brightness-configurations> * </display> * </display-states> * <stable-device-values> @@ -120,7 +129,8 @@ final class PersistentDataStore { private final StableDeviceValues mStableDeviceValues = new StableDeviceValues(); // Brightness configuration by user - private BrightnessConfigurations mBrightnessConfigurations = new BrightnessConfigurations(); + private BrightnessConfigurations mGlobalBrightnessConfigurations = + new BrightnessConfigurations(); // True if the data has been loaded. private boolean mLoaded; @@ -293,18 +303,44 @@ final class PersistentDataStore { } } + // Used for testing & reset public void setBrightnessConfigurationForUser(BrightnessConfiguration c, int userSerial, @Nullable String packageName) { loadIfNeeded(); - if (mBrightnessConfigurations.setBrightnessConfigurationForUser(c, userSerial, + if (mGlobalBrightnessConfigurations.setBrightnessConfigurationForUser(c, userSerial, packageName)) { + setDirty(); } } + public boolean setBrightnessConfigurationForDisplayLocked(BrightnessConfiguration configuration, + DisplayDevice device, int userSerial, String packageName) { + if (device == null || !device.hasStableUniqueId()) { + return false; + } + DisplayState state = getDisplayState(device.getUniqueId(), /*createIfAbsent*/ true); + if (state.setBrightnessConfiguration(configuration, userSerial, packageName)) { + setDirty(); + return true; + } + return false; + } + + + public BrightnessConfiguration getBrightnessConfigurationForDisplayLocked( + String uniqueDisplayId, int userSerial) { + loadIfNeeded(); + DisplayState state = mDisplayStates.get(uniqueDisplayId); + if (state != null) { + return state.getBrightnessConfiguration(userSerial); + } + return null; + } + public BrightnessConfiguration getBrightnessConfiguration(int userSerial) { loadIfNeeded(); - return mBrightnessConfigurations.getBrightnessConfiguration(userSerial); + return mGlobalBrightnessConfigurations.getBrightnessConfiguration(userSerial); } private DisplayState getDisplayState(String uniqueId, boolean createIfAbsent) { @@ -391,7 +427,7 @@ final class PersistentDataStore { mStableDeviceValues.loadFromXml(parser); } if (parser.getName().equals(TAG_BRIGHTNESS_CONFIGURATIONS)) { - mBrightnessConfigurations.loadFromXml(parser); + mGlobalBrightnessConfigurations.loadFromXml(parser); } } } @@ -470,7 +506,7 @@ final class PersistentDataStore { mStableDeviceValues.saveToXml(serializer); serializer.endTag(null, TAG_STABLE_DEVICE_VALUES); serializer.startTag(null, TAG_BRIGHTNESS_CONFIGURATIONS); - mBrightnessConfigurations.saveToXml(serializer); + mGlobalBrightnessConfigurations.saveToXml(serializer); serializer.endTag(null, TAG_BRIGHTNESS_CONFIGURATIONS); serializer.endTag(null, TAG_DISPLAY_MANAGER_STATE); serializer.endDocument(); @@ -493,14 +529,18 @@ final class PersistentDataStore { } pw.println(" StableDeviceValues:"); mStableDeviceValues.dump(pw, " "); - pw.println(" BrightnessConfigurations:"); - mBrightnessConfigurations.dump(pw, " "); + pw.println(" GlobalBrightnessConfigurations:"); + mGlobalBrightnessConfigurations.dump(pw, " "); } private static final class DisplayState { private int mColorMode; private float mBrightness; + // Brightness configuration by user + private BrightnessConfigurations mDisplayBrightnessConfigurations = + new BrightnessConfigurations(); + public boolean setColorMode(int colorMode) { if (colorMode == mColorMode) { return false; @@ -525,6 +565,16 @@ final class PersistentDataStore { return mBrightness; } + public boolean setBrightnessConfiguration(BrightnessConfiguration configuration, + int userSerial, String packageName) { + mDisplayBrightnessConfigurations.setBrightnessConfigurationForUser( + configuration, userSerial, packageName); + return true; + } + + public BrightnessConfiguration getBrightnessConfiguration(int userSerial) { + return mDisplayBrightnessConfigurations.mConfigurations.get(userSerial); + } public void loadFromXml(TypedXmlPullParser parser) throws IOException, XmlPullParserException { @@ -540,6 +590,9 @@ final class PersistentDataStore { String brightness = parser.nextText(); mBrightness = Float.parseFloat(brightness); break; + case TAG_BRIGHTNESS_CONFIGURATIONS: + mDisplayBrightnessConfigurations.loadFromXml(parser); + break; } } } @@ -548,15 +601,21 @@ final class PersistentDataStore { serializer.startTag(null, TAG_COLOR_MODE); serializer.text(Integer.toString(mColorMode)); serializer.endTag(null, TAG_COLOR_MODE); + serializer.startTag(null, TAG_BRIGHTNESS_VALUE); serializer.text(Float.toString(mBrightness)); serializer.endTag(null, TAG_BRIGHTNESS_VALUE); + serializer.startTag(null, TAG_BRIGHTNESS_CONFIGURATIONS); + mDisplayBrightnessConfigurations.saveToXml(serializer); + serializer.endTag(null, TAG_BRIGHTNESS_CONFIGURATIONS); } public void dump(final PrintWriter pw, final String prefix) { pw.println(prefix + "ColorMode=" + mColorMode); pw.println(prefix + "BrightnessValue=" + mBrightness); + pw.println(prefix + "DisplayBrightnessConfigurations: "); + mDisplayBrightnessConfigurations.dump(pw, prefix); } } @@ -621,11 +680,11 @@ final class PersistentDataStore { private static final class BrightnessConfigurations { // Maps from a user ID to the users' given brightness configuration - private SparseArray<BrightnessConfiguration> mConfigurations; + private final SparseArray<BrightnessConfiguration> mConfigurations; // Timestamp of time the configuration was set. - private SparseLongArray mTimeStamps; + private final SparseLongArray mTimeStamps; // Package that set the configuration. - private SparseArray<String> mPackageNames; + private final SparseArray<String> mPackageNames; public BrightnessConfigurations() { mConfigurations = new SparseArray<>(); diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index 1531cd589762..fe6f2da32da4 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -4122,6 +4122,18 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } } + /** Called right after {@link IInputMethod#showSoftInput}. */ + private void onShowHideSoftInputRequested(boolean show, IBinder requestToken, + @SoftInputShowHideReason int reason) { + final WindowManagerInternal.ImeTargetInfo info = + mWindowManagerInternal.onToggleImeRequested( + show, mCurFocusedWindow, requestToken, mCurTokenDisplayId); + mSoftInputShowHideHistory.addEntry(new SoftInputShowHideHistory.Entry( + mCurFocusedWindowClient, mCurAttribute, info.focusedWindowName, + mCurFocusedWindowSoftInputMode, reason, mInFullscreenMode, + info.requestWindowName, info.imeControlTargetName, info.imeLayerTargetName)); + } + @BinderThread private void hideMySoftInput(@NonNull IBinder token, int flags) { Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMMS.hideMySoftInput"); @@ -4240,18 +4252,11 @@ public class InputMethodManagerService extends IInputMethodManager.Stub if (DEBUG) Slog.v(TAG, "Calling " + args.arg1 + ".showSoftInput(" + args.arg3 + ", " + msg.arg1 + ", " + args.arg2 + ") for reason: " + InputMethodDebug.softInputDisplayReasonToString(reason)); + final IBinder token = (IBinder) args.arg3; ((IInputMethod) args.arg1).showSoftInput( - (IBinder) args.arg3, msg.arg1, (ResultReceiver) args.arg2); - mSoftInputShowHideHistory.addEntry(new SoftInputShowHideHistory.Entry( - mCurFocusedWindowClient, mCurAttribute, - mWindowManagerInternal.getWindowName(mCurFocusedWindow), - mCurFocusedWindowSoftInputMode, reason, mInFullscreenMode, - mWindowManagerInternal.getWindowName( - mShowRequestWindowMap.get(args.arg3)), - mWindowManagerInternal.getImeControlTargetNameForLogging( - mCurTokenDisplayId), - mWindowManagerInternal.getImeTargetNameForLogging( - mCurTokenDisplayId))); + token, msg.arg1 /* flags */, (ResultReceiver) args.arg2); + final IBinder requestToken = mShowRequestWindowMap.get(token); + onShowHideSoftInputRequested(true /* show */, requestToken, reason); } catch (RemoteException e) { } args.recycle(); @@ -4263,18 +4268,11 @@ public class InputMethodManagerService extends IInputMethodManager.Stub if (DEBUG) Slog.v(TAG, "Calling " + args.arg1 + ".hideSoftInput(0, " + args.arg3 + ", " + args.arg2 + ") for reason: " + InputMethodDebug.softInputDisplayReasonToString(reason)); + final IBinder token = (IBinder) args.arg3; ((IInputMethod)args.arg1).hideSoftInput( - (IBinder) args.arg3, 0, (ResultReceiver)args.arg2); - mSoftInputShowHideHistory.addEntry(new SoftInputShowHideHistory.Entry( - mCurFocusedWindowClient, mCurAttribute, - mWindowManagerInternal.getWindowName(mCurFocusedWindow), - mCurFocusedWindowSoftInputMode, reason, mInFullscreenMode, - mWindowManagerInternal.getWindowName( - mHideRequestWindowMap.get(args.arg3)), - mWindowManagerInternal.getImeControlTargetNameForLogging( - mCurTokenDisplayId), - mWindowManagerInternal.getImeTargetNameForLogging( - mCurTokenDisplayId))); + token, 0 /* flags */, (ResultReceiver) args.arg2); + final IBinder requestToken = mHideRequestWindowMap.get(token); + onShowHideSoftInputRequested(false /* show */, requestToken, reason); } catch (RemoteException e) { } args.recycle(); diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index b54e8f973bd6..207baf539345 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -4363,8 +4363,7 @@ public class NotificationManagerService extends SystemService { final int userId = r.getSbn().getUserId(); if (userId != info.userid && userId != UserHandle.USER_ALL && !mUserProfiles.isCurrentProfile(userId)) { - throw new SecurityException("Disallowed call from listener: " - + info.service); + continue; } cancelNotificationFromListenerLocked(info, callingUid, callingPid, r.getSbn().getPackageName(), r.getSbn().getTag(), @@ -4431,8 +4430,7 @@ public class NotificationManagerService extends SystemService { final int userId = r.getSbn().getUserId(); if (userId != info.userid && userId != UserHandle.USER_ALL && !mUserProfiles.isCurrentProfile(userId)) { - throw new SecurityException("Disallowed call from listener: " - + info.service); + continue; } seen.add(r); if (!r.isSeen()) { diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 904a1f0d6f4f..140098da5791 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -7220,6 +7220,7 @@ public class PackageManagerService extends IPackageManager.Stub t.traceEnd(); mPermissionManager.readLegacyPermissionsTEMP(mSettings.mPermissions); + mPermissionManager.readLegacyPermissionStateTEMP(); if (!mOnlyCore && mFirstBoot) { requestCopyPreoptedFiles(mInjector); @@ -7635,7 +7636,6 @@ public class PackageManagerService extends IPackageManager.Stub + ((SystemClock.uptimeMillis()-startTime)/1000f) + " seconds"); - mPermissionManager.readLegacyPermissionStateTEMP(); // If the build fingerprint has changed since the last time we booted, // we need to re-grant app permission to catch any new ones that // appear. This is really a hack, and means that apps can in some @@ -22528,7 +22528,7 @@ public class PackageManagerService extends IPackageManager.Stub UserManagerInternal umInternal = mInjector.getUserManagerInternal(); final int flags; - if (StorageManager.isUserKeyUnlocked(userId)) { + if (umInternal.isUserUnlockingOrUnlocked(userId)) { flags = StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE; } else if (umInternal.isUserRunning(userId)) { flags = StorageManager.FLAG_STORAGE_DE; @@ -25395,7 +25395,7 @@ public class PackageManagerService extends IPackageManager.Stub UserManagerInternal umInternal = mInjector.getUserManagerInternal(); for (UserInfo user : mUserManager.getUsers(false /* includeDying */)) { final int flags; - if (StorageManager.isUserKeyUnlocked(user.id)) { + if (umInternal.isUserUnlockingOrUnlocked(user.id)) { flags = StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE; } else if (umInternal.isUserRunning(user.id)) { flags = StorageManager.FLAG_STORAGE_DE; @@ -25735,7 +25735,7 @@ public class PackageManagerService extends IPackageManager.Stub StorageManagerInternal smInternal = mInjector.getLocalService(StorageManagerInternal.class); for (UserInfo user : mUserManager.getUsers(false /*excludeDying*/)) { final int flags; - if (StorageManager.isUserKeyUnlocked(user.id)) { + if (umInternal.isUserUnlockingOrUnlocked(user.id)) { flags = StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE; } else if (umInternal.isUserRunning(user.id)) { flags = StorageManager.FLAG_STORAGE_DE; @@ -25749,7 +25749,7 @@ public class PackageManagerService extends IPackageManager.Stub // Note: this code block is executed with the Installer lock // already held, since it's invoked as a side-effect of // executeBatchLI() - if (StorageManager.isUserKeyUnlocked(user.id)) { + if (umInternal.isUserUnlockingOrUnlocked(user.id)) { // Prepare app data on external storage; currently this is used to // setup any OBB dirs that were created by the installer correctly. int uid = UserHandle.getUid(user.id, UserHandle.getAppId(pkg.getUid())); diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java index fcbf40e29933..62d6717e847a 100644 --- a/services/core/java/com/android/server/pm/ShortcutService.java +++ b/services/core/java/com/android/server/pm/ShortcutService.java @@ -1664,6 +1664,19 @@ public class ShortcutService extends IShortcutService.Stub { mContext.enforceCallingPermission(permission, message); } + private void verifyCallerUserId(@UserIdInt int userId) { + if (isCallerSystem()) { + return; // no check + } + + final int callingUid = injectBinderCallingUid(); + + // Otherwise, make sure the arguments are valid. + if (UserHandle.getUserId(callingUid) != userId) { + throw new SecurityException("Invalid user-ID"); + } + } + private void verifyCaller(@NonNull String packageName, @UserIdInt int userId) { Preconditions.checkStringNotEmpty(packageName, "packageName"); @@ -2847,6 +2860,8 @@ public class ShortcutService extends IShortcutService.Stub { @Override public boolean isRequestPinItemSupported(int callingUserId, int requestType) { + verifyCallerUserId(callingUserId); + final long token = injectClearCallingIdentity(); try { return mShortcutRequestPinProcessor diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java index 08a67d7f4bb5..c11ffb492a93 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java @@ -217,6 +217,8 @@ public class PermissionManagerService extends IPermissionManager.Stub { /** All storage permissions */ private static final List<String> STORAGE_PERMISSIONS = new ArrayList<>(); + /** All nearby devices permissions */ + private static final List<String> NEARBY_DEVICES_PERMISSIONS = new ArrayList<>(); /** If the permission of the value is granted, so is the key */ private static final Map<String, String> FULLER_PERMISSION_MAP = new HashMap<>(); @@ -233,6 +235,9 @@ public class PermissionManagerService extends IPermissionManager.Stub { STORAGE_PERMISSIONS.add(Manifest.permission.READ_EXTERNAL_STORAGE); STORAGE_PERMISSIONS.add(Manifest.permission.WRITE_EXTERNAL_STORAGE); STORAGE_PERMISSIONS.add(Manifest.permission.ACCESS_MEDIA_LOCATION); + NEARBY_DEVICES_PERMISSIONS.add(Manifest.permission.BLUETOOTH_ADVERTISE); + NEARBY_DEVICES_PERMISSIONS.add(Manifest.permission.BLUETOOTH_CONNECT); + NEARBY_DEVICES_PERMISSIONS.add(Manifest.permission.BLUETOOTH_SCAN); } /** Set of source package names for Privileged Permission Allowlist */ @@ -1210,6 +1215,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { private boolean checkExistsAndEnforceCannotModifyImmutablyRestrictedPermission( @NonNull String permName) { + final String permissionPackageName; final boolean isImmutablyRestrictedPermission; synchronized (mLock) { final Permission bp = mRegistry.getPermission(permName); @@ -1217,15 +1223,25 @@ public class PermissionManagerService extends IPermissionManager.Stub { Slog.w(TAG, "No such permissions: " + permName); return false; } + permissionPackageName = bp.getPackageName(); isImmutablyRestrictedPermission = bp.isHardOrSoftRestricted() && bp.isImmutablyRestricted(); } + + final int callingUid = getCallingUid(); + final int callingUserId = UserHandle.getUserId(callingUid); + if (mPackageManagerInt.filterAppAccess(permissionPackageName, callingUid, callingUserId)) { + EventLog.writeEvent(0x534e4554, "186404356", callingUid, permName); + return false; + } + if (isImmutablyRestrictedPermission && mContext.checkCallingOrSelfPermission( Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS) != PackageManager.PERMISSION_GRANTED) { throw new SecurityException("Cannot modify allowlisting of an immutably " + "restricted permission: " + permName); } + return true; } @@ -3076,13 +3092,26 @@ public class PermissionManagerService extends IPermissionManager.Stub { Permission bp = mRegistry.getPermission(permission); if (bp != null && bp.isRuntime()) { int flags = ps.getPermissionFlags(permission); - if ((flags & FLAG_PERMISSION_REVOKE_WHEN_REQUESTED) != 0) { - int flagsToRemove = FLAG_PERMISSION_REVOKE_WHEN_REQUESTED; + // We're willing to preserve an implicit "Nearby devices" + // permission grant if this app was already able to interact + // with nearby devices via background location access + boolean preserveGrant = false; + if (ArrayUtils.contains(NEARBY_DEVICES_PERMISSIONS, permission) + && ps.isPermissionGranted( + android.Manifest.permission.ACCESS_BACKGROUND_LOCATION) + && (ps.getPermissionFlags( + android.Manifest.permission.ACCESS_BACKGROUND_LOCATION) + & (FLAG_PERMISSION_REVOKE_WHEN_REQUESTED + | FLAG_PERMISSION_REVOKED_COMPAT)) == 0) { + preserveGrant = true; + } + if ((flags & BLOCKING_PERMISSION_FLAGS) == 0 - && supportsRuntimePermissions) { + && supportsRuntimePermissions + && !preserveGrant) { if (ps.revokePermission(bp)) { if (DEBUG_PERMISSIONS) { Slog.i(TAG, "Revoking runtime permission " diff --git a/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java b/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java index edd5f5f415c6..ff6511f06577 100644 --- a/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java +++ b/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java @@ -41,6 +41,7 @@ import com.android.server.devicestate.DeviceState; import com.android.server.devicestate.DeviceStateProvider; import com.android.server.policy.devicestate.config.Conditions; import com.android.server.policy.devicestate.config.DeviceStateConfig; +import com.android.server.policy.devicestate.config.Flags; import com.android.server.policy.devicestate.config.LidSwitchCondition; import com.android.server.policy.devicestate.config.NumericRange; import com.android.server.policy.devicestate.config.SensorCondition; @@ -87,7 +88,7 @@ public final class DeviceStateProviderImpl implements DeviceStateProvider, @VisibleForTesting static final DeviceState DEFAULT_DEVICE_STATE = new DeviceState(MINIMUM_DEVICE_STATE, - "DEFAULT"); + "DEFAULT", 0 /* flags */); private static final String VENDOR_CONFIG_FILE_PATH = "etc/devicestate/"; private static final String DATA_CONFIG_FILE_PATH = "system/devicestate/"; @@ -131,7 +132,26 @@ public final class DeviceStateProviderImpl implements DeviceStateProvider, config.getDeviceState()) { final int state = stateConfig.getIdentifier().intValue(); final String name = stateConfig.getName() == null ? "" : stateConfig.getName(); - deviceStateList.add(new DeviceState(state, name)); + + int flags = 0; + final Flags configFlags = stateConfig.getFlags(); + if (configFlags != null) { + List<String> configFlagStrings = configFlags.getFlag(); + for (int i = 0; i < configFlagStrings.size(); i++) { + final String configFlagString = configFlagStrings.get(i); + switch (configFlagString) { + case "FLAG_CANCEL_STICKY_REQUESTS": + flags |= DeviceState.FLAG_CANCEL_STICKY_REQUESTS; + break; + default: + Slog.w(TAG, "Parsed unknown flag with name: " + + configFlagString); + break; + } + } + } + + deviceStateList.add(new DeviceState(state, name, flags)); final Conditions condition = stateConfig.getConditions(); conditionsList.add(condition); diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 139ab0cc6585..45f5daac8dd9 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -484,6 +484,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { int mLidNavigationAccessibility; int mShortPressOnPowerBehavior; int mLongPressOnPowerBehavior; + long mLongPressOnPowerAssistantTimeoutMs; int mVeryLongPressOnPowerBehavior; int mDoublePressOnPowerBehavior; int mTriplePressOnPowerBehavior; @@ -732,6 +733,9 @@ public class PhoneWindowManager implements WindowManagerPolicy { Settings.Global.POWER_BUTTON_LONG_PRESS), false, this, UserHandle.USER_ALL); resolver.registerContentObserver(Settings.Global.getUriFor( + Settings.Global.POWER_BUTTON_LONG_PRESS_DURATION_MS), false, this, + UserHandle.USER_ALL); + resolver.registerContentObserver(Settings.Global.getUriFor( Settings.Global.POWER_BUTTON_VERY_LONG_PRESS), false, this, UserHandle.USER_ALL); resolver.registerContentObserver(Settings.Global.getUriFor( @@ -1732,6 +1736,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { com.android.internal.R.integer.config_shortPressOnPowerBehavior); mLongPressOnPowerBehavior = mContext.getResources().getInteger( com.android.internal.R.integer.config_longPressOnPowerBehavior); + mLongPressOnPowerAssistantTimeoutMs = mContext.getResources().getInteger( + com.android.internal.R.integer.config_longPressOnPowerDurationMs); mVeryLongPressOnPowerBehavior = mContext.getResources().getInteger( com.android.internal.R.integer.config_veryLongPressOnPowerBehavior); mDoublePressOnPowerBehavior = mContext.getResources().getInteger( @@ -1955,7 +1961,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { */ private final class PowerKeyRule extends SingleKeyGestureDetector.SingleKeyRule { PowerKeyRule(int gestures) { - super(KEYCODE_POWER, gestures); + super(mContext, KEYCODE_POWER, gestures); } @Override @@ -1970,6 +1976,15 @@ public class PhoneWindowManager implements WindowManagerPolicy { } @Override + long getLongPressTimeoutMs() { + if (getResolvedLongPressOnPowerBehavior() == LONG_PRESS_POWER_ASSISTANT) { + return mLongPressOnPowerAssistantTimeoutMs; + } else { + return super.getLongPressTimeoutMs(); + } + } + + @Override void onLongPress(long eventTime) { if (mSingleKeyGestureDetector.beganFromNonInteractive() && !mSupportLongPressPowerWhenNonInteractive) { @@ -1997,7 +2012,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { */ private final class BackKeyRule extends SingleKeyGestureDetector.SingleKeyRule { BackKeyRule(int gestures) { - super(KEYCODE_BACK, gestures); + super(mContext, KEYCODE_BACK, gestures); } @Override @@ -2017,7 +2032,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { } private void initSingleKeyGestureRules() { - mSingleKeyGestureDetector = new SingleKeyGestureDetector(mContext); + mSingleKeyGestureDetector = new SingleKeyGestureDetector(); int powerKeyGestures = 0; if (hasVeryLongPressOnPowerBehavior()) { @@ -2115,6 +2130,11 @@ public class PhoneWindowManager implements WindowManagerPolicy { Settings.Global.POWER_BUTTON_LONG_PRESS, mContext.getResources().getInteger( com.android.internal.R.integer.config_longPressOnPowerBehavior)); + mLongPressOnPowerAssistantTimeoutMs = Settings.Global.getLong( + mContext.getContentResolver(), + Settings.Global.POWER_BUTTON_LONG_PRESS_DURATION_MS, + mContext.getResources().getInteger( + com.android.internal.R.integer.config_longPressOnPowerDurationMs)); mVeryLongPressOnPowerBehavior = Settings.Global.getInt(resolver, Settings.Global.POWER_BUTTON_VERY_LONG_PRESS, mContext.getResources().getInteger( @@ -5331,6 +5351,9 @@ public class PhoneWindowManager implements WindowManagerPolicy { pw.print("mLongPressOnPowerBehavior="); pw.println(longPressOnPowerBehaviorToString(mLongPressOnPowerBehavior)); pw.print(prefix); + pw.print("mLongPressOnPowerAssistantTimeoutMs="); + pw.println(mLongPressOnPowerAssistantTimeoutMs); + pw.print(prefix); pw.print("mVeryLongPressOnPowerBehavior="); pw.println(veryLongPressOnPowerBehaviorToString(mVeryLongPressOnPowerBehavior)); pw.print(prefix); diff --git a/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java b/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java index 1ef2bf9151e0..6fee69bf0472 100644 --- a/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java +++ b/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java @@ -44,9 +44,6 @@ public final class SingleKeyGestureDetector { private static final int MSG_KEY_VERY_LONG_PRESS = 1; private static final int MSG_KEY_DELAYED_PRESS = 2; - private final long mLongPressTimeout; - private final long mVeryLongPressTimeout; - private volatile int mKeyPressCounter; private boolean mBeganFromNonInteractive = false; @@ -86,12 +83,19 @@ public final class SingleKeyGestureDetector { * </pre> */ abstract static class SingleKeyRule { + private final int mKeyCode; private final int mSupportedGestures; + private final long mDefaultLongPressTimeout; + private final long mDefaultVeryLongPressTimeout; - SingleKeyRule(int keyCode, @KeyGestureFlag int supportedGestures) { + SingleKeyRule(Context context, int keyCode, @KeyGestureFlag int supportedGestures) { mKeyCode = keyCode; mSupportedGestures = supportedGestures; + mDefaultLongPressTimeout = + ViewConfiguration.get(context).getDeviceGlobalActionKeyTimeout(); + mDefaultVeryLongPressTimeout = context.getResources().getInteger( + com.android.internal.R.integer.config_veryLongPressTimeout); } /** @@ -134,10 +138,28 @@ public final class SingleKeyGestureDetector { */ void onMultiPress(long downTime, int count) {} /** + * Returns the timeout in milliseconds for a long press. + * + * If multipress is also supported, this should always be greater than the multipress + * timeout. If very long press is supported, this should always be less than the very long + * press timeout. + */ + long getLongPressTimeoutMs() { + return mDefaultLongPressTimeout; + } + /** * Callback when long press has been detected. */ void onLongPress(long eventTime) {} /** + * Returns the timeout in milliseconds for a very long press. + * + * If long press is supported, this should always be longer than the long press timeout. + */ + long getVeryLongPressTimeoutMs() { + return mDefaultVeryLongPressTimeout; + } + /** * Callback when very long press has been detected. */ void onVeryLongPress(long eventTime) {} @@ -151,10 +173,7 @@ public final class SingleKeyGestureDetector { } } - public SingleKeyGestureDetector(Context context) { - mLongPressTimeout = ViewConfiguration.get(context).getDeviceGlobalActionKeyTimeout(); - mVeryLongPressTimeout = context.getResources().getInteger( - com.android.internal.R.integer.config_veryLongPressTimeout); + public SingleKeyGestureDetector() { mHandler = new KeyHandler(); } @@ -225,14 +244,14 @@ public final class SingleKeyGestureDetector { final Message msg = mHandler.obtainMessage(MSG_KEY_LONG_PRESS, keyCode, 0, eventTime); msg.setAsynchronous(true); - mHandler.sendMessageDelayed(msg, mLongPressTimeout); + mHandler.sendMessageDelayed(msg, mActiveRule.getLongPressTimeoutMs()); } if (mActiveRule.supportVeryLongPress()) { final Message msg = mHandler.obtainMessage(MSG_KEY_VERY_LONG_PRESS, keyCode, 0, eventTime); msg.setAsynchronous(true); - mHandler.sendMessageDelayed(msg, mVeryLongPressTimeout); + mHandler.sendMessageDelayed(msg, mActiveRule.getVeryLongPressTimeoutMs()); } } else { mHandler.removeMessages(MSG_KEY_LONG_PRESS); diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java index 7555a7f2920b..c91d8dedc41b 100644 --- a/services/core/java/com/android/server/power/Notifier.java +++ b/services/core/java/com/android/server/power/Notifier.java @@ -96,6 +96,7 @@ public class Notifier { private static final int MSG_BROADCAST_ENHANCED_PREDICTION = 4; private static final int MSG_PROFILE_TIMED_OUT = 5; private static final int MSG_WIRED_CHARGING_STARTED = 6; + private static final int MSG_SCREEN_POLICY = 7; private static final long[] CHARGING_VIBRATION_TIME = { 40, 40, 40, 40, 40, 40, 40, 40, 40, // ramp-up sampling rate = 40ms @@ -120,6 +121,7 @@ public class Notifier { private final SuspendBlocker mSuspendBlocker; private final WindowManagerPolicy mPolicy; private final FaceDownDetector mFaceDownDetector; + private final ScreenUndimDetector mScreenUndimDetector; private final ActivityManagerInternal mActivityManagerInternal; private final InputManagerInternal mInputManagerInternal; private final InputMethodManagerInternal mInputMethodManagerInternal; @@ -167,13 +169,14 @@ public class Notifier { public Notifier(Looper looper, Context context, IBatteryStats batteryStats, SuspendBlocker suspendBlocker, WindowManagerPolicy policy, - FaceDownDetector faceDownDetector) { + FaceDownDetector faceDownDetector, ScreenUndimDetector screenUndimDetector) { mContext = context; mBatteryStats = batteryStats; mAppOps = mContext.getSystemService(AppOpsManager.class); mSuspendBlocker = suspendBlocker; mPolicy = policy; mFaceDownDetector = faceDownDetector; + mScreenUndimDetector = screenUndimDetector; mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class); mInputManagerInternal = LocalServices.getService(InputManagerInternal.class); mInputMethodManagerInternal = LocalServices.getService(InputMethodManagerInternal.class); @@ -620,6 +623,22 @@ public class Notifier { } /** + * Called when the screen policy changes. + */ + public void onScreenPolicyUpdate(int newPolicy) { + if (DEBUG) { + Slog.d(TAG, "onScreenPolicyUpdate: newPolicy=" + newPolicy); + } + + synchronized (mLock) { + Message msg = mHandler.obtainMessage(MSG_SCREEN_POLICY); + msg.arg1 = newPolicy; + msg.setAsynchronous(true); + mHandler.sendMessage(msg); + } + } + + /** * Dumps data for bugreports. * * @param pw The stream to print to. @@ -659,6 +678,7 @@ public class Notifier { tm.notifyUserActivity(); mPolicy.userActivity(); mFaceDownDetector.userActivity(event); + mScreenUndimDetector.userActivity(); } void postEnhancedDischargePredictionBroadcast(long delayMs) { @@ -812,6 +832,10 @@ public class Notifier { mSuspendBlocker.release(); } + private void screenPolicyChanging(int screenPolicy) { + mScreenUndimDetector.recordScreenPolicy(screenPolicy); + } + private void lockProfile(@UserIdInt int userId) { mTrustManager.setDeviceLockedForUser(userId, true /*locked*/); } @@ -852,6 +876,9 @@ public class Notifier { case MSG_WIRED_CHARGING_STARTED: showWiredChargingStarted(msg.arg1); break; + case MSG_SCREEN_POLICY: + screenPolicyChanging(msg.arg1); + break; } } } diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java index 9638255dfc79..688a3b2f1d59 100644 --- a/services/core/java/com/android/server/power/PowerManagerService.java +++ b/services/core/java/com/android/server/power/PowerManagerService.java @@ -129,6 +129,7 @@ import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.NoSuchElementException; import java.util.Objects; /** @@ -273,6 +274,7 @@ public final class PowerManagerService extends SystemService private final BatterySavingStats mBatterySavingStats; private final AttentionDetector mAttentionDetector; private final FaceDownDetector mFaceDownDetector; + private final ScreenUndimDetector mScreenUndimDetector; private final BinderService mBinderService; private final LocalService mLocalService; private final NativeWrapper mNativeWrapper; @@ -532,6 +534,11 @@ public final class PowerManagerService extends SystemService // True if the proximity sensor reads a positive result. private boolean mProximityPositive; + // Indicates that we have already intercepted the power key to temporarily ignore the proximity + // wake lock and turn the screen back on. This should get reset when prox reads 'far' again + // (when {@link #mProximityPositive} is set to false). + private boolean mInterceptedPowerKeyForProximity; + // Screen brightness setting limits. public final float mScreenBrightnessMinimum; public final float mScreenBrightnessMaximum; @@ -831,9 +838,10 @@ public final class PowerManagerService extends SystemService static class Injector { Notifier createNotifier(Looper looper, Context context, IBatteryStats batteryStats, SuspendBlocker suspendBlocker, WindowManagerPolicy policy, - FaceDownDetector faceDownDetector) { + FaceDownDetector faceDownDetector, ScreenUndimDetector screenUndimDetector) { return new Notifier( - looper, context, batteryStats, suspendBlocker, policy, faceDownDetector); + looper, context, batteryStats, suspendBlocker, policy, faceDownDetector, + screenUndimDetector); } SuspendBlocker createSuspendBlocker(PowerManagerService service, String name) { @@ -954,6 +962,7 @@ public final class PowerManagerService extends SystemService mInjector.createAmbientDisplaySuppressionController(context); mAttentionDetector = new AttentionDetector(this::onUserAttention, mLock); mFaceDownDetector = new FaceDownDetector(this::onFlip); + mScreenUndimDetector = new ScreenUndimDetector(); mBatterySavingStats = new BatterySavingStats(mLock); mBatterySaverPolicy = @@ -1140,7 +1149,7 @@ public final class PowerManagerService extends SystemService mBatteryStats = BatteryStatsService.getService(); mNotifier = mInjector.createNotifier(Looper.getMainLooper(), mContext, mBatteryStats, mInjector.createSuspendBlocker(this, "PowerManagerService.Broadcasts"), - mPolicy, mFaceDownDetector); + mPolicy, mFaceDownDetector, mScreenUndimDetector); mWirelessChargerDetector = mInjector.createWirelessChargerDetector(sensorManager, mInjector.createSuspendBlocker( @@ -1175,6 +1184,7 @@ public final class PowerManagerService extends SystemService mBatterySaverController.systemReady(); mBatterySaverPolicy.systemReady(); mFaceDownDetector.systemReady(mContext); + mScreenUndimDetector.systemReady(mContext); // Register for settings changes. resolver.registerContentObserver(Settings.Secure.getUriFor( @@ -1490,7 +1500,11 @@ public final class PowerManagerService extends SystemService mRequestWaitForNegativeProximity = true; } - wakeLock.mLock.unlinkToDeath(wakeLock, 0); + try { + wakeLock.mLock.unlinkToDeath(wakeLock, 0); + } catch (NoSuchElementException e) { + Slog.wtf(TAG, "Failed to unlink wakelock", e); + } removeWakeLockLocked(wakeLock, index); } } @@ -3180,6 +3194,7 @@ public final class PowerManagerService extends SystemService final boolean ready = mDisplayManagerInternal.requestPowerState(groupId, displayPowerRequest, mRequestWaitForNegativeProximity); + mNotifier.onScreenPolicyUpdate(displayPowerRequest.policy); if (DEBUG_SPEW) { Slog.d(TAG, "updateDisplayPowerStateLocked: displayReady=" + ready @@ -3313,6 +3328,7 @@ public final class PowerManagerService extends SystemService public void onProximityNegative() { synchronized (mLock) { mProximityPositive = false; + mInterceptedPowerKeyForProximity = false; mDirty |= DIRTY_PROXIMITY_POSITIVE; userActivityNoUpdateLocked(Display.DEFAULT_DISPLAY_GROUP, mClock.uptimeMillis(), PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, Process.SYSTEM_UID); @@ -4153,6 +4169,8 @@ public final class PowerManagerService extends SystemService } pw.println(); pw.println(" mRequestWaitForNegativeProximity=" + mRequestWaitForNegativeProximity); + pw.println(" mInterceptedPowerKeyForProximity=" + + mInterceptedPowerKeyForProximity); pw.println(" mSandmanScheduled=" + mSandmanScheduled); pw.println(" mBatteryLevelLow=" + mBatteryLevelLow); pw.println(" mLightDeviceIdleMode=" + mLightDeviceIdleMode); @@ -5984,8 +6002,9 @@ public final class PowerManagerService extends SystemService final DisplayPowerRequest displayPowerRequest = mDisplayGroupPowerStateMapper.getPowerRequestLocked( Display.DEFAULT_DISPLAY_GROUP); - if (displayPowerRequest.useProximitySensor && mProximityPositive) { + if (mProximityPositive && !mInterceptedPowerKeyForProximity) { mDisplayManagerInternal.ignoreProximitySensorUntilChanged(); + mInterceptedPowerKeyForProximity = true; return true; } } diff --git a/services/core/java/com/android/server/power/ScreenUndimDetector.java b/services/core/java/com/android/server/power/ScreenUndimDetector.java new file mode 100644 index 000000000000..951bc1f76e6d --- /dev/null +++ b/services/core/java/com/android/server/power/ScreenUndimDetector.java @@ -0,0 +1,297 @@ +/* + * 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.power; + +import static android.hardware.display.DisplayManagerInternal.DisplayPowerRequest.POLICY_BRIGHT; +import static android.hardware.display.DisplayManagerInternal.DisplayPowerRequest.POLICY_DIM; +import static android.hardware.display.DisplayManagerInternal.DisplayPowerRequest.POLICY_DOZE; +import static android.hardware.display.DisplayManagerInternal.DisplayPowerRequest.POLICY_OFF; +import static android.provider.DeviceConfig.NAMESPACE_ATTENTION_MANAGER_SERVICE; + +import android.annotation.NonNull; +import android.content.Context; +import android.os.PowerManager; +import android.os.SystemClock; +import android.provider.DeviceConfig; +import android.util.Slog; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.FrameworkStatsLog; + +import java.util.Set; +import java.util.concurrent.TimeUnit; + +/** + * Detects when user manually undims the screen (x times) and acquires a wakelock to keep the screen + * on temporarily (without changing the screen timeout setting). + */ +public class ScreenUndimDetector { + private static final String TAG = "ScreenUndimDetector"; + private static final boolean DEBUG = false; + + private static final String UNDIM_DETECTOR_WAKE_LOCK = "UndimDetectorWakeLock"; + + /** DeviceConfig flag: is keep screen on feature enabled. */ + static final String KEY_KEEP_SCREEN_ON_ENABLED = "keep_screen_on_enabled"; + private static final boolean DEFAULT_KEEP_SCREEN_ON_ENABLED = true; + private static final int OUTCOME_POWER_BUTTON = + FrameworkStatsLog.TIMEOUT_AUTO_EXTENDED_REPORTED__OUTCOME__POWER_BUTTON; + private static final int OUTCOME_TIMEOUT = + FrameworkStatsLog.TIMEOUT_AUTO_EXTENDED_REPORTED__OUTCOME__TIMEOUT; + private boolean mKeepScreenOnEnabled; + + /** DeviceConfig flag: how long should we keep the screen on. */ + @VisibleForTesting + static final String KEY_KEEP_SCREEN_ON_FOR_MILLIS = "keep_screen_on_for_millis"; + @VisibleForTesting + static final long DEFAULT_KEEP_SCREEN_ON_FOR_MILLIS = TimeUnit.MINUTES.toMillis(10); + private long mKeepScreenOnForMillis; + + /** DeviceConfig flag: how many user undims required to trigger keeping the screen on. */ + @VisibleForTesting + static final String KEY_UNDIMS_REQUIRED = "undims_required"; + @VisibleForTesting + static final int DEFAULT_UNDIMS_REQUIRED = 2; + private int mUndimsRequired; + + /** + * DeviceConfig flag: what is the maximum duration between undims to still consider them + * consecutive. + */ + @VisibleForTesting + static final String KEY_MAX_DURATION_BETWEEN_UNDIMS_MILLIS = + "max_duration_between_undims_millis"; + @VisibleForTesting + static final long DEFAULT_MAX_DURATION_BETWEEN_UNDIMS_MILLIS = TimeUnit.MINUTES.toMillis(5); + private long mMaxDurationBetweenUndimsMillis; + + @VisibleForTesting + PowerManager.WakeLock mWakeLock; + + @VisibleForTesting + int mCurrentScreenPolicy; + @VisibleForTesting + int mUndimCounter = 0; + @VisibleForTesting + long mUndimCounterStartedMillis; + private long mUndimOccurredTime = -1; + private long mInteractionAfterUndimTime = -1; + private InternalClock mClock; + + public ScreenUndimDetector() { + mClock = new InternalClock(); + } + + ScreenUndimDetector(InternalClock clock) { + mClock = clock; + } + + static class InternalClock { + public long getCurrentTime() { + return SystemClock.elapsedRealtime(); + } + } + + /** Should be called in parent's systemReady() */ + public void systemReady(Context context) { + readValuesFromDeviceConfig(); + DeviceConfig.addOnPropertiesChangedListener(NAMESPACE_ATTENTION_MANAGER_SERVICE, + context.getMainExecutor(), + (properties) -> onDeviceConfigChange(properties.getKeyset())); + + final PowerManager powerManager = context.getSystemService(PowerManager.class); + mWakeLock = powerManager.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK + | PowerManager.ON_AFTER_RELEASE, + UNDIM_DETECTOR_WAKE_LOCK); + } + + /** + * Launches a message that figures out the screen transitions and detects user undims. Must be + * called by the parent that is trying to update the screen policy. + */ + public void recordScreenPolicy(int newPolicy) { + if (newPolicy == mCurrentScreenPolicy) { + return; + } + + if (DEBUG) { + Slog.d(TAG, + "Screen policy transition: " + mCurrentScreenPolicy + " -> " + newPolicy); + } + + // update the current policy with the new one immediately so we don't accidentally get + // into a loop (which is possible if the switch below triggers a new policy). + final int currentPolicy = mCurrentScreenPolicy; + mCurrentScreenPolicy = newPolicy; + + if (!mKeepScreenOnEnabled) { + return; + } + + switch (currentPolicy) { + case POLICY_DIM: + if (newPolicy == POLICY_BRIGHT) { + final long now = mClock.getCurrentTime(); + final long timeElapsedSinceFirstUndim = now - mUndimCounterStartedMillis; + if (timeElapsedSinceFirstUndim >= mMaxDurationBetweenUndimsMillis) { + reset(); + } + if (mUndimCounter == 0) { + mUndimCounterStartedMillis = now; + } + + mUndimCounter++; + + if (DEBUG) { + Slog.d(TAG, "User undim, counter=" + mUndimCounter + + " (required=" + mUndimsRequired + ")" + + ", timeElapsedSinceFirstUndim=" + timeElapsedSinceFirstUndim + + " (max=" + mMaxDurationBetweenUndimsMillis + ")"); + } + if (mUndimCounter >= mUndimsRequired) { + reset(); + if (DEBUG) { + Slog.d(TAG, "Acquiring a wake lock for " + mKeepScreenOnForMillis); + } + if (mWakeLock != null) { + mUndimOccurredTime = mClock.getCurrentTime(); + mWakeLock.acquire(mKeepScreenOnForMillis); + } + } + } else { + if (newPolicy == POLICY_OFF || newPolicy == POLICY_DOZE) { + checkAndLogUndim(OUTCOME_TIMEOUT); + } + reset(); + } + break; + case POLICY_BRIGHT: + if (newPolicy == POLICY_OFF || newPolicy == POLICY_DOZE) { + checkAndLogUndim(OUTCOME_POWER_BUTTON); + } + if (newPolicy != POLICY_DIM) { + reset(); + } + break; + } + } + + @VisibleForTesting + void reset() { + if (DEBUG) { + Slog.d(TAG, "Resetting the undim detector"); + } + mUndimCounter = 0; + mUndimCounterStartedMillis = 0; + if (mWakeLock != null && mWakeLock.isHeld()) { + mWakeLock.release(); + } + } + + private boolean readKeepScreenOnNotificationEnabled() { + return DeviceConfig.getBoolean(NAMESPACE_ATTENTION_MANAGER_SERVICE, + KEY_KEEP_SCREEN_ON_ENABLED, + DEFAULT_KEEP_SCREEN_ON_ENABLED); + } + + private long readKeepScreenOnForMillis() { + return DeviceConfig.getLong(NAMESPACE_ATTENTION_MANAGER_SERVICE, + KEY_KEEP_SCREEN_ON_FOR_MILLIS, + DEFAULT_KEEP_SCREEN_ON_FOR_MILLIS); + } + + private int readUndimsRequired() { + int undimsRequired = DeviceConfig.getInt(NAMESPACE_ATTENTION_MANAGER_SERVICE, + KEY_UNDIMS_REQUIRED, + DEFAULT_UNDIMS_REQUIRED); + + if (undimsRequired < 1 || undimsRequired > 5) { + Slog.e(TAG, "Provided undimsRequired=" + undimsRequired + + " is not allowed [1, 5]; using the default=" + DEFAULT_UNDIMS_REQUIRED); + return DEFAULT_UNDIMS_REQUIRED; + } + + return undimsRequired; + } + + private long readMaxDurationBetweenUndimsMillis() { + return DeviceConfig.getLong(NAMESPACE_ATTENTION_MANAGER_SERVICE, + KEY_MAX_DURATION_BETWEEN_UNDIMS_MILLIS, + DEFAULT_MAX_DURATION_BETWEEN_UNDIMS_MILLIS); + } + + private void onDeviceConfigChange(@NonNull Set<String> keys) { + for (String key : keys) { + Slog.i(TAG, "onDeviceConfigChange; key=" + key); + switch (key) { + case KEY_KEEP_SCREEN_ON_ENABLED: + case KEY_KEEP_SCREEN_ON_FOR_MILLIS: + case KEY_UNDIMS_REQUIRED: + case KEY_MAX_DURATION_BETWEEN_UNDIMS_MILLIS: + readValuesFromDeviceConfig(); + return; + default: + Slog.i(TAG, "Ignoring change on " + key); + } + } + } + + @VisibleForTesting + void readValuesFromDeviceConfig() { + mKeepScreenOnEnabled = readKeepScreenOnNotificationEnabled(); + mKeepScreenOnForMillis = readKeepScreenOnForMillis(); + mUndimsRequired = readUndimsRequired(); + mMaxDurationBetweenUndimsMillis = readMaxDurationBetweenUndimsMillis(); + + Slog.i(TAG, "readValuesFromDeviceConfig():" + + "\nmKeepScreenOnForMillis=" + mKeepScreenOnForMillis + + "\nmKeepScreenOnNotificationEnabled=" + mKeepScreenOnEnabled + + "\nmUndimsRequired=" + mUndimsRequired); + + } + + /** + * The user interacted with the screen after an undim, indicating the phone is in use. + * We use this event for logging. + */ + public void userActivity() { + if (mUndimOccurredTime != 1 && mInteractionAfterUndimTime == -1) { + mInteractionAfterUndimTime = mClock.getCurrentTime(); + } + } + + /** + * Checks and logs if an undim occurred. + * + * A log will occur if an undim seems to have resulted in a timeout or a direct screen off such + * as from a power button. Some outcomes may not be correctly assigned to a + * TIMEOUT_AUTO_EXTENDED_REPORTED__OUTCOME value. + */ + private void checkAndLogUndim(int outcome) { + if (mUndimOccurredTime != -1) { + long now = mClock.getCurrentTime(); + FrameworkStatsLog.write(FrameworkStatsLog.TIMEOUT_AUTO_EXTENDED_REPORTED, + outcome, + /* time_to_outcome_millis=*/ now - mUndimOccurredTime, + /* time_to_first_interaction_millis= */ + mInteractionAfterUndimTime != -1 ? now - mInteractionAfterUndimTime : -1 + ); + mUndimOccurredTime = -1; + mInteractionAfterUndimTime = -1; + } + } +} diff --git a/services/core/java/com/android/server/powerstats/PowerStatsDataStorage.java b/services/core/java/com/android/server/powerstats/PowerStatsDataStorage.java index 2a95416747a6..06253a08d937 100644 --- a/services/core/java/com/android/server/powerstats/PowerStatsDataStorage.java +++ b/services/core/java/com/android/server/powerstats/PowerStatsDataStorage.java @@ -124,12 +124,8 @@ public class PowerStatsDataStorage { @Override public void read(InputStream in) throws IOException { while (in.available() > 0) { - try { - DataElement dataElement = new DataElement(in); - mCallback.onReadDataElement(dataElement.getData()); - } catch (IOException e) { - Slog.e(TAG, "Failed to read from storage. " + e.getMessage()); - } + DataElement dataElement = new DataElement(in); + mCallback.onReadDataElement(dataElement.getData()); } } } diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java index 771332071756..80bc16a9b052 100644 --- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java +++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java @@ -16,6 +16,7 @@ package com.android.server.wallpaper; +import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL; import static android.app.WallpaperManager.COMMAND_REAPPLY; import static android.app.WallpaperManager.FLAG_LOCK; import static android.app.WallpaperManager.FLAG_SYSTEM; @@ -2045,7 +2046,21 @@ public class WallpaperManagerService extends IWallpaperManager.Stub } } + private boolean hasCrossUserPermission() { + final int interactPermission = + mContext.checkCallingPermission(INTERACT_ACROSS_USERS_FULL); + return interactPermission == PackageManager.PERMISSION_GRANTED; + } + + @Override public boolean hasNamedWallpaper(String name) { + final int callingUser = UserHandle.getCallingUserId(); + final boolean allowCrossUser = hasCrossUserPermission(); + if (DEBUG) { + Slog.d(TAG, "hasNamedWallpaper() caller " + Binder.getCallingUid() + + " cross-user?: " + allowCrossUser); + } + synchronized (mLock) { List<UserInfo> users; final long ident = Binder.clearCallingIdentity(); @@ -2055,6 +2070,11 @@ public class WallpaperManagerService extends IWallpaperManager.Stub Binder.restoreCallingIdentity(ident); } for (UserInfo user: users) { + if (!allowCrossUser && callingUser != user.id) { + // No cross-user information for callers without permission + continue; + } + // ignore managed profiles if (user.isManagedProfile()) { continue; diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java index 54d97eea1521..a4d2348d03f1 100644 --- a/services/core/java/com/android/server/wm/AccessibilityController.java +++ b/services/core/java/com/android/server/wm/AccessibilityController.java @@ -88,6 +88,7 @@ import android.view.SurfaceControl; import android.view.ViewConfiguration; import android.view.WindowInfo; import android.view.WindowManager; +import android.view.WindowManagerPolicyConstants; import android.view.animation.DecelerateInterpolator; import android.view.animation.Interpolator; @@ -755,6 +756,7 @@ final class AccessibilityController { if (magnifying) { switch (transition) { case WindowManager.TRANSIT_OLD_ACTIVITY_OPEN: + case WindowManager.TRANSIT_OLD_TASK_FRAGMENT_OPEN: case WindowManager.TRANSIT_OLD_TASK_OPEN: case WindowManager.TRANSIT_OLD_TASK_TO_FRONT: case WindowManager.TRANSIT_OLD_WALLPAPER_OPEN: @@ -1200,7 +1202,7 @@ final class AccessibilityController { final SurfaceControl.Transaction t = mService.mTransactionFactory.get(); final int layer = mService.mPolicy.getWindowLayerFromTypeLw(TYPE_MAGNIFICATION_OVERLAY) * - WindowManagerService.TYPE_LAYER_MULTIPLIER; + WindowManagerPolicyConstants.TYPE_LAYER_MULTIPLIER; t.setLayer(mSurfaceControl, layer).setPosition(mSurfaceControl, 0, 0); InputMonitor.setTrustedOverlayInputInfo(mSurfaceControl, t, mDisplayContent.getDisplayId(), "Magnification Overlay"); diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java index 675c7eb23f2c..cc0db1d12e7b 100644 --- a/services/core/java/com/android/server/wm/ActivityClientController.java +++ b/services/core/java/com/android/server/wm/ActivityClientController.java @@ -21,6 +21,8 @@ import static android.app.ActivityTaskManager.INVALID_TASK_ID; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; +import static android.os.Process.INVALID_UID; +import static android.os.Process.SYSTEM_UID; import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; import static android.service.voice.VoiceInteractionSession.SHOW_SOURCE_APPLICATION; import static android.view.Display.DEFAULT_DISPLAY; @@ -57,6 +59,7 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.pm.ActivityInfo; +import android.content.pm.PackageManagerInternal; import android.content.pm.ParceledListSlice; import android.content.pm.ResolveInfo; import android.content.res.Configuration; @@ -68,6 +71,7 @@ import android.os.PersistableBundle; import android.os.RemoteException; import android.os.SystemClock; import android.os.Trace; +import android.os.UserHandle; import android.service.voice.VoiceInteractionManagerInternal; import android.util.Slog; import android.view.RemoteAnimationDefinition; @@ -79,6 +83,7 @@ import com.android.internal.policy.IKeyguardDismissCallback; import com.android.internal.protolog.common.ProtoLog; import com.android.server.LocalServices; import com.android.server.Watchdog; +import com.android.server.pm.parsing.pkg.AndroidPackage; import com.android.server.uri.NeededUriGrants; import com.android.server.vr.VrManagerInternal; @@ -549,7 +554,9 @@ class ActivityClientController extends IActivityClientController.Stub { if (ar == null) { return null; } - final ActivityRecord below = ar.getTask().getActivityBelow(ar); + // Exclude finishing activity. + final ActivityRecord below = ar.getTask().getActivity((r) -> !r.finishing, + ar, false /*includeBoundary*/, true /*traverseTopToBottom*/); if (below != null && below.getUid() == ar.getUid()) { return below.appToken.asBinder(); } @@ -583,20 +590,45 @@ class ActivityClientController extends IActivityClientController.Stub { @Override public int getLaunchedFromUid(IBinder token) { + if (!canGetLaunchedFrom()) { + return INVALID_UID; + } synchronized (mGlobalLock) { final ActivityRecord r = ActivityRecord.forTokenLocked(token); - return r != null ? r.launchedFromUid : android.os.Process.INVALID_UID; + return r != null ? r.launchedFromUid : INVALID_UID; } } @Override public String getLaunchedFromPackage(IBinder token) { + if (!canGetLaunchedFrom()) { + return null; + } synchronized (mGlobalLock) { final ActivityRecord r = ActivityRecord.forTokenLocked(token); return r != null ? r.launchedFromPackage : null; } } + /** Whether the caller can get the package or uid that launched its activity. */ + private boolean canGetLaunchedFrom() { + final int uid = Binder.getCallingUid(); + if (UserHandle.getAppId(uid) == SYSTEM_UID) { + return true; + } + final PackageManagerInternal pm = mService.mWindowManager.mPmInternal; + final AndroidPackage callingPkg = pm.getPackage(uid); + if (callingPkg == null) { + return false; + } + if (callingPkg.isSignedWithPlatformKey()) { + return true; + } + final String[] installerNames = pm.getKnownPackageNames( + PackageManagerInternal.PACKAGE_INSTALLER, UserHandle.getUserId(uid)); + return installerNames.length > 0 && callingPkg.getPackageName().equals(installerNames[0]); + } + @Override public void setRequestedOrientation(IBinder token, int requestedOrientation) { final long origId = Binder.clearCallingIdentity(); diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java index 20958aa1c9b1..73ef385618dc 100644 --- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java +++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java @@ -187,6 +187,10 @@ class ActivityMetricsLogger { return mAssociatedTransitionInfo != null && mAssociatedTransitionInfo.allDrawn(); } + boolean hasActiveTransitionInfo() { + return mAssociatedTransitionInfo != null; + } + boolean contains(ActivityRecord r) { return mAssociatedTransitionInfo != null && mAssociatedTransitionInfo.contains(r); } @@ -336,10 +340,11 @@ class ActivityMetricsLogger { } /** Only keep the records which can be drawn. */ - void updatePendingDraw() { + void updatePendingDraw(boolean keepInitializing) { for (int i = mPendingDrawActivities.size() - 1; i >= 0; i--) { final ActivityRecord r = mPendingDrawActivities.get(i); - if (!r.mVisibleRequested) { + if (!r.mVisibleRequested + && !(keepInitializing && r.isState(ActivityRecord.State.INITIALIZING))) { if (DEBUG_METRICS) Slog.i(TAG, "Discard pending draw " + r); mPendingDrawActivities.remove(i); } @@ -663,7 +668,7 @@ class ActivityMetricsLogger { // visible such as after the top task is finished. for (int i = mTransitionInfoList.size() - 2; i >= 0; i--) { final TransitionInfo prevInfo = mTransitionInfoList.get(i); - prevInfo.updatePendingDraw(); + prevInfo.updatePendingDraw(false /* keepInitializing */); if (prevInfo.allDrawn()) { abort(prevInfo, "nothing will be drawn"); } @@ -698,6 +703,7 @@ class ActivityMetricsLogger { // Always calculate the delay because the caller may need to know the individual drawn time. info.mWindowsDrawnDelayMs = info.calculateDelay(timestampNs); info.removePendingDrawActivity(r); + info.updatePendingDraw(false /* keepInitializing */); final TransitionInfoSnapshot infoSnapshot = new TransitionInfoSnapshot(info); if (info.mLoggedTransitionStarting && info.allDrawn()) { done(false /* abort */, info, "notifyWindowsDrawn - all windows drawn", timestampNs); @@ -749,7 +755,9 @@ class ActivityMetricsLogger { info.mCurrentTransitionDelayMs = info.calculateDelay(timestampNs); info.mReason = activityToReason.valueAt(index); info.mLoggedTransitionStarting = true; - info.updatePendingDraw(); + // Do not remove activity in initializing state because the transition may be started + // by starting window. The initializing activity may be requested to visible soon. + info.updatePendingDraw(true /* keepInitializing */); if (info.allDrawn()) { done(false /* abort */, info, "notifyTransitionStarting - all windows drawn", timestampNs); diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 54c660e93c42..dc6f5b62a6c8 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -676,6 +676,12 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A boolean mLastImeShown; /** + * When set to true, the IME insets will be frozen until the next app becomes IME input target. + * @see InsetsPolicy#adjustVisibilityForIme + */ + boolean mImeInsetsFrozenUntilStartInput; + + /** * A flag to determine if this AR is in the process of closing or entering PIP. This is needed * to help AR know that the app is in the process of closing but hasn't yet started closing on * the WM side. @@ -1460,6 +1466,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A associateStartingDataWithTask(); overrideConfigurationPropagation(mStartingWindow, task); } + mImeInsetsFrozenUntilStartInput = false; } if (rootTask != null && rootTask.topRunningActivity() == this) { @@ -3234,6 +3241,20 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // TODO(b/137329632): find the next activity directly underneath this one, not just anywhere final ActivityRecord next = getDisplayArea().topRunningActivity( true /* considerKeyguardState */); + + // If the finishing activity is the last activity of a organized TaskFragment and has an + // adjacent TaskFragment, check if the activity removal should be delayed. + boolean delayRemoval = false; + final TaskFragment taskFragment = getTaskFragment(); + if (next != null && taskFragment != null && taskFragment.isEmbedded()) { + final TaskFragment organized = taskFragment.getOrganizedTaskFragment(); + final TaskFragment adjacent = + organized != null ? organized.getAdjacentTaskFragment() : null; + if (adjacent != null && organized.topRunningActivity() == null) { + delayRemoval = organized.isDelayLastActivityRemoval(); + } + } + // isNextNotYetVisible is to check if the next activity is invisible, or it has been // requested to be invisible but its windows haven't reported as invisible. If so, it // implied that the current finishing activity should be added into stopping list rather @@ -3248,7 +3269,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } if (isCurrentVisible) { - if (isNextNotYetVisible) { + if (isNextNotYetVisible || delayRemoval) { // Add this activity to the list of stopping activities. It will be processed and // destroyed when the next activity reports idle. addToStopping(false /* scheduleIdle */, false /* idleDelayed */, @@ -3669,6 +3690,16 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } cleanUp(true /* cleanServices */, true /* setState */); if (remove) { + if (mStartingData != null && mVisible && task != null) { + // A corner case that the app terminates its trampoline activity on a separated + // process by killing itself. Transfer the starting window to the next activity + // which will be visible, so the dead activity can be removed immediately (no + // longer animating) and the reveal animation can play normally on next activity. + final ActivityRecord top = task.topRunningActivity(); + if (top != null && !top.mVisible && top.shouldBeVisible()) { + top.transferStartingWindow(this); + } + } removeFromHistory("appDied"); } } @@ -3904,7 +3935,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A if (tStartingWindow != null && fromActivity.mStartingSurface != null) { // In this case, the starting icon has already been displayed, so start // letting windows get shown immediately without any more transitions. - getDisplayContent().mSkipAppTransitionAnimation = true; + if (fromActivity.mVisible) { + mDisplayContent.mSkipAppTransitionAnimation = true; + } ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Moving existing starting %s" + " from %s to %s", tStartingWindow, fromActivity, this); @@ -4914,6 +4947,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A && imeInputTarget.getWindow().mActivityRecord == this && mDisplayContent.mInputMethodWindow != null && mDisplayContent.mInputMethodWindow.isVisible(); + mImeInsetsFrozenUntilStartInput = true; } final DisplayContent displayContent = getDisplayContent(); @@ -6045,6 +6079,14 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // closing activity having to wait until idle timeout to be stopped or destroyed if the // next activity won't report idle (e.g. repeated view animation). mTaskSupervisor.scheduleProcessStoppingAndFinishingActivitiesIfNeeded(); + + // If the activity is visible, but no windows are eligible to start input, unfreeze + // to avoid permanently frozen IME insets. + if (mImeInsetsFrozenUntilStartInput && getWindow( + win -> WindowManager.LayoutParams.mayUseInputMethod(win.mAttrs.flags)) + == null) { + mImeInsetsFrozenUntilStartInput = false; + } } } @@ -7066,7 +7108,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A return; } - mDisplayContent.mPinnedTaskController.onCancelFixedRotationTransform(task); + mDisplayContent.mPinnedTaskController.onCancelFixedRotationTransform(); // Perform rotation animation according to the rotation of this activity. startFreezingScreen(originalDisplayRotation); // This activity may relaunch or perform configuration change so once it has reported drawn, @@ -8029,6 +8071,13 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } } + @Override + void onResize() { + // Reset freezing IME insets flag when the activity resized. + mImeInsetsFrozenUntilStartInput = false; + super.onResize(); + } + /** Returns true if the configuration is compatible with this activity. */ boolean isConfigurationCompatible(Configuration config) { final int orientation = getRequestedOrientation(); diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index 3bc6435ae017..f6757f599d7c 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -1509,7 +1509,7 @@ class ActivityStarter { final Task targetTask = r.getTask() != null ? r.getTask() : mTargetTask; - if (startedActivityRootTask == null || targetTask == null) { + if (startedActivityRootTask == null || targetTask == null || !targetTask.isAttached()) { return; } diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java index 50ac4330facd..e593c1c1e656 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java +++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java @@ -2596,7 +2596,10 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { } boolean matches(ActivityRecord r) { - return mTargetComponent.equals(r.mActivityComponent) || mLaunchingState.contains(r); + if (!mLaunchingState.hasActiveTransitionInfo()) { + return mTargetComponent.equals(r.mActivityComponent); + } + return mLaunchingState.contains(r); } void dump(PrintWriter pw, String prefix) { diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java index 174b3965f11a..6aea848b12f7 100644 --- a/services/core/java/com/android/server/wm/AppTransition.java +++ b/services/core/java/com/android/server/wm/AppTransition.java @@ -40,6 +40,8 @@ import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_UNOCCLUDE; import static android.view.WindowManager.TRANSIT_OLD_NONE; import static android.view.WindowManager.TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE; import static android.view.WindowManager.TRANSIT_OLD_TASK_CLOSE; +import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_CLOSE; +import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_OPEN; import static android.view.WindowManager.TRANSIT_OLD_TASK_OPEN; import static android.view.WindowManager.TRANSIT_OLD_TASK_OPEN_BEHIND; import static android.view.WindowManager.TRANSIT_OLD_TASK_TO_BACK; @@ -1005,6 +1007,21 @@ public class AppTransition implements Dump { animAttr = enter ? WindowAnimation_launchTaskBehindSourceAnimation : WindowAnimation_launchTaskBehindTargetAnimation; + break; + // TODO(b/189386466): Use activity transition as the fallback. Investigate if we + // need new TaskFragment transition. + case TRANSIT_OLD_TASK_FRAGMENT_OPEN: + animAttr = enter + ? WindowAnimation_activityOpenEnterAnimation + : WindowAnimation_activityOpenExitAnimation; + break; + // TODO(b/189386466): Use activity transition as the fallback. Investigate if we + // need new TaskFragment transition. + case TRANSIT_OLD_TASK_FRAGMENT_CLOSE: + animAttr = enter + ? WindowAnimation_activityCloseEnterAnimation + : WindowAnimation_activityCloseExitAnimation; + break; } a = animAttr != 0 ? loadAnimationAttr(lp, animAttr, transit) : null; ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM, @@ -1315,6 +1332,12 @@ public class AppTransition implements Dump { case TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE: { return "TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE"; } + case TRANSIT_OLD_TASK_FRAGMENT_OPEN: { + return "TRANSIT_OLD_TASK_FRAGMENT_OPEN"; + } + case TRANSIT_OLD_TASK_FRAGMENT_CLOSE: { + return "TRANSIT_OLD_TASK_FRAGMENT_CLOSE"; + } default: { return "<UNKNOWN: " + transition + ">"; } diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java index d6b0086d8dd9..eeadabde1476 100644 --- a/services/core/java/com/android/server/wm/AppTransitionController.java +++ b/services/core/java/com/android/server/wm/AppTransitionController.java @@ -39,6 +39,8 @@ import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_UNOCCLUDE; import static android.view.WindowManager.TRANSIT_OLD_NONE; import static android.view.WindowManager.TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE; import static android.view.WindowManager.TRANSIT_OLD_TASK_CLOSE; +import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_CLOSE; +import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_OPEN; import static android.view.WindowManager.TRANSIT_OLD_TASK_OPEN; import static android.view.WindowManager.TRANSIT_OLD_TASK_OPEN_BEHIND; import static android.view.WindowManager.TRANSIT_OLD_TASK_TO_BACK; @@ -68,6 +70,7 @@ import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACT import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; +import android.annotation.IntDef; import android.annotation.Nullable; import android.os.Trace; import android.util.ArrayMap; @@ -86,6 +89,8 @@ import android.view.animation.Animation; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.protolog.common.ProtoLog; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.LinkedList; import java.util.function.Predicate; @@ -102,6 +107,20 @@ public class AppTransitionController { private RemoteAnimationDefinition mRemoteAnimationDefinition = null; private static final int KEYGUARD_GOING_AWAY_ANIMATION_DURATION = 400; + private static final int TYPE_NONE = 0; + private static final int TYPE_ACTIVITY = 1; + private static final int TYPE_TASK_FRAGMENT = 2; + private static final int TYPE_TASK = 3; + + @IntDef(prefix = { "TYPE_" }, value = { + TYPE_NONE, + TYPE_ACTIVITY, + TYPE_TASK_FRAGMENT, + TYPE_TASK + }) + @Retention(RetentionPolicy.SOURCE) + @interface TransitContainerType {} + private final ArrayMap<WindowContainer, Integer> mTempTransitionReasons = new ArrayMap<>(); AppTransitionController(WindowManagerService service, DisplayContent displayContent) { @@ -387,33 +406,38 @@ public class AppTransitionController { openingApps, closingApps, true /* visible */); final ArraySet<WindowContainer> closingWcs = getAnimationTargets( openingApps, closingApps, false /* visible */); - final boolean isActivityOpening = !openingWcs.isEmpty() - && openingWcs.valueAt(0).asActivityRecord() != null; - final boolean isActivityClosing = !closingWcs.isEmpty() - && closingWcs.valueAt(0).asActivityRecord() != null; - final boolean isTaskOpening = !openingWcs.isEmpty() && !isActivityOpening; - final boolean isTaskClosing = !closingWcs.isEmpty() && !isActivityClosing; - - if (appTransition.containsTransitRequest(TRANSIT_TO_FRONT) && isTaskOpening) { + final WindowContainer<?> openingContainer = !openingWcs.isEmpty() + ? openingWcs.valueAt(0) : null; + final WindowContainer<?> closingContainer = !closingWcs.isEmpty() + ? closingWcs.valueAt(0) : null; + @TransitContainerType int openingType = getTransitContainerType(openingContainer); + @TransitContainerType int closingType = getTransitContainerType(closingContainer); + if (appTransition.containsTransitRequest(TRANSIT_TO_FRONT) && openingType == TYPE_TASK) { return TRANSIT_OLD_TASK_TO_FRONT; } - if (appTransition.containsTransitRequest(TRANSIT_TO_BACK) && isTaskClosing) { + if (appTransition.containsTransitRequest(TRANSIT_TO_BACK) && closingType == TYPE_TASK) { return TRANSIT_OLD_TASK_TO_BACK; } if (appTransition.containsTransitRequest(TRANSIT_OPEN)) { - if (isTaskOpening) { + if (openingType == TYPE_TASK) { return (appTransition.getTransitFlags() & TRANSIT_FLAG_OPEN_BEHIND) != 0 ? TRANSIT_OLD_TASK_OPEN_BEHIND : TRANSIT_OLD_TASK_OPEN; } - if (isActivityOpening) { + if (openingType == TYPE_ACTIVITY) { return TRANSIT_OLD_ACTIVITY_OPEN; } + if (openingType == TYPE_TASK_FRAGMENT) { + return TRANSIT_OLD_TASK_FRAGMENT_OPEN; + } } if (appTransition.containsTransitRequest(TRANSIT_CLOSE)) { - if (isTaskClosing) { + if (closingType == TYPE_TASK) { return TRANSIT_OLD_TASK_CLOSE; } - if (isActivityClosing) { + if (closingType == TYPE_TASK_FRAGMENT) { + return TRANSIT_OLD_TASK_FRAGMENT_CLOSE; + } + if (closingType == TYPE_ACTIVITY) { for (int i = closingApps.size() - 1; i >= 0; i--) { if (closingApps.valueAt(i).visibleIgnoringKeyguard) { return TRANSIT_OLD_ACTIVITY_CLOSE; @@ -430,6 +454,23 @@ public class AppTransitionController { return TRANSIT_OLD_NONE; } + @TransitContainerType + private static int getTransitContainerType(@Nullable WindowContainer<?> container) { + if (container == null) { + return TYPE_NONE; + } + if (container.asTask() != null) { + return TYPE_TASK; + } + if (container.asTaskFragment() != null) { + return TYPE_TASK_FRAGMENT; + } + if (container.asActivityRecord() != null) { + return TYPE_ACTIVITY; + } + return TYPE_NONE; + } + private static WindowManager.LayoutParams getAnimLp(ActivityRecord activity) { final WindowState mainWindow = activity != null ? activity.findMainWindow() : null; return mainWindow != null ? mainWindow.mAttrs : null; diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index f5f2990c500d..74287c415f30 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -3830,6 +3830,23 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp return mWmService.mForceDesktopModeOnExternalDisplays && !isDefaultDisplay && !isPrivate(); } + /** @see WindowManagerInternal#onToggleImeRequested */ + void onShowImeRequested() { + if (mImeLayeringTarget == null || mInputMethodWindow == null) { + return; + } + // If IME window will be shown on the rotated activity, share the transformed state to + // IME window so it can compute rotated frame with rotated configuration. + if (mImeLayeringTarget.mToken.isFixedRotationTransforming()) { + mInputMethodWindow.mToken.linkFixedRotationTransform(mImeLayeringTarget.mToken); + // Hide the window until the rotation is done to avoid intermediate artifacts if the + // parent surface of IME container is changed. + if (mFadeRotationAnimationController != null) { + mFadeRotationAnimationController.hideImmediately(mInputMethodWindow.mToken); + } + } + } + @VisibleForTesting void setImeLayeringTarget(WindowState target) { mImeLayeringTarget = target; @@ -4005,6 +4022,9 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp void updateImeInputAndControlTarget(WindowState target) { if (mImeInputTarget != target) { ProtoLog.i(WM_DEBUG_IME, "setInputMethodInputTarget %s", target); + if (target != null && target.mActivityRecord != null) { + target.mActivityRecord.mImeInsetsFrozenUntilStartInput = false; + } setImeInputTarget(target); updateImeControlTarget(); } diff --git a/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java b/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java index 45f401b52082..fed4f6205a3b 100644 --- a/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java +++ b/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java @@ -23,6 +23,8 @@ import static com.android.server.wm.Task.TAG_VISIBILITY; import android.annotation.Nullable; import android.util.Slog; +import java.util.ArrayList; + /** Helper class to ensure activities are in the right visible state for a container. */ class EnsureActivitiesVisibleHelper { private final TaskFragment mTaskFragment; @@ -98,17 +100,37 @@ class EnsureActivitiesVisibleHelper { && mTaskFragment.isTopActivityFocusable() && (starting == null || !starting.isDescendantOf(mTaskFragment)); + ArrayList<TaskFragment> adjacentTaskFragments = null; for (int i = mTaskFragment.mChildren.size() - 1; i >= 0; --i) { final WindowContainer child = mTaskFragment.mChildren.get(i); if (child.asTaskFragment() != null) { final TaskFragment childTaskFragment = child.asTaskFragment(); childTaskFragment.updateActivityVisibilities(starting, configChanges, preserveWindows, notifyClients); - mBehindFullyOccludedContainer = childTaskFragment.getBounds().equals( + mBehindFullyOccludedContainer |= childTaskFragment.getBounds().equals( mTaskFragment.getBounds()); if (mAboveTop && mTop.getTaskFragment() == childTaskFragment) { mAboveTop = false; } + + if (mBehindFullyOccludedContainer) { + continue; + } + + if (adjacentTaskFragments != null && adjacentTaskFragments.contains( + childTaskFragment)) { + // Everything behind two adjacent TaskFragments are occluded. + mBehindFullyOccludedContainer = true; + continue; + } + + final TaskFragment adjacentTaskFrag = childTaskFragment.getAdjacentTaskFragment(); + if (adjacentTaskFrag != null) { + if (adjacentTaskFragments == null) { + adjacentTaskFragments = new ArrayList<>(); + } + adjacentTaskFragments.add(adjacentTaskFrag); + } } else if (child.asActivityRecord() != null) { setActivityVisibilityState(child.asActivityRecord(), starting, resumeTopActivity); } diff --git a/services/core/java/com/android/server/wm/FadeRotationAnimationController.java b/services/core/java/com/android/server/wm/FadeRotationAnimationController.java index eab3f108d17a..52a7ac75e2dc 100644 --- a/services/core/java/com/android/server/wm/FadeRotationAnimationController.java +++ b/services/core/java/com/android/server/wm/FadeRotationAnimationController.java @@ -42,6 +42,9 @@ public class FadeRotationAnimationController extends FadeAnimationController { /** A runnable which gets called when the {@link #show()} is called. */ private Runnable mOnShowRunnable; + /** Whether to use constant zero alpha animation. */ + private boolean mHideImmediately; + public FadeRotationAnimationController(DisplayContent displayContent) { super(displayContent); mService = displayContent.mWmService; @@ -51,6 +54,10 @@ public class FadeRotationAnimationController extends FadeAnimationController { mService.mWindowPlacerLocked.performSurfacePlacement(); } } : null; + if (mFrozenTimeoutRunnable != null) { + // Hide the windows immediately because screen should have been covered by screenshot. + mHideImmediately = true; + } final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy(); final WindowState navigationBar = displayPolicy.getNavigationBar(); if (navigationBar != null) { @@ -120,6 +127,15 @@ public class FadeRotationAnimationController extends FadeAnimationController { } } + /** Hides the window immediately until it is drawn in new rotation. */ + void hideImmediately(WindowToken windowToken) { + final boolean original = mHideImmediately; + mHideImmediately = true; + mTargetWindowTokens.add(windowToken); + fadeWindowToken(false /* show */, windowToken, ANIMATION_TYPE_FIXED_TRANSFORM); + mHideImmediately = original; + } + /** Returns {@code true} if the window is handled by this controller. */ boolean isHandledToken(WindowToken token) { return token == mNavBarToken || isTargetToken(token); @@ -145,8 +161,7 @@ public class FadeRotationAnimationController extends FadeAnimationController { @Override public Animation getFadeOutAnimation() { - if (mFrozenTimeoutRunnable != null) { - // Hide the window immediately because screen should have been covered by screenshot. + if (mHideImmediately) { return new AlphaAnimation(0 /* fromAlpha */, 0 /* toAlpha */); } return super.getFadeOutAnimation(); diff --git a/services/core/java/com/android/server/wm/InputManagerCallback.java b/services/core/java/com/android/server/wm/InputManagerCallback.java index aa7e6c9c80fc..18a2c601f6d3 100644 --- a/services/core/java/com/android/server/wm/InputManagerCallback.java +++ b/services/core/java/com/android/server/wm/InputManagerCallback.java @@ -30,6 +30,7 @@ import android.util.Slog; import android.view.InputApplicationHandle; import android.view.KeyEvent; import android.view.WindowManager; +import android.view.WindowManagerPolicyConstants; import com.android.internal.util.function.pooled.PooledLambda; import com.android.server.input.InputManagerService; @@ -181,8 +182,8 @@ final class InputManagerCallback implements InputManagerService.WindowManagerCal @Override public int getPointerLayer() { return mService.mPolicy.getWindowLayerFromTypeLw(WindowManager.LayoutParams.TYPE_POINTER) - * WindowManagerService.TYPE_LAYER_MULTIPLIER - + WindowManagerService.TYPE_LAYER_OFFSET; + * WindowManagerPolicyConstants.TYPE_LAYER_MULTIPLIER + + WindowManagerPolicyConstants.TYPE_LAYER_OFFSET; } /** Callback to get pointer display id. */ diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java index d819557006a6..c8373df7155c 100644 --- a/services/core/java/com/android/server/wm/InputMonitor.java +++ b/services/core/java/com/android/server/wm/InputMonitor.java @@ -47,6 +47,7 @@ import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_INPUT; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; import static com.android.server.wm.WindowManagerService.LOGTAG_INPUT_FOCUS; +import android.annotation.Nullable; import android.graphics.Rect; import android.graphics.Region; import android.os.Handler; @@ -66,6 +67,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.protolog.common.ProtoLog; import java.io.PrintWriter; +import java.lang.ref.WeakReference; import java.util.Set; import java.util.function.Consumer; @@ -100,6 +102,15 @@ final class InputMonitor { private final ArrayMap<String, InputConsumerImpl> mInputConsumers = new ArrayMap(); /** + * Set when recents (overview) is active as part of a shell transition. While set, any focus + * going to the recents activity will be redirected to the Recents input consumer. Since we + * draw the live-tile above the recents activity, we also need to provide that activity as a + * z-layering reference so that we can place the recents input consumer above it. + */ + private WeakReference<ActivityRecord> mActiveRecentsActivity = null; + private WeakReference<ActivityRecord> mActiveRecentsLayerRef = null; + + /** * Representation of a input consumer that the policy has added to the window manager to consume * input events going to windows below it. */ @@ -278,6 +289,7 @@ final class InputMonitor { inputWindowHandle.setInputFeatures(w.mAttrs.inputFeatures); inputWindowHandle.setPaused(w.mActivityRecord != null && w.mActivityRecord.paused); inputWindowHandle.setVisible(w.isVisible()); + inputWindowHandle.setWindowToken(w.mClient); final boolean focusable = w.canReceiveKeys() && (mService.mPerDisplayFocusEnabled || mDisplayContent.isOnTop()); @@ -395,6 +407,21 @@ final class InputMonitor { } /** + * Inform InputMonitor when recents is active so it can enable the recents input consumer. + * @param activity The active recents activity. {@code null} means recents is not active. + * @param layer An activity whose Z-layer is used as a reference for how to sort the consumer. + */ + void setActiveRecents(@Nullable ActivityRecord activity, @Nullable ActivityRecord layer) { + final boolean clear = activity == null; + mActiveRecentsActivity = clear ? null : new WeakReference<>(activity); + mActiveRecentsLayerRef = clear ? null : new WeakReference<>(layer); + } + + private static <T> T getWeak(WeakReference<T> ref) { + return ref != null ? ref.get() : null; + } + + /** * Called when the current input focus changes. */ private void updateInputFocusRequest(InputConsumerImpl recentsAnimationInputConsumer) { @@ -404,8 +431,10 @@ final class InputMonitor { if (recentsAnimationInputConsumer != null && focus != null) { final RecentsAnimationController recentsAnimationController = mService.getRecentsAnimationController(); - final boolean shouldApplyRecentsInputConsumer = recentsAnimationController != null - && recentsAnimationController.shouldApplyInputConsumer(focus.mActivityRecord); + final boolean shouldApplyRecentsInputConsumer = (recentsAnimationController != null + && recentsAnimationController.shouldApplyInputConsumer(focus.mActivityRecord)) + // Shell transitions doesn't use RecentsAnimationController + || getWeak(mActiveRecentsActivity) != null; if (shouldApplyRecentsInputConsumer) { requestFocus(recentsAnimationInputConsumer.mWindowHandle.token, recentsAnimationInputConsumer.mName); @@ -505,6 +534,14 @@ final class InputMonitor { mInDrag = inDrag; resetInputConsumers(mInputTransaction); + // Update recents input consumer layer if active + if (mAddRecentsAnimationInputConsumerHandle + && getWeak(mActiveRecentsActivity) != null) { + final WindowContainer layer = getWeak(mActiveRecentsLayerRef); + mRecentsAnimationInputConsumer.show(mInputTransaction, + layer != null ? layer : getWeak(mActiveRecentsActivity)); + mAddRecentsAnimationInputConsumerHandle = false; + } mDisplayContent.forAllWindows(this, true /* traverseTopToBottom */); updateInputFocusRequest(mRecentsAnimationInputConsumer); @@ -539,11 +576,12 @@ final class InputMonitor { final int privateFlags = w.mAttrs.privateFlags; + // This only works for legacy transitions. if (mAddRecentsAnimationInputConsumerHandle && shouldApplyRecentsInputConsumer) { if (recentsAnimationController.updateInputConsumerForApp( mRecentsAnimationInputConsumer.mWindowHandle)) { mRecentsAnimationInputConsumer.show(mInputTransaction, - recentsAnimationController.getHighestLayerTask()); + recentsAnimationController.getHighestLayerActivity()); mAddRecentsAnimationInputConsumerHandle = false; } } diff --git a/services/core/java/com/android/server/wm/InputWindowHandleWrapper.java b/services/core/java/com/android/server/wm/InputWindowHandleWrapper.java index 7a4d13c2d697..0a24d3c69b17 100644 --- a/services/core/java/com/android/server/wm/InputWindowHandleWrapper.java +++ b/services/core/java/com/android/server/wm/InputWindowHandleWrapper.java @@ -20,6 +20,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.graphics.Region; import android.os.IBinder; +import android.view.IWindow; import android.view.InputApplicationHandle; import android.view.InputWindowHandle; import android.view.SurfaceControl; @@ -275,6 +276,14 @@ class InputWindowHandleWrapper { mChanged = true; } + void setWindowToken(IWindow windowToken) { + if (mHandle.getWindow() == windowToken) { + return; + } + mHandle.setWindowToken(windowToken); + mChanged = true; + } + @Override public String toString() { return mHandle + ", changed=" + mChanged; diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java index 0a837849d5e7..869d351df7b3 100644 --- a/services/core/java/com/android/server/wm/InsetsPolicy.java +++ b/services/core/java/com/android/server/wm/InsetsPolicy.java @@ -213,7 +213,7 @@ class InsetsPolicy { InsetsState getInsetsForWindow(WindowState target) { final InsetsState originalState = mStateController.getInsetsForWindow(target); final InsetsState state = adjustVisibilityForTransientTypes(originalState); - return target.mIsImWindow ? adjustVisibilityForIme(state, state == originalState) : state; + return adjustVisibilityForIme(target, state, state == originalState); } /** @@ -243,16 +243,37 @@ class InsetsPolicy { return state; } - // Navigation bar insets is always visible to IME. - private static InsetsState adjustVisibilityForIme(InsetsState originalState, + private InsetsState adjustVisibilityForIme(WindowState w, InsetsState originalState, boolean copyState) { - final InsetsSource originalNavSource = originalState.peekSource(ITYPE_NAVIGATION_BAR); - if (originalNavSource != null && !originalNavSource.isVisible()) { - final InsetsState state = copyState ? new InsetsState(originalState) : originalState; - final InsetsSource navSource = new InsetsSource(originalNavSource); - navSource.setVisible(true); - state.addSource(navSource); - return state; + if (w.mIsImWindow) { + // Navigation bar insets is always visible to IME. + final InsetsSource originalNavSource = originalState.peekSource(ITYPE_NAVIGATION_BAR); + if (originalNavSource != null && !originalNavSource.isVisible()) { + final InsetsState state = copyState ? new InsetsState(originalState) + : originalState; + final InsetsSource navSource = new InsetsSource(originalNavSource); + navSource.setVisible(true); + state.addSource(navSource); + return state; + } + } else if (w.mActivityRecord != null && w.mActivityRecord.mImeInsetsFrozenUntilStartInput) { + // During switching tasks with gestural navigation, if the IME is attached to + // one app window on that time, even the next app window is behind the IME window, + // conceptually the window should not receive the IME insets if the next window is + // not eligible IME requester and ready to show IME on top of it. + final boolean shouldImeAttachedToApp = mDisplayContent.shouldImeAttachedToApp(); + final InsetsSource originalImeSource = originalState.peekSource(ITYPE_IME); + + if (shouldImeAttachedToApp && originalImeSource != null) { + final boolean imeVisibility = + w.mActivityRecord.mLastImeShown || w.getRequestedVisibility(ITYPE_IME); + final InsetsState state = copyState ? new InsetsState(originalState) + : originalState; + final InsetsSource imeSource = new InsetsSource(originalImeSource); + imeSource.setVisible(imeVisibility); + state.addSource(imeSource); + return state; + } } return originalState; } diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java index d67d5fa7db4f..6f3edbcff4c0 100644 --- a/services/core/java/com/android/server/wm/KeyguardController.java +++ b/services/core/java/com/android/server/wm/KeyguardController.java @@ -20,6 +20,7 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; import static android.view.Display.DEFAULT_DISPLAY; +import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY; import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION; import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION; import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE; @@ -27,6 +28,7 @@ import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_W import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY; import static android.view.WindowManager.TRANSIT_KEYGUARD_OCCLUDE; import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE; +import static android.view.WindowManager.TRANSIT_TO_BACK; import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_NO_WINDOW_ANIMATIONS; import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_SUBTLE_WINDOW_ANIMATIONS; import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_TO_SHADE; @@ -235,8 +237,14 @@ class KeyguardController { mAodShowing ? 1 : 0, 1 /* keyguardGoingAway */, "keyguardGoingAway"); - mRootWindowContainer.getDefaultDisplay().requestTransitionAndLegacyPrepare( - TRANSIT_KEYGUARD_GOING_AWAY, convertTransitFlags(flags)); + final int transitFlags = convertTransitFlags(flags); + final DisplayContent dc = mRootWindowContainer.getDefaultDisplay(); + dc.prepareAppTransition(TRANSIT_KEYGUARD_GOING_AWAY, transitFlags); + // We are deprecating TRANSIT_KEYGUARD_GOING_AWAY for Shell transition and use + // TRANSIT_FLAG_KEYGUARD_GOING_AWAY to indicate that it should animate keyguard going + // away. + dc.mAtmService.getTransitionController().requestTransitionIfNeeded( + TRANSIT_TO_BACK, transitFlags, null /* trigger */, dc); updateKeyguardSleepToken(); // Some stack visibility might change (e.g. docked stack) @@ -276,7 +284,7 @@ class KeyguardController { } private int convertTransitFlags(int keyguardGoingAwayFlags) { - int result = 0; + int result = TRANSIT_FLAG_KEYGUARD_GOING_AWAY; if ((keyguardGoingAwayFlags & KEYGUARD_GOING_AWAY_FLAG_TO_SHADE) != 0) { result |= TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE; } diff --git a/services/core/java/com/android/server/wm/PinnedTaskController.java b/services/core/java/com/android/server/wm/PinnedTaskController.java index 4f7c9a4ccf1b..6014a87ae8f7 100644 --- a/services/core/java/com/android/server/wm/PinnedTaskController.java +++ b/services/core/java/com/android/server/wm/PinnedTaskController.java @@ -319,15 +319,11 @@ class PinnedTaskController { } /** Resets the states which were used to perform fixed rotation with PiP task. */ - void onCancelFixedRotationTransform(Task task) { + void onCancelFixedRotationTransform() { mFreezingTaskConfig = false; mDeferOrientationChanging = false; mDestRotatedBounds = null; mPipTransaction = null; - if (!task.isOrganized()) { - // Force clearing Task#mForceNotOrganized because the display didn't rotate. - task.onConfigurationChanged(task.getParent().getConfiguration()); - } } /** diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java index c457df9f6e86..d7dc3061f250 100644 --- a/services/core/java/com/android/server/wm/RecentsAnimationController.java +++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java @@ -1103,9 +1103,9 @@ public class RecentsAnimationController implements DeathRecipient { } /** - * Returns the task with the highest layer, or null if none is found. + * Returns the activity with the highest layer, or null if none is found. */ - public Task getHighestLayerTask() { + public ActivityRecord getHighestLayerActivity() { int highestLayer = Integer.MIN_VALUE; Task highestLayerTask = null; for (int i = mPendingAnimations.size() - 1; i >= 0; i--) { @@ -1116,7 +1116,7 @@ public class RecentsAnimationController implements DeathRecipient { highestLayerTask = adapter.mTask; } } - return highestLayerTask; + return highestLayerTask.getTopMostActivity(); } boolean isAnimatingTask(Task task) { diff --git a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java index 58363f2316ea..d440a14d6199 100644 --- a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java +++ b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java @@ -18,6 +18,7 @@ package com.android.server.wm; import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; import static android.util.RotationUtils.deltaRotation; +import static android.view.WindowManagerPolicyConstants.SCREEN_FREEZE_LAYER_BASE; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ORIENTATION; import static com.android.internal.protolog.ProtoLogGroup.WM_SHOW_SURFACE_ALLOC; @@ -30,8 +31,6 @@ import static com.android.server.wm.ScreenRotationAnimationProto.STARTED; import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_SCREEN_ROTATION; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; -import static com.android.server.wm.WindowManagerService.TYPE_LAYER_MULTIPLIER; -import static com.android.server.wm.WindowStateAnimator.WINDOW_FREEZE_LAYER; import android.animation.ArgbEvaluator; import android.content.Context; @@ -91,13 +90,6 @@ import java.io.PrintWriter; class ScreenRotationAnimation { private static final String TAG = TAG_WITH_CLASS_NAME ? "ScreenRotationAnimation" : TAG_WM; - /* - * Layers for screen rotation animation. We put these layers above - * WINDOW_FREEZE_LAYER so that screen freeze will cover all windows. - */ - private static final int SCREEN_FREEZE_LAYER_BASE = WINDOW_FREEZE_LAYER + TYPE_LAYER_MULTIPLIER; - private static final int SCREEN_FREEZE_LAYER_ENTER = SCREEN_FREEZE_LAYER_BASE; - private final Context mContext; private final DisplayContent mDisplayContent; private final float[] mTmpFloats = new float[9]; @@ -423,7 +415,7 @@ class ScreenRotationAnimation { finalWidth * 2, finalHeight * 2); Rect inner = new Rect(0, 0, finalWidth, finalHeight); mEnteringBlackFrame = new BlackFrame(mService.mTransactionFactory, t, outer, inner, - SCREEN_FREEZE_LAYER_ENTER, mDisplayContent, false, mEnterBlackFrameLayer); + SCREEN_FREEZE_LAYER_BASE, mDisplayContent, false, mEnterBlackFrameLayer); } catch (OutOfResourcesException e) { Slog.w(TAG, "Unable to allocate black surface", e); } diff --git a/services/core/java/com/android/server/wm/StrictModeFlash.java b/services/core/java/com/android/server/wm/StrictModeFlash.java index 48a7bdec2b94..cdf6b08b1c57 100644 --- a/services/core/java/com/android/server/wm/StrictModeFlash.java +++ b/services/core/java/com/android/server/wm/StrictModeFlash.java @@ -27,6 +27,7 @@ import android.graphics.Rect; import android.view.Surface; import android.view.Surface.OutOfResourcesException; import android.view.SurfaceControl; +import android.view.WindowManagerPolicyConstants; class StrictModeFlash { private static final String TAG = TAG_WITH_CLASS_NAME ? "StrictModeFlash" : TAG_WM; @@ -52,7 +53,7 @@ class StrictModeFlash { .build(); // one more than Watermark? arbitrary. - t.setLayer(ctrl, WindowManagerService.TYPE_LAYER_MULTIPLIER * 101); + t.setLayer(ctrl, WindowManagerPolicyConstants.STRICT_MODE_LAYER); t.setPosition(ctrl, 0, 0); t.show(ctrl); // Ensure we aren't considered as obscuring for Input purposes. diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 04b6d470467a..73dda7476155 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -590,16 +590,6 @@ class Task extends TaskFragment { // false. private boolean mDeferTaskAppear; - /** - * Forces this task to be unorganized. Currently it is used for deferring the control of - * organizer when windowing mode is changing from PiP to fullscreen with orientation change. - * It is true only during Task#setWindowingMode ~ DisplayRotation#continueRotation. - * - * TODO(b/179235349): Remove this field by making surface operations from task organizer sync - * with display rotation. - */ - private boolean mForceNotOrganized; - // Tracking cookie for the creation of this task. IBinder mLaunchCookie; @@ -1932,19 +1922,16 @@ class Task extends TaskFragment { } } - if (pipChanging) { - // If the top activity is using fixed rotation, it should be changing from PiP to - // fullscreen with display orientation change. Do not notify fullscreen task organizer - // because the restoration of task surface and the transformation of activity surface - // need to be done synchronously. + if (pipChanging && wasInPictureInPicture) { + // If the top activity is changing from PiP to fullscreen with fixed rotation, + // clear the crop and rotation matrix of task because fixed rotation will handle + // the transformation on activity level. This also avoids flickering caused by the + // latency of fullscreen task organizer configuring the surface. final ActivityRecord r = topRunningActivity(); if (r != null && mDisplayContent.isFixedRotationLaunchingApp(r)) { - mForceNotOrganized = true; + getSyncTransaction().setWindowCrop(mSurfaceControl, null) + .setMatrix(mSurfaceControl, Matrix.IDENTITY_MATRIX, new float[9]); } - } else { - // If the display orientation change is done, let the corresponding task organizer take - // back the control of this task. - mForceNotOrganized = false; } saveLaunchingStateIfNeeded(); @@ -3114,10 +3101,6 @@ class Task extends TaskFragment { }); } - boolean isFocusableAndVisible() { - return isTopActivityFocusable() && shouldBeVisible(null /* starting */); - } - void positionChildAtTop(ActivityRecord child) { positionChildAt(child, POSITION_TOP); } @@ -3676,9 +3659,6 @@ class Task extends TaskFragment { pw.print(" isResizeable="); pw.println(isResizeable()); pw.print(prefix); pw.print("lastActiveTime="); pw.print(lastActiveTime); pw.println(" (inactive for " + (getInactiveDuration() / 1000) + "s)"); - if (mForceNotOrganized) { - pw.print(prefix); pw.println("mForceNotOrganized=true"); - } } @Override @@ -4154,9 +4134,6 @@ class Task extends TaskFragment { } private boolean canBeOrganized() { - if (mForceNotOrganized) { - return false; - } // All root tasks can be organized if (isRootTask()) { return true; @@ -4309,7 +4286,6 @@ class Task extends TaskFragment { return setTaskOrganizer(null); } - final int windowingMode = getWindowingMode(); final TaskOrganizerController controller = mWmService.mAtmService.mTaskOrganizerController; final ITaskOrganizer organizer = controller.getTaskOrganizer(); if (!forceUpdate && mTaskOrganizer == organizer) { @@ -5037,7 +5013,10 @@ class Task extends TaskFragment { if (topFragment == f) { return; } - + if (!f.isFocusableAndVisible()) { + // No need to resume activity in TaskFragment that is not visible. + return; + } resumed[0] |= f.resumeTopActivity(prev, options, deferPause); }, true); return resumed[0]; diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java index cfad93692f09..c7ca180bfa14 100644 --- a/services/core/java/com/android/server/wm/TaskDisplayArea.java +++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java @@ -32,6 +32,7 @@ import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; +import static android.view.WindowManagerPolicyConstants.SPLIT_DIVIDER_LAYER; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ORIENTATION; import static com.android.server.wm.ActivityRecord.State.RESUMED; @@ -112,7 +113,6 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> { private final ArrayList<WindowContainer> mTmpNormalChildren = new ArrayList<>(); private final ArrayList<WindowContainer> mTmpHomeChildren = new ArrayList<>(); private final IntArray mTmpNeedsZBoostIndexes = new IntArray(); - private int mTmpLayerForSplitScreenDividerAnchor; private int mTmpLayerForAnimationLayer; private ArrayList<Task> mTmpTasks = new ArrayList<>(); @@ -855,26 +855,23 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> { layer = adjustRootTaskLayer(t, mTmpHomeChildren, layer, false /* normalRootTasks */); // The home animation layer is between the root home tasks and the normal root tasks. final int layerForHomeAnimationLayer = layer++; - mTmpLayerForSplitScreenDividerAnchor = layer++; mTmpLayerForAnimationLayer = layer++; layer = adjustRootTaskLayer(t, mTmpNormalChildren, layer, true /* normalRootTasks */); // The boosted animation layer is between the normal root tasks and the always on top // root tasks. final int layerForBoostedAnimationLayer = layer++; + // Always on top tasks layer should higher than split divider layer so set it as start. + layer = SPLIT_DIVIDER_LAYER + 1; adjustRootTaskLayer(t, mTmpAlwaysOnTopChildren, layer, false /* normalRootTasks */); t.setLayer(mHomeAppAnimationLayer, layerForHomeAnimationLayer); t.setLayer(mAppAnimationLayer, mTmpLayerForAnimationLayer); - t.setLayer(mSplitScreenDividerAnchor, mTmpLayerForSplitScreenDividerAnchor); + t.setLayer(mSplitScreenDividerAnchor, SPLIT_DIVIDER_LAYER); t.setLayer(mBoostedAppAnimationLayer, layerForBoostedAnimationLayer); } private int adjustNormalRootTaskLayer(WindowContainer child, int layer) { - if (child.asTask() != null && child.inSplitScreenWindowingMode()) { - // The split screen divider anchor is located above the split screen window. - mTmpLayerForSplitScreenDividerAnchor = layer++; - } if ((child.asTask() != null && child.asTask().isAnimatingByRecents()) || child.isAppTransitioning()) { // The animation layer is located above the highest animating root task and no diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java index 6c31716f8634..0b9d3dc9d043 100644 --- a/services/core/java/com/android/server/wm/TaskFragment.java +++ b/services/core/java/com/android/server/wm/TaskFragment.java @@ -221,6 +221,13 @@ class TaskFragment extends WindowContainer<WindowContainer> { private IBinder mFragmentToken; /** + * Whether to delay the last activity of TaskFragment being immediately removed while finishing. + * This should only be set on a embedded TaskFragment, where the organizer can have the + * opportunity to perform other actions or animations. + */ + private boolean mDelayLastActivityRemoval; + + /** * The PID of the organizer that created this TaskFragment. It should be the same as the PID * of {@link android.window.TaskFragmentCreationParams#getOwnerToken()}. * {@link ActivityRecord#INVALID_PID} if this is not an organizer-created TaskFragment. @@ -317,8 +324,10 @@ class TaskFragment extends WindowContainer<WindowContainer> { // Reset the adjacent TaskFragment if its adjacent TaskFragment is also this TaskFragment. if (mAdjacentTaskFragment != null && mAdjacentTaskFragment.mAdjacentTaskFragment == this) { mAdjacentTaskFragment.mAdjacentTaskFragment = null; + mAdjacentTaskFragment.mDelayLastActivityRemoval = false; } mAdjacentTaskFragment = null; + mDelayLastActivityRemoval = false; } void setTaskFragmentOrganizer(TaskFragmentOrganizerToken organizer, int pid) { @@ -422,6 +431,20 @@ class TaskFragment extends WindowContainer<WindowContainer> { } /** + * Returns the TaskFragment that is being organized, which could be this or the ascendant + * TaskFragment. + */ + @Nullable + TaskFragment getOrganizedTaskFragment() { + if (mTaskFragmentOrganizer != null) { + return this; + } + + TaskFragment parentTaskFragment = getParent().asTaskFragment(); + return parentTaskFragment != null ? parentTaskFragment.getOrganizedTaskFragment() : null; + } + + /** * Simply check and give warning logs if this is not operated on leaf {@link TaskFragment}. */ private void warnForNonLeafTaskFragment(String func) { @@ -1262,6 +1285,10 @@ class TaskFragment extends WindowContainer<WindowContainer> { return getVisibility(starting) != TASK_FRAGMENT_VISIBILITY_INVISIBLE; } + boolean isFocusableAndVisible() { + return isTopActivityFocusable() && shouldBeVisible(null /* starting */); + } + final boolean startPausing(boolean uiSleeping, ActivityRecord resuming, String reason) { return startPausing(mTaskSupervisor.mUserLeaving, uiSleeping, resuming, reason); } @@ -2082,6 +2109,17 @@ class TaskFragment extends WindowContainer<WindowContainer> { }); } + void setDelayLastActivityRemoval(boolean delay) { + if (!mIsEmbedded) { + Slog.w(TAG, "Set delaying last activity removal on a non-embedded TF."); + } + mDelayLastActivityRemoval = delay; + } + + boolean isDelayLastActivityRemoval() { + return mDelayLastActivityRemoval; + } + boolean shouldDeferRemoval() { if (!hasChild()) { return false; diff --git a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java index 96860a23d3cf..690f67c25cfe 100644 --- a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java +++ b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java @@ -199,6 +199,8 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr void onTaskFragmentError(ITaskFragmentOrganizer organizer, IBinder errorCallbackToken, Throwable exception) { + ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, + "Sending TaskFragment error exception=%s", exception.toString()); final Bundle exceptionBundle = putExceptionInBundle(exception); try { organizer.onTaskFragmentError(errorCallbackToken, exceptionBundle); @@ -313,6 +315,7 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr void onTaskFragmentError(ITaskFragmentOrganizer organizer, IBinder errorCallbackToken, Throwable exception) { validateAndGetState(organizer); + Slog.w(TAG, "onTaskFragmentError ", exception); PendingTaskFragmentEvent pendingEvent = new PendingTaskFragmentEvent(organizer, errorCallbackToken, exception, PendingTaskFragmentEvent.EVENT_ERROR); mPendingTaskFragmentEvents.add(pendingEvent); diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java index 5366bd42fa1a..07f0197f520b 100644 --- a/services/core/java/com/android/server/wm/Transition.java +++ b/services/core/java/com/android/server/wm/Transition.java @@ -16,15 +16,19 @@ package com.android.server.wm; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.ROTATION_UNDEFINED; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.INVALID_DISPLAY; +import static android.view.WindowManager.INPUT_CONSUMER_RECENTS_ANIMATION; import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS; import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_UNSPECIFIED; import static android.view.WindowManager.TRANSIT_CHANGE; import static android.view.WindowManager.TRANSIT_CLOSE; import static android.view.WindowManager.TRANSIT_FLAG_IS_RECENTS; +import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY; import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION; import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION; import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE; @@ -138,6 +142,12 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe /** The final animation targets derived from participants after promotion. */ private ArraySet<WindowContainer> mTargets = null; + /** + * Set of participating windowtokens (activity/wallpaper) which are visible at the end of + * the transition animation. + */ + private final ArraySet<WindowToken> mVisibleAtTransitionEndTokens = new ArraySet<>(); + /** Custom activity-level animation options and callbacks. */ private TransitionInfo.AnimationOptions mOverrideOptions; private IRemoteCallback mClientAnimationStartCallback = null; @@ -149,7 +159,7 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe // TODO(b/188595497): remove when not needed. /** @see RecentsAnimationController#mNavigationBarAttachedToApp */ private boolean mNavBarAttachedToApp = false; - private int mNavBarDisplayId = INVALID_DISPLAY; + private int mRecentsDisplayId = INVALID_DISPLAY; Transition(@TransitionType int type, @TransitionFlags int flags, TransitionController controller, BLASTSyncEngine syncEngine) { @@ -169,6 +179,11 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe return mSyncId; } + @TransitionFlags + int getFlags() { + return mFlags; + } + /** * Formally starts the transition. Participants can be collected before this is started, * but this won't consider itself ready until started -- even if all the participants have @@ -344,7 +359,14 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe for (int i = 0; i < mParticipants.size(); ++i) { final ActivityRecord ar = mParticipants.valueAt(i).asActivityRecord(); if (ar != null) { - if (!ar.isVisibleRequested()) { + boolean visibleAtTransitionEnd = mVisibleAtTransitionEndTokens.contains(ar); + // We need both the expected visibility AND current requested-visibility to be + // false. If it is expected-visible but not currently visible, it means that + // another animation is queued-up to animate this to invisibility, so we can't + // remove the surfaces yet. If it is currently visible, but not expected-visible, + // then doing commitVisibility here would actually be out-of-order and leave the + // activity in a bad state. + if (!visibleAtTransitionEnd && !ar.isVisibleRequested()) { boolean commitVisibility = true; if (ar.getDeferHidingClient() && ar.getTask() != null) { if (ar.pictureInPictureArgs != null @@ -367,17 +389,20 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe activitiesWentInvisible = true; } } - if (mChanges.get(ar).mVisible != ar.isVisibleRequested()) { + if (mChanges.get(ar).mVisible != visibleAtTransitionEnd) { // Legacy dispatch relies on this (for now). - ar.mEnteringAnimation = ar.isVisibleRequested(); + ar.mEnteringAnimation = visibleAtTransitionEnd; } mController.dispatchLegacyAppTransitionFinished(ar); } final WallpaperWindowToken wt = mParticipants.valueAt(i).asWallpaperToken(); - if (wt != null && !wt.isVisibleRequested()) { - ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, - " Commit wallpaper becoming invisible: %s", wt); - wt.commitVisibility(false /* visible */); + if (wt != null) { + final boolean visibleAtTransitionEnd = mVisibleAtTransitionEndTokens.contains(wt); + if (!visibleAtTransitionEnd && !wt.isVisibleRequested()) { + ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, + " Commit wallpaper becoming invisible: %s", wt); + wt.commitVisibility(false /* visible */); + } } } if (activitiesWentInvisible) { @@ -390,6 +415,13 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe sendRemoteCallback(mClientAnimationFinishCallback); legacyRestoreNavigationBarFromApp(); + + if (mRecentsDisplayId != INVALID_DISPLAY) { + // Clean up input monitors (for recents) + final DisplayContent dc = + mController.mAtm.mRootWindowContainer.getDisplayContent(mRecentsDisplayId); + dc.getInputMonitor().setActiveRecents(null /* activity */, null /* layer */); + } } void abort() { @@ -487,6 +519,15 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe } } + // Record windowtokens (activity/wallpaper) that are expected to be visible after the + // transition animation. This will be used in finishTransition to prevent prematurely + // committing visibility. + for (int i = mParticipants.size() - 1; i >= 0; --i) { + final WindowContainer wc = mParticipants.valueAt(i); + if (wc.asWindowToken() == null || !wc.isVisibleRequested()) continue; + mVisibleAtTransitionEndTokens.add(wc.asWindowToken()); + } + mStartTransaction = transaction; mFinishTransaction = mController.mAtm.mWindowManager.mTransactionFactory.get(); buildFinishTransaction(mFinishTransaction, info.getRootLeash()); @@ -534,7 +575,43 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe } final DisplayContent dc = mController.mAtm.mRootWindowContainer.getDisplayContent(displayId); - if (dc == null || !dc.getDisplayPolicy().shouldAttachNavBarToAppDuringTransition() + if (dc == null) return; + mRecentsDisplayId = displayId; + + // Recents has an input-consumer to grab input from the "live tile" app. Set that up here + final InputConsumerImpl recentsAnimationInputConsumer = + dc.getInputMonitor().getInputConsumer(INPUT_CONSUMER_RECENTS_ANIMATION); + if (recentsAnimationInputConsumer != null) { + // find the top-most going-away activity and the recents activity. The top-most + // is used as layer reference while the recents is used for registering the consumer + // override. + ActivityRecord recentsActivity = null; + ActivityRecord topActivity = null; + for (int i = 0; i < info.getChanges().size(); ++i) { + final TransitionInfo.Change change = info.getChanges().get(i); + if (change.getTaskInfo() == null) continue; + final Task task = Task.fromWindowContainerToken( + info.getChanges().get(i).getTaskInfo().token); + if (task == null) continue; + final int activityType = change.getTaskInfo().topActivityType; + final boolean isRecents = activityType == ACTIVITY_TYPE_HOME + || activityType == ACTIVITY_TYPE_RECENTS; + if (isRecents && recentsActivity == null) { + recentsActivity = task.getTopVisibleActivity(); + } else if (!isRecents && topActivity == null) { + topActivity = task.getTopNonFinishingActivity(); + } + } + if (recentsActivity != null && topActivity != null) { + recentsAnimationInputConsumer.mWindowHandle.touchableRegion.set( + topActivity.getBounds()); + dc.getInputMonitor().setActiveRecents(recentsActivity, topActivity); + } + } + + // The rest of this function handles nav-bar reparenting + + if (!dc.getDisplayPolicy().shouldAttachNavBarToAppDuringTransition() // Skip the case where the nav bar is controlled by fade rotation. || dc.getFadeRotationAnimationController() != null) { return; @@ -561,7 +638,6 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe return; } mNavBarAttachedToApp = true; - mNavBarDisplayId = displayId; navWindow.mToken.cancelAnimation(); final SurfaceControl.Transaction t = navWindow.mToken.getPendingTransaction(); final SurfaceControl navSurfaceControl = navWindow.mToken.getSurfaceControl(); @@ -585,12 +661,17 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe if (!mNavBarAttachedToApp) return; mNavBarAttachedToApp = false; + if (mRecentsDisplayId == INVALID_DISPLAY) { + Slog.e(TAG, "Reparented navigation bar without a valid display"); + mRecentsDisplayId = DEFAULT_DISPLAY; + } + if (mController.mStatusBar != null) { - mController.mStatusBar.setNavigationBarLumaSamplingEnabled(mNavBarDisplayId, true); + mController.mStatusBar.setNavigationBarLumaSamplingEnabled(mRecentsDisplayId, true); } final DisplayContent dc = - mController.mAtm.mRootWindowContainer.getDisplayContent(mNavBarDisplayId); + mController.mAtm.mRootWindowContainer.getDisplayContent(mRecentsDisplayId); final WindowState navWindow = dc.getDisplayPolicy().getNavigationBar(); if (navWindow == null) return; navWindow.setSurfaceTranslationY(0); @@ -622,13 +703,14 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe } private void handleNonAppWindowsInTransition(int displayId, - @TransitionType int transit, int flags) { + @TransitionType int transit, @TransitionFlags int flags) { final DisplayContent dc = mController.mAtm.mRootWindowContainer.getDisplayContent(displayId); if (dc == null) { return; } - if (transit == TRANSIT_KEYGUARD_GOING_AWAY + if ((transit == TRANSIT_KEYGUARD_GOING_AWAY + || (flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY) != 0) && !WindowManagerService.sEnableRemoteKeyguardGoingAwayAnimation) { if ((flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER) != 0 && (flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION) == 0 diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java index 44dee4d85dc5..419b4c3a706d 100644 --- a/services/core/java/com/android/server/wm/TransitionController.java +++ b/services/core/java/com/android/server/wm/TransitionController.java @@ -18,11 +18,7 @@ package com.android.server.wm; import static android.view.WindowManager.TRANSIT_CLOSE; import static android.view.WindowManager.TRANSIT_FLAG_IS_RECENTS; -import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION; -import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION; -import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE; -import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER; -import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY; +import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY; import static android.view.WindowManager.TRANSIT_NONE; import static android.view.WindowManager.TRANSIT_OPEN; @@ -121,11 +117,16 @@ class TransitionController { void registerTransitionPlayer(@Nullable ITransitionPlayer player) { try { + // Note: asBinder() can be null if player is same process (likely in a test). if (mTransitionPlayer != null) { - mTransitionPlayer.asBinder().unlinkToDeath(mTransitionPlayerDeath, 0); + if (mTransitionPlayer.asBinder() != null) { + mTransitionPlayer.asBinder().unlinkToDeath(mTransitionPlayerDeath, 0); + } mTransitionPlayer = null; } - player.asBinder().linkToDeath(mTransitionPlayerDeath, 0); + if (player.asBinder() != null) { + player.asBinder().linkToDeath(mTransitionPlayerDeath, 0); + } mTransitionPlayer = player; } catch (RemoteException e) { throw new RuntimeException("Unable to set transition player"); @@ -230,6 +231,10 @@ class TransitionController { if (isCollecting()) { // Make the collecting transition wait until this request is ready. mCollectingTransition.setReady(readyGroupRef, false); + if ((flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY) != 0) { + // Add keyguard flag to dismiss keyguard + mCollectingTransition.addFlag(flags); + } } else { newTransition = requestStartTransition(createTransition(type, flags), trigger != null ? trigger.asTask() : null, remoteTransition); @@ -354,11 +359,7 @@ class TransitionController { } void dispatchLegacyAppTransitionStarting(TransitionInfo info) { - final boolean keyguardGoingAway = info.getType() == TRANSIT_KEYGUARD_GOING_AWAY - || (info.getFlags() & (TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE - | TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION - | TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER - | TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION)) != 0; + final boolean keyguardGoingAway = info.isKeyguardGoingAway(); for (int i = 0; i < mLegacyListeners.size(); ++i) { mLegacyListeners.get(i).onAppTransitionStartingLocked(keyguardGoingAway, 0 /* durationHint */, SystemClock.uptimeMillis(), diff --git a/services/core/java/com/android/server/wm/Watermark.java b/services/core/java/com/android/server/wm/Watermark.java index 66ab094f0994..9780d3317e11 100644 --- a/services/core/java/com/android/server/wm/Watermark.java +++ b/services/core/java/com/android/server/wm/Watermark.java @@ -31,6 +31,7 @@ import android.util.TypedValue; import android.view.Surface; import android.view.Surface.OutOfResourcesException; import android.view.SurfaceControl; +import android.view.WindowManagerPolicyConstants; /** * Displays a watermark on top of the window manager's windows. @@ -117,7 +118,7 @@ class Watermark { .setFormat(PixelFormat.TRANSLUCENT) .setCallsite(TITLE) .build(); - t.setLayer(ctrl, WindowManagerService.TYPE_LAYER_MULTIPLIER * 100) + t.setLayer(ctrl, WindowManagerPolicyConstants.WATERMARK_LAYER) .setPosition(ctrl, 0, 0) .show(ctrl); // Ensure we aren't considered as obscuring for Input purposes. diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java index 5bc4d4997f17..132f1392cba7 100644 --- a/services/core/java/com/android/server/wm/WindowManagerInternal.java +++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java @@ -676,24 +676,43 @@ public abstract class WindowManagerInternal { public abstract String getWindowName(@NonNull IBinder binder); /** - * Return the window name of IME Insets control target. + * The callback after the request of show/hide input method is sent. * + * @param show Whether to show or hide input method. + * @param focusedToken The token of focused window. + * @param requestToken The token of window who requests the change. * @param displayId The ID of the display which input method is currently focused. - * @return The corresponding {@link WindowState#getName()} + * @return The information of the input method target. */ - public abstract @Nullable String getImeControlTargetNameForLogging(int displayId); + public abstract ImeTargetInfo onToggleImeRequested(boolean show, + @NonNull IBinder focusedToken, @NonNull IBinder requestToken, int displayId); - /** - * Return the current window name of the input method is on top of. - * - * Note that the concept of this window is only reparent the target window behind the input - * method window, it may different with the window which reported by - * {@code InputMethodManagerService#reportStartInput} which has input connection. - * - * @param displayId The ID of the display which input method is currently focused. - * @return The corresponding {@link WindowState#getName()} - */ - public abstract @Nullable String getImeTargetNameForLogging(int displayId); + /** The information of input method target when IME is requested to show or hide. */ + public static class ImeTargetInfo { + public final String focusedWindowName; + public final String requestWindowName; + + /** The window name of IME Insets control target. */ + public final String imeControlTargetName; + + /** + * The current window name of the input method is on top of. + * <p> + * Note that the concept of this window is only used to reparent the target window behind + * the input method window, it may be different from the window reported by + * {@link com.android.server.inputmethod.InputMethodManagerService#reportStartInput} which + * has input connection. + */ + public final String imeLayerTargetName; + + public ImeTargetInfo(String focusedWindowName, String requestWindowName, + String imeControlTargetName, String imeLayerTargetName) { + this.focusedWindowName = focusedWindowName; + this.requestWindowName = requestWindowName; + this.imeControlTargetName = imeControlTargetName; + this.imeLayerTargetName = imeLayerTargetName; + } + } /** * Moves the {@link WindowToken} {@code binder} to the display specified by {@code displayId}. diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 36809b374525..81f9f6f42c03 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -85,8 +85,10 @@ import static android.view.WindowManagerGlobal.ADD_OKAY; import static android.view.WindowManagerGlobal.RELAYOUT_RES_BLAST_SYNC; import static android.view.WindowManagerGlobal.RELAYOUT_RES_SURFACE_CHANGED; import static android.view.WindowManagerPolicyConstants.NAV_BAR_INVALID; +import static android.view.WindowManagerPolicyConstants.TYPE_LAYER_MULTIPLIER; import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_MISSING_WINDOW; import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_NOT_VISIBLE_ON_SCREEN; +import static android.window.WindowProviderService.isWindowProviderService; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_BOOT; @@ -339,27 +341,6 @@ public class WindowManagerService extends IWindowManager.Stub static final boolean PROFILE_ORIENTATION = false; - /** How much to multiply the policy's type layer, to reserve room - * for multiple windows of the same type and Z-ordering adjustment - * with TYPE_LAYER_OFFSET. */ - static final int TYPE_LAYER_MULTIPLIER = 10000; - - /** Offset from TYPE_LAYER_MULTIPLIER for moving a group of windows above - * or below others in the same layer. */ - static final int TYPE_LAYER_OFFSET = 1000; - - /** How much to increment the layer for each window, to reserve room - * for effect surfaces between them. - */ - static final int WINDOW_LAYER_MULTIPLIER = 5; - - /** - * Animation thumbnail is as far as possible below the window above - * the thumbnail (or in other words as far as possible above the window - * below it). - */ - static final int LAYER_OFFSET_THUMBNAIL = WINDOW_LAYER_MULTIPLIER - 1; - /** The maximum length we will accept for a loaded animation duration: * this is 10 seconds. */ @@ -1730,16 +1711,22 @@ public class WindowManagerService extends IWindowManager.Stub && mWindowContextListenerController.hasListener(windowContextToken)) { final int windowContextType = mWindowContextListenerController .getWindowType(windowContextToken); + final Bundle options = mWindowContextListenerController + .getOptions(windowContextToken); if (type != windowContextType) { ProtoLog.w(WM_ERROR, "Window types in WindowContext and" + " LayoutParams.type should match! Type from LayoutParams is %d," + " but type from WindowContext is %d", type, windowContextType); - return WindowManagerGlobal.ADD_INVALID_TYPE; + // We allow WindowProviderService to add window other than windowContextType, + // but the WindowProviderService won't be associated with the window's + // WindowToken. + if (!isWindowProviderService(options)) { + return WindowManagerGlobal.ADD_INVALID_TYPE; + } + } else { + mWindowContextListenerController.registerWindowContainerListener( + windowContextToken, token, callingUid, type, options); } - final Bundle options = mWindowContextListenerController - .getOptions(windowContextToken); - mWindowContextListenerController.registerWindowContainerListener( - windowContextToken, token, callingUid, type, options); } // From now on, no exceptions or errors allowed! @@ -7086,6 +7073,7 @@ public class WindowManagerService extends IWindowManager.Stub "requestScrollCapture: caught exception dispatching to window." + "token=%s", targetWindow.mClient.asBinder()); responseBuilder.setWindowTitle(targetWindow.getName()); + responseBuilder.setPackageName(targetWindow.getOwningPackage()); responseBuilder.setDescription(String.format("caught exception: %s", e)); listener.onScrollCaptureResponse(responseBuilder.build()); } @@ -7899,30 +7887,37 @@ public class WindowManagerService extends IWindowManager.Stub } @Override - public String getImeControlTargetNameForLogging(int displayId) { - synchronized (mGlobalLock) { - final DisplayContent dc = mRoot.getDisplayContent(displayId); - if (dc == null) { - return null; - } - final InsetsControlTarget target = dc.getImeTarget(IME_TARGET_CONTROL); - if (target == null) { - return null; - } - final WindowState win = target.getWindow(); - return win != null ? win.getName() : target.toString(); - } - } - - @Override - public String getImeTargetNameForLogging(int displayId) { + public ImeTargetInfo onToggleImeRequested(boolean show, IBinder focusedToken, + IBinder requestToken, int displayId) { + final String focusedWindowName; + final String requestWindowName; + final String imeControlTargetName; + final String imeLayerTargetName; synchronized (mGlobalLock) { + final WindowState focusedWin = mWindowMap.get(focusedToken); + focusedWindowName = focusedWin != null ? focusedWin.getName() : "null"; + final WindowState requestWin = mWindowMap.get(requestToken); + requestWindowName = requestWin != null ? requestWin.getName() : "null"; final DisplayContent dc = mRoot.getDisplayContent(displayId); - if (dc == null || dc.getImeTarget(IME_TARGET_LAYERING) == null) { - return null; + if (dc != null) { + final InsetsControlTarget controlTarget = dc.getImeTarget(IME_TARGET_CONTROL); + if (controlTarget != null) { + final WindowState w = InsetsControlTarget.asWindowOrNull(controlTarget); + imeControlTargetName = w != null ? w.getName() : controlTarget.toString(); + } else { + imeControlTargetName = "null"; + } + final InsetsControlTarget target = dc.getImeTarget(IME_TARGET_LAYERING); + imeLayerTargetName = target != null ? target.getWindow().getName() : "null"; + if (show) { + dc.onShowImeRequested(); + } + } else { + imeControlTargetName = imeLayerTargetName = "no-display"; } - return dc.getImeTarget(IME_TARGET_LAYERING).getWindow().getName(); } + return new ImeTargetInfo(focusedWindowName, requestWindowName, imeControlTargetName, + imeLayerTargetName); } @Override @@ -8241,7 +8236,7 @@ public class WindowManagerService extends IWindowManager.Stub } updateInputChannel(clientChannel.getToken(), callingUid, callingPid, displayId, surface, - name, applicationHandle, flags, privateFlags, type, null /* region */); + name, applicationHandle, flags, privateFlags, type, null /* region */, window); clientChannel.copyTo(outInputChannel); } @@ -8249,9 +8244,10 @@ public class WindowManagerService extends IWindowManager.Stub private void updateInputChannel(IBinder channelToken, int callingUid, int callingPid, int displayId, SurfaceControl surface, String name, InputApplicationHandle applicationHandle, int flags, - int privateFlags, int type, Region region) { + int privateFlags, int type, Region region, IWindow window) { InputWindowHandle h = new InputWindowHandle(applicationHandle, displayId); h.token = channelToken; + h.setWindowToken(window); h.name = name; final int sanitizedFlags = flags & (LayoutParams.FLAG_NOT_TOUCHABLE @@ -8307,7 +8303,7 @@ public class WindowManagerService extends IWindowManager.Stub } updateInputChannel(channelToken, win.mOwnerUid, win.mOwnerPid, displayId, surface, name, - applicationHandle, flags, privateFlags, win.mWindowType, region); + applicationHandle, flags, privateFlags, win.mWindowType, region, win.mClient); } /** Return whether layer tracing is enabled */ @@ -8636,8 +8632,9 @@ public class WindowManagerService extends IWindowManager.Stub if (imeTargetWindowTask == null) { return false; } - final TaskSnapshot snapshot = mAtmService.getTaskSnapshot(imeTargetWindowTask.mTaskId, - false /* isLowResolution */); + final TaskSnapshot snapshot = getTaskSnapshot(imeTargetWindowTask.mTaskId, + imeTargetWindowTask.mUserId, false /* isLowResolution */, + false /* restoreFromDisk */); return snapshot != null && snapshot.hasImeSurface(); } } diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java index aa147c4c8712..45c47bab1175 100644 --- a/services/core/java/com/android/server/wm/WindowOrganizerController.java +++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java @@ -741,6 +741,21 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub break; } tf1.setAdjacentTaskFragment(tf2); + + final Bundle bundle = hop.getLaunchOptions(); + final WindowContainerTransaction.TaskFragmentAdjacentOptions adjacentOptions = + bundle != null ? new WindowContainerTransaction.TaskFragmentAdjacentOptions( + bundle) : null; + if (adjacentOptions == null) { + break; + } + + tf1.setDelayLastActivityRemoval( + adjacentOptions.isDelayPrimaryLastActivityRemoval()); + if (tf2 != null) { + tf2.setDelayLastActivityRemoval( + adjacentOptions.isDelaySecondaryLastActivityRemoval()); + } break; } return effects; diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java index 65b065ab8eab..cd29f0eb61a2 100644 --- a/services/core/java/com/android/server/wm/WindowProcessController.java +++ b/services/core/java/com/android/server/wm/WindowProcessController.java @@ -730,11 +730,14 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio canUpdate = true; } - // Compare the z-order of ActivityStacks if both activities landed on same display. - if (display == topDisplay - && mPreQTopResumedActivity.getRootTask().compareTo( - activity.getRootTask()) <= 0) { - canUpdate = true; + // Update the topmost activity if the activity has higher z-order than the current + // top-resumed activity. + if (!canUpdate) { + final ActivityRecord ar = topDisplay.getActivity(r -> r == activity, + true /* traverseTopToBottom */, mPreQTopResumedActivity); + if (ar != null && ar != mPreQTopResumedActivity) { + canUpdate = true; + } } if (canUpdate) { diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 0091b618696e..1ca0c7eb8aba 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -107,6 +107,8 @@ import static android.view.WindowManager.LayoutParams.isSystemAlertWindowType; import static android.view.WindowManagerGlobal.RELAYOUT_RES_DRAG_RESIZING_DOCKED; import static android.view.WindowManagerGlobal.RELAYOUT_RES_DRAG_RESIZING_FREEFORM; import static android.view.WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME; +import static android.view.WindowManagerPolicyConstants.TYPE_LAYER_MULTIPLIER; +import static android.view.WindowManagerPolicyConstants.TYPE_LAYER_OFFSET; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS; @@ -153,8 +155,6 @@ import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; import static com.android.server.wm.WindowManagerService.H.WINDOW_STATE_BLAST_SYNC_TIMEOUT; import static com.android.server.wm.WindowManagerService.MAX_ANIMATION_DURATION; -import static com.android.server.wm.WindowManagerService.TYPE_LAYER_MULTIPLIER; -import static com.android.server.wm.WindowManagerService.TYPE_LAYER_OFFSET; import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL; import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_REMOVING_FOCUS; import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_PLACE_SURFACES; diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java index 80941961cc5a..f25706a97fb6 100644 --- a/services/core/java/com/android/server/wm/WindowStateAnimator.java +++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java @@ -41,7 +41,6 @@ import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY; import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; -import static com.android.server.wm.WindowManagerService.TYPE_LAYER_MULTIPLIER; import static com.android.server.wm.WindowManagerService.logWithStack; import static com.android.server.wm.WindowStateAnimatorProto.DRAW_STATE; import static com.android.server.wm.WindowStateAnimatorProto.SURFACE; @@ -71,7 +70,6 @@ import java.io.PrintWriter; **/ class WindowStateAnimator { static final String TAG = TAG_WITH_CLASS_NAME ? "WindowStateAnimator" : TAG_WM; - static final int WINDOW_FREEZE_LAYER = TYPE_LAYER_MULTIPLIER * 200; static final int PRESERVED_SURFACE_LAYER = 1; /** diff --git a/services/core/xsd/device-state-config/device-state-config.xsd b/services/core/xsd/device-state-config/device-state-config.xsd index 94a398f2cdb7..86f41769008d 100644 --- a/services/core/xsd/device-state-config/device-state-config.xsd +++ b/services/core/xsd/device-state-config/device-state-config.xsd @@ -40,10 +40,19 @@ <xs:element name="name" type="xs:string" minOccurs="0"> <xs:annotation name="nullable" /> </xs:element> + <xs:element name="flags" type="flags" /> <xs:element name="conditions" type="conditions" /> </xs:sequence> </xs:complexType> + <xs:complexType name="flags"> + <xs:sequence> + <xs:element name="flag" type="xs:string" minOccurs="0" maxOccurs="unbounded"> + <xs:annotation name="nullable" /> + </xs:element> + </xs:sequence> + </xs:complexType> + <xs:complexType name="conditions"> <xs:sequence> <xs:element name="lid-switch" type="lidSwitchCondition" minOccurs="0"> diff --git a/services/core/xsd/device-state-config/schema/current.txt b/services/core/xsd/device-state-config/schema/current.txt index 08fccf8ad949..a98d4e569cd6 100644 --- a/services/core/xsd/device-state-config/schema/current.txt +++ b/services/core/xsd/device-state-config/schema/current.txt @@ -11,9 +11,11 @@ package com.android.server.policy.devicestate.config { public class DeviceState { ctor public DeviceState(); method public com.android.server.policy.devicestate.config.Conditions getConditions(); + method public com.android.server.policy.devicestate.config.Flags getFlags(); method public java.math.BigInteger getIdentifier(); method @Nullable public String getName(); method public void setConditions(com.android.server.policy.devicestate.config.Conditions); + method public void setFlags(com.android.server.policy.devicestate.config.Flags); method public void setIdentifier(java.math.BigInteger); method public void setName(@Nullable String); } @@ -23,6 +25,11 @@ package com.android.server.policy.devicestate.config { method public java.util.List<com.android.server.policy.devicestate.config.DeviceState> getDeviceState(); } + public class Flags { + ctor public Flags(); + method @Nullable public java.util.List<java.lang.String> getFlag(); + } + public class LidSwitchCondition { ctor public LidSwitchCondition(); method public boolean getOpen(); diff --git a/services/net/Android.bp b/services/net/Android.bp index a822257e1a74..53ce6b264651 100644 --- a/services/net/Android.bp +++ b/services/net/Android.bp @@ -52,7 +52,6 @@ java_library { // classes generated by netd_aidl_interfaces-platform-java above. "netd_aidl_interface-V3-java", "networkstack-client", - "modules-utils-build_system", ], apex_available: [ "com.android.wifi", diff --git a/services/tests/PackageManager/packageinstaller/Android.bp b/services/tests/PackageManager/packageinstaller/Android.bp new file mode 100644 index 000000000000..35d754b4adc5 --- /dev/null +++ b/services/tests/PackageManager/packageinstaller/Android.bp @@ -0,0 +1,39 @@ +// Copyright (C) 2021 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + +android_test { + name: "PackageInstallerTests", + srcs: [ + "src/**/*.java", + "src/**/*.kt", + ], + static_libs: [ + "androidx.test.rules", + "androidx.test.runner", + "junit", + "kotlin-test", + "truth-prebuilt", + ], + platform_apis: true, + test_suites: ["device-tests"], +} diff --git a/packages/SystemUI/res/values-h560dp-xhdpi/config.xml b/services/tests/PackageManager/packageinstaller/AndroidManifest.xml index cf2017f1eb6f..d7062587c4bc 100644 --- a/packages/SystemUI/res/values-h560dp-xhdpi/config.xml +++ b/services/tests/PackageManager/packageinstaller/AndroidManifest.xml @@ -1,7 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> - <!-- - ~ Copyright (C) 2014 The Android Open Source Project + ~ Copyright (C) 2021 The Android Open Source Project ~ ~ Licensed under the Apache License, Version 2.0 (the "License"); ~ you may not use this file except in compliance with the License. @@ -13,11 +12,18 @@ ~ distributed under the License is distributed on an "AS IS" BASIS, ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ~ See the License for the specific language governing permissions and - ~ limitations under the License + ~ limitations under the License. --> -<resources> - <!-- The maximum count of notifications on Keyguard. The rest will be collapsed in an overflow - card. --> - <integer name="keyguard_max_notification_count">3</integer> -</resources> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.packageinstaller.test"> + + <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/> + + <instrumentation + android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="com.android.packageinstaller.test" + /> + +</manifest> diff --git a/services/tests/PackageManager/packageinstaller/AndroidTest.xml b/services/tests/PackageManager/packageinstaller/AndroidTest.xml new file mode 100644 index 000000000000..c39285ffca38 --- /dev/null +++ b/services/tests/PackageManager/packageinstaller/AndroidTest.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2021 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<configuration description="Test module config for PackageInstallerTests"> + <option name="test-tag" value="PackageInstallerTests" /> + + <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> + <option name="cleanup-apks" value="true" /> + <option name="test-file-name" value="PackageInstallerTests.apk" /> + </target_preparer> + + <test class="com.android.tradefed.testtype.AndroidJUnitTest"> + <option name="package" value="com.android.packageinstaller.test" /> + </test> +</configuration> diff --git a/services/tests/PackageManager/packageinstaller/src/com/android/packageinstaller/test/ExportedComponentTest.kt b/services/tests/PackageManager/packageinstaller/src/com/android/packageinstaller/test/ExportedComponentTest.kt new file mode 100644 index 000000000000..d7d2726c1583 --- /dev/null +++ b/services/tests/PackageManager/packageinstaller/src/com/android/packageinstaller/test/ExportedComponentTest.kt @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.packageinstaller.test + +import android.content.Context +import android.content.Intent +import android.content.pm.PackageManager +import android.net.Uri +import androidx.test.InstrumentationRegistry +import com.google.common.truth.Truth.assertThat +import com.google.common.truth.Truth.assertWithMessage +import org.junit.Test + +class ExportedComponentTest { + + private val context: Context = InstrumentationRegistry.getContext() + + @Test + fun verifyNoExportedReceivers() { + val intent = Intent(Intent.ACTION_INSTALL_PACKAGE).apply { + data = Uri.parse("content://mockForTest") + } + val packageInstallers = context.packageManager.queryIntentActivities(intent, + PackageManager.MATCH_DEFAULT_ONLY or PackageManager.MATCH_DISABLED_COMPONENTS) + .map { it.activityInfo.packageName } + .distinct() + .map { context.packageManager.getPackageInfo(it, PackageManager.GET_RECEIVERS) } + + assertThat(packageInstallers).isNotEmpty() + + packageInstallers.forEach { + val exported = it.receivers.filter { it.exported } + assertWithMessage("Receivers should not be exported").that(exported).isEmpty() + } + } +} diff --git a/services/tests/mockingservicestests/AndroidManifest.xml b/services/tests/mockingservicestests/AndroidManifest.xml index 17a5dccb57da..3cab5ecd3de1 100644 --- a/services/tests/mockingservicestests/AndroidManifest.xml +++ b/services/tests/mockingservicestests/AndroidManifest.xml @@ -28,6 +28,7 @@ <uses-permission android:name="android.permission.MANAGE_APPOPS"/> <uses-permission android:name="android.permission.MONITOR_DEVICE_CONFIG_ACCESS"/> <uses-permission android:name="android.permission.WRITE_DEVICE_CONFIG"/> + <uses-permission android:name="android.permission.WAKE_LOCK"/> <uses-permission android:name="android.permission.OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD"/> diff --git a/services/tests/mockingservicestests/assets/SensorPrivacyServiceMockingTest/persisted_file6.xml b/services/tests/mockingservicestests/assets/SensorPrivacyServiceMockingTest/persisted_file6.xml new file mode 100644 index 000000000000..b3c142285c8a --- /dev/null +++ b/services/tests/mockingservicestests/assets/SensorPrivacyServiceMockingTest/persisted_file6.xml @@ -0,0 +1,3 @@ +<?xml version='1.0' encoding='UTF-8' standalone='yes' ?> +<sensor-privacy enabled="false"> +</sensor-privacy> diff --git a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java index acf50b4569c6..e472b062388e 100644 --- a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java @@ -323,6 +323,7 @@ public class DeviceIdleControllerTest { when(mPowerManager.newWakeLock(anyInt(), anyString())).thenReturn(mWakeLock); doNothing().when(mWakeLock).acquire(); doNothing().when(mAlarmManager).set(anyInt(), anyLong(), anyString(), any(), any()); + doNothing().when(mAlarmManager).setExact(anyInt(), anyLong(), anyString(), any(), any()); doNothing().when(mAlarmManager) .setWindow(anyInt(), anyLong(), anyLong(), anyString(), any(), any()); doReturn(mock(Sensor.class)).when(mSensorManager) diff --git a/services/tests/mockingservicestests/src/com/android/server/power/ScreenUndimDetectorTest.java b/services/tests/mockingservicestests/src/com/android/server/power/ScreenUndimDetectorTest.java new file mode 100644 index 000000000000..f94377fe51c2 --- /dev/null +++ b/services/tests/mockingservicestests/src/com/android/server/power/ScreenUndimDetectorTest.java @@ -0,0 +1,305 @@ +/* + * 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.power; + +import static android.hardware.display.DisplayManagerInternal.DisplayPowerRequest.POLICY_BRIGHT; +import static android.hardware.display.DisplayManagerInternal.DisplayPowerRequest.POLICY_DIM; +import static android.hardware.display.DisplayManagerInternal.DisplayPowerRequest.POLICY_DOZE; +import static android.hardware.display.DisplayManagerInternal.DisplayPowerRequest.POLICY_OFF; +import static android.hardware.display.DisplayManagerInternal.DisplayPowerRequest.POLICY_VR; +import static android.provider.DeviceConfig.NAMESPACE_ATTENTION_MANAGER_SERVICE; + +import static com.android.server.power.ScreenUndimDetector.DEFAULT_MAX_DURATION_BETWEEN_UNDIMS_MILLIS; +import static com.android.server.power.ScreenUndimDetector.KEY_KEEP_SCREEN_ON_ENABLED; +import static com.android.server.power.ScreenUndimDetector.KEY_MAX_DURATION_BETWEEN_UNDIMS_MILLIS; +import static com.android.server.power.ScreenUndimDetector.KEY_UNDIMS_REQUIRED; + +import static com.google.common.truth.Truth.assertThat; + +import android.os.SystemClock; +import android.provider.DeviceConfig; +import android.testing.TestableContext; + +import androidx.test.platform.app.InstrumentationRegistry; + +import com.android.server.testables.TestableDeviceConfig; + +import org.junit.Before; +import org.junit.ClassRule; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; +import org.mockito.MockitoAnnotations; + +import java.util.Arrays; +import java.util.List; + +/** + * Tests for {@link com.android.server.power.ScreenUndimDetector} + */ +@RunWith(JUnit4.class) +public class ScreenUndimDetectorTest { + private static final List<Integer> ALL_POLICIES = + Arrays.asList(POLICY_OFF, + POLICY_DOZE, + POLICY_DIM, + POLICY_BRIGHT, + POLICY_VR); + + @ClassRule + public static final TestableContext sContext = new TestableContext( + InstrumentationRegistry.getInstrumentation().getTargetContext(), null); + @Rule + public TestableDeviceConfig.TestableDeviceConfigRule + mDeviceConfigRule = new TestableDeviceConfig.TestableDeviceConfigRule(); + + private ScreenUndimDetector mScreenUndimDetector; + + private final TestClock mClock = new TestClock(); + + private static class TestClock extends ScreenUndimDetector.InternalClock { + long mCurrentTime = 0; + @Override + public long getCurrentTime() { + return mCurrentTime; + } + + public void advanceTime(long millisAdvanced) { + mCurrentTime += millisAdvanced; + } + } + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + + DeviceConfig.setProperty(NAMESPACE_ATTENTION_MANAGER_SERVICE, + KEY_UNDIMS_REQUIRED, + Integer.toString(1), false /*makeDefault*/); + DeviceConfig.setProperty(NAMESPACE_ATTENTION_MANAGER_SERVICE, + KEY_MAX_DURATION_BETWEEN_UNDIMS_MILLIS, + Long.toString(DEFAULT_MAX_DURATION_BETWEEN_UNDIMS_MILLIS), + false /*makeDefault*/); + + mScreenUndimDetector = new ScreenUndimDetector(mClock); + mScreenUndimDetector.systemReady(sContext); + } + + @Test + public void recordScreenPolicy_disabledByFlag_noop() { + DeviceConfig.setProperty(NAMESPACE_ATTENTION_MANAGER_SERVICE, + KEY_KEEP_SCREEN_ON_ENABLED, Boolean.FALSE.toString(), false /*makeDefault*/); + + setup(); + mScreenUndimDetector.recordScreenPolicy(POLICY_DIM); + mScreenUndimDetector.recordScreenPolicy(POLICY_BRIGHT); + + assertThat(mScreenUndimDetector.mWakeLock.isHeld()).isFalse(); + } + + @Test + public void recordScreenPolicy_samePolicy_noop() { + for (int policy : ALL_POLICIES) { + setup(); + mScreenUndimDetector.recordScreenPolicy(policy); + mScreenUndimDetector.recordScreenPolicy(policy); + + assertThat(mScreenUndimDetector.mWakeLock.isHeld()).isFalse(); + } + } + + @Test + public void recordScreenPolicy_dimToBright_extends() { + mScreenUndimDetector.recordScreenPolicy(POLICY_DIM); + mScreenUndimDetector.recordScreenPolicy(POLICY_BRIGHT); + + assertThat(mScreenUndimDetector.mWakeLock.isHeld()).isTrue(); + } + + @Test + public void recordScreenPolicy_otherTransitions_doesNotExtend() { + for (int from : ALL_POLICIES) { + for (int to : ALL_POLICIES) { + if (from == POLICY_DIM && to == POLICY_BRIGHT) { + continue; + } + setup(); + mScreenUndimDetector.recordScreenPolicy(from); + mScreenUndimDetector.recordScreenPolicy(to); + + assertThat(mScreenUndimDetector.mWakeLock.isHeld()).isFalse(); + assertThat(mScreenUndimDetector.mUndimCounter).isEqualTo(0); + } + } + } + + @Test + public void recordScreenPolicy_dimToBright_twoUndimsNeeded_extends() { + DeviceConfig.setProperty(NAMESPACE_ATTENTION_MANAGER_SERVICE, + KEY_UNDIMS_REQUIRED, + Integer.toString(2), false /*makeDefault*/); + mScreenUndimDetector.readValuesFromDeviceConfig(); + + mScreenUndimDetector.recordScreenPolicy(POLICY_DIM); + mScreenUndimDetector.recordScreenPolicy(POLICY_BRIGHT); + + assertThat(mScreenUndimDetector.mWakeLock.isHeld()).isFalse(); + + mScreenUndimDetector.recordScreenPolicy(POLICY_DIM); + mScreenUndimDetector.recordScreenPolicy(POLICY_BRIGHT); + + assertThat(mScreenUndimDetector.mWakeLock.isHeld()).isTrue(); + } + + @Test + public void recordScreenPolicy_dimBrightDimOff_resetsCounter_doesNotExtend() { + DeviceConfig.setProperty(NAMESPACE_ATTENTION_MANAGER_SERVICE, + KEY_UNDIMS_REQUIRED, + Integer.toString(2), false /*makeDefault*/); + mScreenUndimDetector.readValuesFromDeviceConfig(); + + mScreenUndimDetector.recordScreenPolicy(POLICY_DIM); + mScreenUndimDetector.recordScreenPolicy(POLICY_BRIGHT); + mScreenUndimDetector.recordScreenPolicy(POLICY_DIM); + mScreenUndimDetector.recordScreenPolicy(POLICY_OFF); + + assertThat(mScreenUndimDetector.mWakeLock.isHeld()).isFalse(); + assertThat(mScreenUndimDetector.mUndimCounter).isEqualTo(0); + } + + @Test + public void recordScreenPolicy_undimToOff_resetsCounter() { + DeviceConfig.setProperty(NAMESPACE_ATTENTION_MANAGER_SERVICE, + KEY_UNDIMS_REQUIRED, + Integer.toString(2), false /*makeDefault*/); + mScreenUndimDetector.readValuesFromDeviceConfig(); + + mScreenUndimDetector.recordScreenPolicy(POLICY_BRIGHT); + mScreenUndimDetector.recordScreenPolicy(POLICY_DIM); + mScreenUndimDetector.recordScreenPolicy(POLICY_BRIGHT); + mScreenUndimDetector.recordScreenPolicy(POLICY_OFF); + + assertThat(mScreenUndimDetector.mWakeLock.isHeld()).isFalse(); + assertThat(mScreenUndimDetector.mUndimCounter).isEqualTo(0); + } + + @Test + public void recordScreenPolicy_undimOffUndim_doesNotExtend() { + DeviceConfig.setProperty(NAMESPACE_ATTENTION_MANAGER_SERVICE, + KEY_UNDIMS_REQUIRED, + Integer.toString(2), false /*makeDefault*/); + mScreenUndimDetector.readValuesFromDeviceConfig(); + + // undim + mScreenUndimDetector.recordScreenPolicy(POLICY_BRIGHT); + mScreenUndimDetector.recordScreenPolicy(POLICY_DIM); + mScreenUndimDetector.recordScreenPolicy(POLICY_BRIGHT); + // off + mScreenUndimDetector.recordScreenPolicy(POLICY_OFF); + // second undim + mScreenUndimDetector.recordScreenPolicy(POLICY_BRIGHT); + mScreenUndimDetector.recordScreenPolicy(POLICY_DIM); + mScreenUndimDetector.recordScreenPolicy(POLICY_BRIGHT); + + assertThat(mScreenUndimDetector.mWakeLock.isHeld()).isFalse(); + assertThat(mScreenUndimDetector.mUndimCounter).isEqualTo(1); + } + + @Test + public void recordScreenPolicy_dimToBright_tooFarApart_doesNotExtend() { + DeviceConfig.setProperty(NAMESPACE_ATTENTION_MANAGER_SERVICE, + KEY_UNDIMS_REQUIRED, + Integer.toString(2), false /*makeDefault*/); + mScreenUndimDetector.readValuesFromDeviceConfig(); + + mScreenUndimDetector.recordScreenPolicy(POLICY_DIM); + mScreenUndimDetector.recordScreenPolicy(POLICY_BRIGHT); + + mClock.advanceTime(DEFAULT_MAX_DURATION_BETWEEN_UNDIMS_MILLIS + 5); + mScreenUndimDetector.recordScreenPolicy(POLICY_DIM); + mScreenUndimDetector.recordScreenPolicy(POLICY_BRIGHT); + + assertThat(mScreenUndimDetector.mWakeLock.isHeld()).isFalse(); + assertThat(mScreenUndimDetector.mUndimCounter).isEqualTo(1); + } + + @Test + public void recordScreenPolicy_dimToNonBright_resets() { + for (int to : Arrays.asList(POLICY_OFF, POLICY_DOZE, POLICY_VR)) { + setup(); + mScreenUndimDetector.mUndimCounter = 1; + mScreenUndimDetector.mUndimCounterStartedMillis = 123; + mScreenUndimDetector.mWakeLock.acquire(); + + mScreenUndimDetector.recordScreenPolicy(POLICY_DIM); + mScreenUndimDetector.recordScreenPolicy(to); + + assertThat(mScreenUndimDetector.mUndimCounter).isEqualTo(0); + assertThat(mScreenUndimDetector.mUndimCounterStartedMillis).isEqualTo(0); + assertThat(mScreenUndimDetector.mWakeLock.isHeld()).isFalse(); + } + + } + + @Test + public void recordScreenPolicy_brightToNonDim_resets() { + for (int to : Arrays.asList(POLICY_OFF, POLICY_DOZE, POLICY_VR)) { + setup(); + mScreenUndimDetector.mUndimCounter = 1; + mScreenUndimDetector.mUndimCounterStartedMillis = 123; + mScreenUndimDetector.mWakeLock.acquire(); + + mScreenUndimDetector.recordScreenPolicy(POLICY_BRIGHT); + mScreenUndimDetector.recordScreenPolicy(to); + + assertThat(mScreenUndimDetector.mUndimCounter).isEqualTo(0); + assertThat(mScreenUndimDetector.mUndimCounterStartedMillis).isEqualTo(0); + assertThat(mScreenUndimDetector.mWakeLock.isHeld()).isFalse(); + } + } + + @Test + public void recordScreenPolicy_otherTransitions_doesNotReset() { + DeviceConfig.setProperty(NAMESPACE_ATTENTION_MANAGER_SERVICE, + KEY_UNDIMS_REQUIRED, + Integer.toString(3), + false /*makeDefault*/); + mScreenUndimDetector.readValuesFromDeviceConfig(); + + for (int from : ALL_POLICIES) { + for (int to : ALL_POLICIES) { + if (from == POLICY_DIM && to != POLICY_BRIGHT) { + continue; + } + if (from == POLICY_BRIGHT && to != POLICY_DIM) { + continue; + } + mScreenUndimDetector.mCurrentScreenPolicy = POLICY_OFF; + mScreenUndimDetector.mUndimCounter = 1; + mScreenUndimDetector.mUndimCounterStartedMillis = + SystemClock.currentThreadTimeMillis(); + + mScreenUndimDetector.recordScreenPolicy(from); + mScreenUndimDetector.recordScreenPolicy(to); + + assertThat(mScreenUndimDetector.mUndimCounter).isNotEqualTo(0); + assertThat(mScreenUndimDetector.mUndimCounterStartedMillis).isNotEqualTo(0); + } + } + } +} diff --git a/services/tests/mockingservicestests/src/com/android/server/sensorprivacy/SensorPrivacyServiceMockingTest.java b/services/tests/mockingservicestests/src/com/android/server/sensorprivacy/SensorPrivacyServiceMockingTest.java index 844687f34555..ba79a764b672 100644 --- a/services/tests/mockingservicestests/src/com/android/server/sensorprivacy/SensorPrivacyServiceMockingTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/sensorprivacy/SensorPrivacyServiceMockingTest.java @@ -60,6 +60,8 @@ public class SensorPrivacyServiceMockingTest { String.format(PERSISTENCE_FILE_PATHS_TEMPLATE, 4); public static final String PERSISTENCE_FILE5 = String.format(PERSISTENCE_FILE_PATHS_TEMPLATE, 5); + public static final String PERSISTENCE_FILE6 = + String.format(PERSISTENCE_FILE_PATHS_TEMPLATE, 6); private Context mContext; @Mock @@ -111,6 +113,7 @@ public class SensorPrivacyServiceMockingTest { initServiceWithPersistenceFile(onDeviceFile, PERSISTENCE_FILE3); initServiceWithPersistenceFile(onDeviceFile, PERSISTENCE_FILE4); initServiceWithPersistenceFile(onDeviceFile, PERSISTENCE_FILE5); + initServiceWithPersistenceFile(onDeviceFile, PERSISTENCE_FILE6); // Try all files with two known users doReturn(new int[]{0, 10}).when(mMockedUserManagerInternal).getUserIds(); @@ -124,6 +127,7 @@ public class SensorPrivacyServiceMockingTest { initServiceWithPersistenceFile(onDeviceFile, PERSISTENCE_FILE3); initServiceWithPersistenceFile(onDeviceFile, PERSISTENCE_FILE4); initServiceWithPersistenceFile(onDeviceFile, PERSISTENCE_FILE5); + initServiceWithPersistenceFile(onDeviceFile, PERSISTENCE_FILE6); } finally { mockitoSession.finishMocking(); diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml index bcb2cf8fa0c3..68b84693f0ef 100644 --- a/services/tests/servicestests/AndroidManifest.xml +++ b/services/tests/servicestests/AndroidManifest.xml @@ -71,6 +71,7 @@ <uses-permission android:name="android.permission.MANAGE_BIND_INSTANT_SERVICE"/> <uses-permission android:name="android.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS"/> <uses-permission android:name="android.permission.CONTROL_DISPLAY_BRIGHTNESS"/> + <uses-permission android:name="android.permission.CONFIGURE_DISPLAY_BRIGHTNESS"/> <uses-permission android:name="android.permission.READ_DEVICE_CONFIG"/> <uses-permission android:name="android.permission.WRITE_DEVICE_CONFIG"/> <uses-permission android:name="android.permission.HARDWARE_TEST"/> 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 f4d14995f7c7..a41f79e8f682 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 @@ -348,6 +348,17 @@ public class BiometricSchedulerTest { verify((Interruptable) interruptableMonitor, never()).cancel(); } + @Test + public void testClientDestroyed_afterFinish() { + final HalClientMonitor.LazyDaemon<Object> nonNullDaemon = () -> mock(Object.class); + final TestClientMonitor client = + new TestClientMonitor(mContext, mToken, nonNullDaemon); + mScheduler.scheduleClientMonitor(client); + client.mCallback.onClientFinished(client, true /* success */); + waitForIdle(); + assertTrue(client.wasDestroyed()); + } + private BiometricSchedulerProto getDump(boolean clearSchedulerBuffer) throws Exception { return BiometricSchedulerProto.parseFrom(mScheduler.dumpProtoState(clearSchedulerBuffer)); } @@ -437,6 +448,7 @@ public class BiometricSchedulerTest { private static class TestClientMonitor extends HalClientMonitor<Object> { private boolean mUnableToStart; private boolean mStarted; + private boolean mDestroyed; public TestClientMonitor(@NonNull Context context, @NonNull IBinder token, @NonNull LazyDaemon<Object> lazyDaemon) { @@ -475,6 +487,11 @@ public class BiometricSchedulerTest { } + @Override + public void destroy() { + mDestroyed = true; + } + public boolean wasUnableToStart() { return mUnableToStart; } @@ -482,6 +499,11 @@ public class BiometricSchedulerTest { public boolean hasStarted() { return mStarted; } + + public boolean wasDestroyed() { + return mDestroyed; + } + } private static void waitForIdle() { diff --git a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java index ff8fbda6c83e..b1b6e5341f38 100644 --- a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java @@ -18,6 +18,7 @@ package com.android.server.devicestate; import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE; +import static org.mockito.Mockito.when; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertFalse; import static org.testng.Assert.assertNotNull; @@ -35,6 +36,11 @@ import android.platform.test.annotations.Presubmit; import androidx.test.InstrumentationRegistry; import androidx.test.runner.AndroidJUnit4; +import static org.mockito.Mockito.mock; + +import com.android.server.wm.ActivityTaskManagerInternal; +import com.android.server.wm.WindowProcessController; + import junit.framework.Assert; import org.junit.Before; @@ -55,10 +61,15 @@ import javax.annotation.Nullable; @Presubmit @RunWith(AndroidJUnit4.class) public final class DeviceStateManagerServiceTest { - private static final DeviceState DEFAULT_DEVICE_STATE = new DeviceState(0, "DEFAULT"); - private static final DeviceState OTHER_DEVICE_STATE = new DeviceState(1, "OTHER"); + private static final DeviceState DEFAULT_DEVICE_STATE = + new DeviceState(0, "DEFAULT", 0 /* flags */); + private static final DeviceState OTHER_DEVICE_STATE = + new DeviceState(1, "OTHER", 0 /* flags */); // A device state that is not reported as being supported for the default test provider. - private static final DeviceState UNSUPPORTED_DEVICE_STATE = new DeviceState(255, "UNSUPPORTED"); + private static final DeviceState UNSUPPORTED_DEVICE_STATE = + new DeviceState(255, "UNSUPPORTED", 0 /* flags */); + + private static final int FAKE_PROCESS_ID = 100; private TestDeviceStatePolicy mPolicy; private TestDeviceStateProvider mProvider; @@ -69,6 +80,25 @@ public final class DeviceStateManagerServiceTest { mProvider = new TestDeviceStateProvider(); mPolicy = new TestDeviceStatePolicy(mProvider); mService = new DeviceStateManagerService(InstrumentationRegistry.getContext(), mPolicy); + + // Necessary to allow us to check for top app process id in tests + mService.mActivityTaskManagerInternal = mock(ActivityTaskManagerInternal.class); + WindowProcessController windowProcessController = mock(WindowProcessController.class); + when(mService.mActivityTaskManagerInternal.getTopApp()) + .thenReturn(windowProcessController); + when(windowProcessController.getPid()).thenReturn(FAKE_PROCESS_ID); + + flushHandler(); // Flush the handler to ensure the initial values are committed. + } + + private void flushHandler() { + flushHandler(1); + } + + private void flushHandler(int count) { + for (int i = 0; i < count; i++) { + mService.getHandler().runWithScissors(() -> {}, 0); + } } @Test @@ -80,6 +110,7 @@ public final class DeviceStateManagerServiceTest { DEFAULT_DEVICE_STATE.getIdentifier()); mProvider.setState(OTHER_DEVICE_STATE.getIdentifier()); + flushHandler(); assertEquals(mService.getCommittedState(), Optional.of(OTHER_DEVICE_STATE)); assertEquals(mService.getPendingState(), Optional.empty()); assertEquals(mService.getBaseState(), Optional.of(OTHER_DEVICE_STATE)); @@ -92,6 +123,7 @@ public final class DeviceStateManagerServiceTest { mPolicy.blockConfigure(); mProvider.setState(OTHER_DEVICE_STATE.getIdentifier()); + flushHandler(); assertEquals(mService.getCommittedState(), Optional.of(DEFAULT_DEVICE_STATE)); assertEquals(mService.getPendingState(), Optional.of(OTHER_DEVICE_STATE)); assertEquals(mService.getBaseState(), Optional.of(OTHER_DEVICE_STATE)); @@ -99,6 +131,7 @@ public final class DeviceStateManagerServiceTest { OTHER_DEVICE_STATE.getIdentifier()); mProvider.setState(DEFAULT_DEVICE_STATE.getIdentifier()); + flushHandler(); assertEquals(mService.getCommittedState(), Optional.of(DEFAULT_DEVICE_STATE)); assertEquals(mService.getPendingState(), Optional.of(OTHER_DEVICE_STATE)); assertEquals(mService.getBaseState(), Optional.of(DEFAULT_DEVICE_STATE)); @@ -106,6 +139,7 @@ public final class DeviceStateManagerServiceTest { OTHER_DEVICE_STATE.getIdentifier()); mPolicy.resumeConfigure(); + flushHandler(); assertEquals(mService.getCommittedState(), Optional.of(DEFAULT_DEVICE_STATE)); assertEquals(mService.getPendingState(), Optional.empty()); assertEquals(mService.getBaseState(), Optional.of(DEFAULT_DEVICE_STATE)); @@ -149,6 +183,7 @@ public final class DeviceStateManagerServiceTest { assertEquals(mService.getBaseState(), Optional.of(DEFAULT_DEVICE_STATE)); mProvider.notifySupportedDeviceStates(new DeviceState[]{ DEFAULT_DEVICE_STATE }); + flushHandler(); // The current committed and requests states do not change because the current state remains // supported. @@ -166,6 +201,7 @@ public final class DeviceStateManagerServiceTest { mService.getBinderService().registerCallback(callback); // An initial callback will be triggered on registration, so we clear it here. + flushHandler(); callback.clearLastNotifiedInfo(); assertEquals(mService.getCommittedState(), Optional.of(DEFAULT_DEVICE_STATE)); @@ -174,6 +210,7 @@ public final class DeviceStateManagerServiceTest { mProvider.notifySupportedDeviceStates(new DeviceState[]{ DEFAULT_DEVICE_STATE, OTHER_DEVICE_STATE }); + flushHandler(); // The current committed and requests states do not change because the current state remains // supported. @@ -203,12 +240,14 @@ public final class DeviceStateManagerServiceTest { mService.getBinderService().registerCallback(callback); mProvider.setState(OTHER_DEVICE_STATE.getIdentifier()); + flushHandler(); assertEquals(callback.getLastNotifiedInfo().baseState, OTHER_DEVICE_STATE.getIdentifier()); assertEquals(callback.getLastNotifiedInfo().currentState, OTHER_DEVICE_STATE.getIdentifier()); mProvider.setState(DEFAULT_DEVICE_STATE.getIdentifier()); + flushHandler(); assertEquals(callback.getLastNotifiedInfo().baseState, DEFAULT_DEVICE_STATE.getIdentifier()); assertEquals(callback.getLastNotifiedInfo().currentState, @@ -216,6 +255,7 @@ public final class DeviceStateManagerServiceTest { mPolicy.blockConfigure(); mProvider.setState(OTHER_DEVICE_STATE.getIdentifier()); + flushHandler(); // The callback should not have been notified of the state change as the policy is still // pending callback. assertEquals(callback.getLastNotifiedInfo().baseState, @@ -224,6 +264,7 @@ public final class DeviceStateManagerServiceTest { DEFAULT_DEVICE_STATE.getIdentifier()); mPolicy.resumeConfigure(); + flushHandler(); // Now that the policy is finished processing the callback should be notified of the state // change. assertEquals(callback.getLastNotifiedInfo().baseState, @@ -236,6 +277,7 @@ public final class DeviceStateManagerServiceTest { public void registerCallback_emitsInitialValue() throws RemoteException { TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback(); mService.getBinderService().registerCallback(callback); + flushHandler(); assertNotNull(callback.getLastNotifiedInfo()); assertEquals(callback.getLastNotifiedInfo().baseState, DEFAULT_DEVICE_STATE.getIdentifier()); @@ -247,6 +289,7 @@ public final class DeviceStateManagerServiceTest { public void requestState() throws RemoteException { TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback(); mService.getBinderService().registerCallback(callback); + flushHandler(); final IBinder token = new Binder(); assertEquals(callback.getLastNotifiedStatus(token), @@ -254,6 +297,10 @@ public final class DeviceStateManagerServiceTest { mService.getBinderService().requestState(token, OTHER_DEVICE_STATE.getIdentifier(), 0 /* flags */); + // Flush the handler twice. The first flush ensures the request is added and the policy is + // notified, while the second flush ensures the callback is notified once the change is + // committed. + flushHandler(2 /* count */); assertEquals(callback.getLastNotifiedStatus(token), TestDeviceStateManagerCallback.STATUS_ACTIVE); @@ -271,6 +318,7 @@ public final class DeviceStateManagerServiceTest { OTHER_DEVICE_STATE.getIdentifier()); mService.getBinderService().cancelRequest(token); + flushHandler(); assertEquals(callback.getLastNotifiedStatus(token), TestDeviceStateManagerCallback.STATUS_CANCELED); @@ -291,6 +339,7 @@ public final class DeviceStateManagerServiceTest { public void requestState_pendingStateAtRequest() throws RemoteException { TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback(); mService.getBinderService().registerCallback(callback); + flushHandler(); mPolicy.blockConfigure(); @@ -303,6 +352,10 @@ public final class DeviceStateManagerServiceTest { mService.getBinderService().requestState(firstRequestToken, OTHER_DEVICE_STATE.getIdentifier(), 0 /* flags */); + // Flush the handler twice. The first flush ensures the request is added and the policy is + // notified, while the second flush ensures the callback is notified once the change is + // committed. + flushHandler(2 /* count */); assertEquals(mService.getCommittedState(), Optional.of(DEFAULT_DEVICE_STATE)); assertEquals(mService.getPendingState(), Optional.of(OTHER_DEVICE_STATE)); @@ -312,8 +365,8 @@ public final class DeviceStateManagerServiceTest { mService.getBinderService().requestState(secondRequestToken, DEFAULT_DEVICE_STATE.getIdentifier(), 0 /* flags */); - mPolicy.resumeConfigureOnce(); + flushHandler(); // First request status is now suspended as there is another pending request. assertEquals(callback.getLastNotifiedStatus(firstRequestToken), @@ -330,6 +383,7 @@ public final class DeviceStateManagerServiceTest { DEFAULT_DEVICE_STATE.getIdentifier()); mPolicy.resumeConfigure(); + flushHandler(); assertEquals(mService.getCommittedState(), Optional.of(DEFAULT_DEVICE_STATE)); assertEquals(mService.getPendingState(), Optional.empty()); @@ -339,6 +393,7 @@ public final class DeviceStateManagerServiceTest { // Now cancel the second request to make the first request active. mService.getBinderService().cancelRequest(secondRequestToken); + flushHandler(); assertEquals(callback.getLastNotifiedStatus(firstRequestToken), TestDeviceStateManagerCallback.STATUS_ACTIVE); @@ -356,6 +411,7 @@ public final class DeviceStateManagerServiceTest { public void requestState_sameAsBaseState() throws RemoteException { TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback(); mService.getBinderService().registerCallback(callback); + flushHandler(); final IBinder token = new Binder(); assertEquals(callback.getLastNotifiedStatus(token), @@ -363,6 +419,7 @@ public final class DeviceStateManagerServiceTest { mService.getBinderService().requestState(token, DEFAULT_DEVICE_STATE.getIdentifier(), 0 /* flags */); + flushHandler(); assertEquals(callback.getLastNotifiedStatus(token), TestDeviceStateManagerCallback.STATUS_ACTIVE); @@ -372,6 +429,7 @@ public final class DeviceStateManagerServiceTest { public void requestState_flagCancelWhenBaseChanges() throws RemoteException { TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback(); mService.getBinderService().registerCallback(callback); + flushHandler(); final IBinder token = new Binder(); assertEquals(callback.getLastNotifiedStatus(token), @@ -379,6 +437,10 @@ public final class DeviceStateManagerServiceTest { mService.getBinderService().requestState(token, OTHER_DEVICE_STATE.getIdentifier(), DeviceStateRequest.FLAG_CANCEL_WHEN_BASE_CHANGES); + // Flush the handler twice. The first flush ensures the request is added and the policy is + // notified, while the second flush ensures the callback is notified once the change is + // committed. + flushHandler(2 /* count */); assertEquals(callback.getLastNotifiedStatus(token), TestDeviceStateManagerCallback.STATUS_ACTIVE); @@ -391,6 +453,7 @@ public final class DeviceStateManagerServiceTest { OTHER_DEVICE_STATE.getIdentifier()); mProvider.setState(OTHER_DEVICE_STATE.getIdentifier()); + flushHandler(); // Request is canceled because the base state changed. assertEquals(callback.getLastNotifiedStatus(token), @@ -407,6 +470,7 @@ public final class DeviceStateManagerServiceTest { public void requestState_becomesUnsupported() throws RemoteException { TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback(); mService.getBinderService().registerCallback(callback); + flushHandler(); final IBinder token = new Binder(); assertEquals(callback.getLastNotifiedStatus(token), @@ -414,6 +478,7 @@ public final class DeviceStateManagerServiceTest { mService.getBinderService().requestState(token, OTHER_DEVICE_STATE.getIdentifier(), 0 /* flags */); + flushHandler(); assertEquals(callback.getLastNotifiedStatus(token), TestDeviceStateManagerCallback.STATUS_ACTIVE); @@ -425,6 +490,7 @@ public final class DeviceStateManagerServiceTest { OTHER_DEVICE_STATE.getIdentifier()); mProvider.notifySupportedDeviceStates(new DeviceState[]{ DEFAULT_DEVICE_STATE }); + flushHandler(); // Request is canceled because the state is no longer supported. assertEquals(callback.getLastNotifiedStatus(token), diff --git a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateTest.java b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateTest.java index b5c8053ad77e..e286cb27cc41 100644 --- a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateTest.java +++ b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateTest.java @@ -41,24 +41,26 @@ public final class DeviceStateTest { @Test public void testConstruct() { final DeviceState state = new DeviceState(MINIMUM_DEVICE_STATE /* identifier */, - "CLOSED" /* name */); + "CLOSED" /* name */, DeviceState.FLAG_CANCEL_STICKY_REQUESTS /* flags */); assertEquals(state.getIdentifier(), MINIMUM_DEVICE_STATE); assertEquals(state.getName(), "CLOSED"); + assertEquals(state.getFlags(), DeviceState.FLAG_CANCEL_STICKY_REQUESTS); } @Test public void testConstruct_nullName() { final DeviceState state = new DeviceState(MAXIMUM_DEVICE_STATE /* identifier */, - null /* name */); + null /* name */, 0/* flags */); assertEquals(state.getIdentifier(), MAXIMUM_DEVICE_STATE); assertNull(state.getName()); + assertEquals(state.getFlags(), 0); } @Test public void testConstruct_tooLargeIdentifier() { assertThrows(IllegalArgumentException.class, () -> { final DeviceState state = new DeviceState(MAXIMUM_DEVICE_STATE + 1 /* identifier */, - null /* name */); + null /* name */, 0 /* flags */); }); } @@ -66,7 +68,7 @@ public final class DeviceStateTest { public void testConstruct_tooSmallIdentifier() { assertThrows(IllegalArgumentException.class, () -> { final DeviceState state = new DeviceState(MINIMUM_DEVICE_STATE - 1 /* identifier */, - null /* name */); + null /* name */, 0 /* flags */); }); } } diff --git a/services/tests/servicestests/src/com/android/server/devicestate/OverrideRequestControllerTest.java b/services/tests/servicestests/src/com/android/server/devicestate/OverrideRequestControllerTest.java new file mode 100644 index 000000000000..c9cf2f06640d --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/devicestate/OverrideRequestControllerTest.java @@ -0,0 +1,230 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.devicestate; + +import static com.android.server.devicestate.OverrideRequestController.STATUS_ACTIVE; +import static com.android.server.devicestate.OverrideRequestController.STATUS_CANCELED; +import static com.android.server.devicestate.OverrideRequestController.STATUS_SUSPENDED; + +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertNull; + +import android.annotation.Nullable; +import android.hardware.devicestate.DeviceStateRequest; +import android.os.Binder; +import android.platform.test.annotations.Presubmit; + +import androidx.annotation.NonNull; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.HashMap; +import java.util.Map; + +/** + * Unit tests for {@link OverrideRequestController}. + * <p/> + * Run with <code>atest OverrideRequestControllerTest</code>. + */ +@Presubmit +@RunWith(AndroidJUnit4.class) +public final class OverrideRequestControllerTest { + private TestStatusChangeListener mStatusListener; + private OverrideRequestController mController; + + @Before + public void setup() { + mStatusListener = new TestStatusChangeListener(); + mController = new OverrideRequestController(mStatusListener); + } + + @Test + public void addRequest() { + OverrideRequest request = new OverrideRequest(new Binder(), 0 /* pid */, + 0 /* requestedState */, 0 /* flags */); + assertNull(mStatusListener.getLastStatus(request)); + + mController.addRequest(request); + assertEquals(mStatusListener.getLastStatus(request).intValue(), STATUS_ACTIVE); + } + + @Test + public void addRequest_suspendExistingRequest() { + OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */, + 0 /* requestedState */, 0 /* flags */); + assertNull(mStatusListener.getLastStatus(firstRequest)); + + mController.addRequest(firstRequest); + assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_ACTIVE); + + OverrideRequest secondRequest = new OverrideRequest(new Binder(), 0 /* pid */, + 0 /* requestedState */, 0 /* flags */); + assertNull(mStatusListener.getLastStatus(secondRequest)); + + mController.addRequest(secondRequest); + assertEquals(mStatusListener.getLastStatus(secondRequest).intValue(), STATUS_ACTIVE); + assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_SUSPENDED); + } + + @Test + public void addRequest_cancelActiveRequest() { + OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */, + 0 /* requestedState */, 0 /* flags */); + OverrideRequest secondRequest = new OverrideRequest(new Binder(), 0 /* pid */, + 0 /* requestedState */, 0 /* flags */); + + mController.addRequest(firstRequest); + mController.addRequest(secondRequest); + + assertEquals(mStatusListener.getLastStatus(secondRequest).intValue(), STATUS_ACTIVE); + assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_SUSPENDED); + + mController.cancelRequest(secondRequest.getToken()); + + assertEquals(mStatusListener.getLastStatus(secondRequest).intValue(), STATUS_CANCELED); + assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_ACTIVE); + } + + @Test + public void addRequest_cancelSuspendedRequest() { + OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */, + 0 /* requestedState */, 0 /* flags */); + OverrideRequest secondRequest = new OverrideRequest(new Binder(), 0 /* pid */, + 0 /* requestedState */, 0 /* flags */); + + mController.addRequest(firstRequest); + mController.addRequest(secondRequest); + + assertEquals(mStatusListener.getLastStatus(secondRequest).intValue(), STATUS_ACTIVE); + assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_SUSPENDED); + + mController.cancelRequest(firstRequest.getToken()); + + assertEquals(mStatusListener.getLastStatus(secondRequest).intValue(), STATUS_ACTIVE); + assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_CANCELED); + } + + @Test + public void handleBaseStateChanged() { + OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */, + 0 /* requestedState */, 0 /* flags */); + OverrideRequest secondRequest = new OverrideRequest(new Binder(), 0 /* pid */, + 0 /* requestedState */, + DeviceStateRequest.FLAG_CANCEL_WHEN_BASE_CHANGES /* flags */); + + mController.addRequest(firstRequest); + mController.addRequest(secondRequest); + + assertEquals(mStatusListener.getLastStatus(secondRequest).intValue(), STATUS_ACTIVE); + assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_SUSPENDED); + + mController.handleBaseStateChanged(); + + assertEquals(mStatusListener.getLastStatus(secondRequest).intValue(), STATUS_CANCELED); + assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_ACTIVE); + } + + @Test + public void handleProcessDied() { + OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */, + 0 /* requestedState */, 0 /* flags */); + OverrideRequest secondRequest = new OverrideRequest(new Binder(), 1 /* pid */, + 0 /* requestedState */, 0 /* flags */); + + mController.addRequest(firstRequest); + mController.addRequest(secondRequest); + + assertEquals(mStatusListener.getLastStatus(secondRequest).intValue(), STATUS_ACTIVE); + assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_SUSPENDED); + + mController.handleProcessDied(1); + + assertEquals(mStatusListener.getLastStatus(secondRequest).intValue(), STATUS_CANCELED); + assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_ACTIVE); + + mController.handleProcessDied(0); + + assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_CANCELED); + } + + @Test + public void handleProcessDied_stickyRequests() { + mController.setStickyRequestsAllowed(true); + + OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */, + 0 /* requestedState */, 0 /* flags */); + OverrideRequest secondRequest = new OverrideRequest(new Binder(), 1 /* pid */, + 0 /* requestedState */, 0 /* flags */); + + mController.addRequest(firstRequest); + mController.addRequest(secondRequest); + + assertEquals(mStatusListener.getLastStatus(secondRequest).intValue(), STATUS_ACTIVE); + assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_SUSPENDED); + + mController.handleProcessDied(1); + + assertEquals(mStatusListener.getLastStatus(secondRequest).intValue(), STATUS_ACTIVE); + assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_SUSPENDED); + + mController.cancelStickyRequests(); + + assertEquals(mStatusListener.getLastStatus(secondRequest).intValue(), STATUS_CANCELED); + assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_ACTIVE); + } + + @Test + public void handleNewSupportedStates() { + OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */, + 1 /* requestedState */, 0 /* flags */); + OverrideRequest secondRequest = new OverrideRequest(new Binder(), 0 /* pid */, + 2 /* requestedState */, 0 /* flags */); + + mController.addRequest(firstRequest); + mController.addRequest(secondRequest); + + assertEquals(mStatusListener.getLastStatus(secondRequest).intValue(), STATUS_ACTIVE); + assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_SUSPENDED); + + mController.handleNewSupportedStates(new int[]{ 0, 1 }); + + assertEquals(mStatusListener.getLastStatus(secondRequest).intValue(), STATUS_CANCELED); + assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_ACTIVE); + + mController.handleNewSupportedStates(new int[]{ 0 }); + + assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_CANCELED); + } + + private static final class TestStatusChangeListener implements + OverrideRequestController.StatusChangeListener { + private Map<OverrideRequest, Integer> mLastStatusMap = new HashMap<>(); + + @Override + public void onStatusChanged(@NonNull OverrideRequest request, int newStatus) { + mLastStatusMap.put(request, newStatus); + } + + @Nullable + public Integer getLastStatus(OverrideRequest request) { + return mLastStatusMap.get(request); + } + } +} diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java index 5ba375b922e2..7c55716c5e99 100644 --- a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java @@ -885,6 +885,61 @@ public class DisplayManagerServiceTest { assertFalse(callback.mDisplayAddedCalled); } + + + @Test + public void testSettingTwoBrightnessConfigurationsOnMultiDisplay() { + Context mContext = InstrumentationRegistry.getInstrumentation().getContext(); + DisplayManager displayManager = mContext.getSystemService(DisplayManager.class); + + // get the first two internal displays + Display[] displays = displayManager.getDisplays(); + Display internalDisplayOne = null; + Display internalDisplayTwo = null; + for (Display display : displays) { + if (display.getType() == Display.TYPE_INTERNAL) { + if (internalDisplayOne == null) { + internalDisplayOne = display; + } else { + internalDisplayTwo = display; + break; + } + } + } + + // return if there are fewer than 2 displays on this device + if (internalDisplayOne == null || internalDisplayTwo == null) { + return; + } + + final String uniqueDisplayIdOne = internalDisplayOne.getUniqueId(); + final String uniqueDisplayIdTwo = internalDisplayTwo.getUniqueId(); + + BrightnessConfiguration configOne = + new BrightnessConfiguration.Builder( + new float[]{0.0f, 12345.0f}, new float[]{15.0f, 400.0f}) + .setDescription("model:1").build(); + BrightnessConfiguration configTwo = + new BrightnessConfiguration.Builder( + new float[]{0.0f, 6789.0f}, new float[]{12.0f, 300.0f}) + .setDescription("model:2").build(); + + displayManager.setBrightnessConfigurationForDisplay(configOne, + uniqueDisplayIdOne); + displayManager.setBrightnessConfigurationForDisplay(configTwo, + uniqueDisplayIdTwo); + + BrightnessConfiguration configFromOne = + displayManager.getBrightnessConfigurationForDisplay(uniqueDisplayIdOne); + BrightnessConfiguration configFromTwo = + displayManager.getBrightnessConfigurationForDisplay(uniqueDisplayIdTwo); + + assertNotNull(configFromOne); + assertEquals(configOne, configFromOne); + assertEquals(configTwo, configFromTwo); + + } + private void testDisplayInfoFrameRateOverrideModeCompat(boolean compatChangeEnabled) throws Exception { DisplayManagerService displayManager = diff --git a/services/tests/servicestests/src/com/android/server/display/PersistentDataStoreTest.java b/services/tests/servicestests/src/com/android/server/display/PersistentDataStoreTest.java index 196454bd32ce..57a9cb278c80 100644 --- a/services/tests/servicestests/src/com/android/server/display/PersistentDataStoreTest.java +++ b/services/tests/servicestests/src/com/android/server/display/PersistentDataStoreTest.java @@ -17,13 +17,16 @@ package com.android.server.display; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import android.content.Context; import android.hardware.display.BrightnessConfiguration; import android.util.Pair; +import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; @@ -144,15 +147,93 @@ public class PersistentDataStoreTest { } @Test + public void testStoreAndReloadOfDisplayBrightnessConfigurations() { + final String uniqueDisplayId = "test:123"; + int userSerial = 0; + String packageName = "pdsTestPackage"; + final float[] lux = { 0f, 10f }; + final float[] nits = {1f, 100f }; + final BrightnessConfiguration config = new BrightnessConfiguration.Builder(lux, nits) + .setDescription("a description") + .build(); + mDataStore.loadIfNeeded(); + assertNull(mDataStore.getBrightnessConfigurationForDisplayLocked(uniqueDisplayId, + userSerial)); + + DisplayDevice testDisplayDevice = new DisplayDevice(null, null, uniqueDisplayId, null) { + @Override + public boolean hasStableUniqueId() { + return true; + } + + @Override + public DisplayDeviceInfo getDisplayDeviceInfoLocked() { + return null; + } + }; + + mDataStore.setBrightnessConfigurationForDisplayLocked(config, testDisplayDevice, userSerial, + packageName); + + final ByteArrayOutputStream baos = new ByteArrayOutputStream(); + mInjector.setWriteStream(baos); + mDataStore.saveIfNeeded(); + assertTrue(mInjector.wasWriteSuccessful()); + TestInjector newInjector = new TestInjector(); + PersistentDataStore newDataStore = new PersistentDataStore(newInjector); + ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); + newInjector.setReadStream(bais); + newDataStore.loadIfNeeded(); + assertNotNull(newDataStore.getBrightnessConfigurationForDisplayLocked(uniqueDisplayId, + userSerial)); + assertEquals(mDataStore.getBrightnessConfigurationForDisplayLocked(uniqueDisplayId, + userSerial), newDataStore.getBrightnessConfigurationForDisplayLocked( + uniqueDisplayId, userSerial)); + } + + @Test + public void testSetBrightnessConfigurationFailsWithUnstableId() { + final String uniqueDisplayId = "test:123"; + int userSerial = 0; + String packageName = "pdsTestPackage"; + final float[] lux = { 0f, 10f }; + final float[] nits = {1f, 100f }; + final BrightnessConfiguration config = new BrightnessConfiguration.Builder(lux, nits) + .setDescription("a description") + .build(); + mDataStore.loadIfNeeded(); + assertNull(mDataStore.getBrightnessConfigurationForDisplayLocked(uniqueDisplayId, + userSerial)); + + DisplayDevice testDisplayDevice = new DisplayDevice(null, null, uniqueDisplayId, null) { + @Override + public boolean hasStableUniqueId() { + return false; + } + + @Override + public DisplayDeviceInfo getDisplayDeviceInfoLocked() { + return null; + } + }; + + assertFalse(mDataStore.setBrightnessConfigurationForDisplayLocked( + config, testDisplayDevice, userSerial, packageName)); + } + + @Test public void testStoreAndReloadOfBrightnessConfigurations() { final float[] lux = { 0f, 10f }; final float[] nits = {1f, 100f }; final BrightnessConfiguration config = new BrightnessConfiguration.Builder(lux, nits) .setDescription("a description") .build(); + Context context = InstrumentationRegistry.getInstrumentation().getContext(); + String packageName = context.getPackageName(); + mDataStore.loadIfNeeded(); assertNull(mDataStore.getBrightnessConfiguration(0 /*userSerial*/)); - mDataStore.setBrightnessConfigurationForUser(config, 0, "packagename"); + mDataStore.setBrightnessConfigurationForUser(config, 0, packageName); final ByteArrayOutputStream baos = new ByteArrayOutputStream(); mInjector.setWriteStream(baos); @@ -173,17 +254,18 @@ public class PersistentDataStoreTest { public void testNullBrightnessConfiguration() { final float[] lux = { 0f, 10f }; final float[] nits = {1f, 100f }; + int userSerial = 0; final BrightnessConfiguration config = new BrightnessConfiguration.Builder(lux, nits) .setDescription("a description") .build(); mDataStore.loadIfNeeded(); - assertNull(mDataStore.getBrightnessConfiguration(0 /*userSerial*/)); + assertNull(mDataStore.getBrightnessConfiguration(userSerial)); - mDataStore.setBrightnessConfigurationForUser(config, 0, "packagename"); - assertNotNull(mDataStore.getBrightnessConfiguration(0 /*userSerial*/)); + mDataStore.setBrightnessConfigurationForUser(config, userSerial, "packagename"); + assertNotNull(mDataStore.getBrightnessConfiguration(userSerial)); - mDataStore.setBrightnessConfigurationForUser(null, 0, "packagename"); - assertNull(mDataStore.getBrightnessConfiguration(0 /*userSerial*/)); + mDataStore.setBrightnessConfigurationForUser(null, userSerial, "packagename"); + assertNull(mDataStore.getBrightnessConfiguration(userSerial)); } public class TestInjector extends PersistentDataStore.Injector { diff --git a/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java b/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java index 4d2d2f1a4b7d..8e2c1f051279 100644 --- a/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java +++ b/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java @@ -150,8 +150,9 @@ public final class DeviceStateProviderImplTest { provider.setListener(listener); verify(listener).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture()); - final DeviceState[] expectedStates = new DeviceState[]{ new DeviceState(1, ""), - new DeviceState(2, "") }; + final DeviceState[] expectedStates = new DeviceState[]{ + new DeviceState(1, "", 0 /* flags */), + new DeviceState(2, "", 0 /* flags */) }; assertArrayEquals(expectedStates, mDeviceStateArrayCaptor.getValue()); verify(listener).onStateChanged(mIntegerCaptor.capture()); @@ -187,8 +188,9 @@ public final class DeviceStateProviderImplTest { provider.setListener(listener); verify(listener).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture()); - final DeviceState[] expectedStates = new DeviceState[]{ new DeviceState(1, ""), - new DeviceState(2, "CLOSED") }; + final DeviceState[] expectedStates = new DeviceState[]{ + new DeviceState(1, "", 0 /* flags */), + new DeviceState(2, "CLOSED", 0 /* flags */) }; assertArrayEquals(expectedStates, mDeviceStateArrayCaptor.getValue()); // onStateChanged() should not be called because the provider has not yet been notified of @@ -264,8 +266,11 @@ public final class DeviceStateProviderImplTest { verify(listener).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture()); assertArrayEquals( - new DeviceState[]{ new DeviceState(1, "CLOSED"), new DeviceState(2, "HALF_OPENED"), - new DeviceState(3, "OPENED") }, mDeviceStateArrayCaptor.getValue()); + new DeviceState[]{ + new DeviceState(1, "CLOSED", 0 /* flags */), + new DeviceState(2, "HALF_OPENED", 0 /* flags */), + new DeviceState(3, "OPENED", 0 /* flags */) }, + mDeviceStateArrayCaptor.getValue()); // onStateChanged() should not be called because the provider has not yet been notified of // the initial sensor state. verify(listener, never()).onStateChanged(mIntegerCaptor.capture()); @@ -350,8 +355,10 @@ public final class DeviceStateProviderImplTest { verify(listener).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture()); assertArrayEquals( - new DeviceState[]{ new DeviceState(1, "CLOSED"), new DeviceState(2, "HALF_OPENED"), - }, mDeviceStateArrayCaptor.getValue()); + new DeviceState[]{ + new DeviceState(1, "CLOSED", 0 /* flags */), + new DeviceState(2, "HALF_OPENED", 0 /* flags */) + }, mDeviceStateArrayCaptor.getValue()); // onStateChanged() should be called because the provider could not find the sensor. verify(listener).onStateChanged(mIntegerCaptor.capture()); assertEquals(1, mIntegerCaptor.getValue().intValue()); diff --git a/services/tests/servicestests/src/com/android/server/power/NotifierTest.java b/services/tests/servicestests/src/com/android/server/power/NotifierTest.java index 5012ca9ab420..6e3f754e4882 100644 --- a/services/tests/servicestests/src/com/android/server/power/NotifierTest.java +++ b/services/tests/servicestests/src/com/android/server/power/NotifierTest.java @@ -210,7 +210,7 @@ public class NotifierTest { @Override Notifier createNotifier(Looper looper, Context context, IBatteryStats batteryStats, SuspendBlocker suspendBlocker, WindowManagerPolicy policy, - FaceDownDetector faceDownDetector) { + FaceDownDetector faceDownDetector, ScreenUndimDetector screenUndimDetector) { return mNotifierMock; } @@ -298,6 +298,7 @@ public class NotifierTest { BatteryStats.SERVICE_NAME)), mInjector.createSuspendBlocker(mService, "testBlocker"), null, + null, null); } } diff --git a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java index 5eabc1bea148..3d64d4b5abb9 100644 --- a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java @@ -215,7 +215,7 @@ public class PowerManagerServiceTest { @Override Notifier createNotifier(Looper looper, Context context, IBatteryStats batteryStats, SuspendBlocker suspendBlocker, WindowManagerPolicy policy, - FaceDownDetector faceDownDetector) { + FaceDownDetector faceDownDetector, ScreenUndimDetector screenUndimDetector) { return mNotifierMock; } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index f57c416e4a97..7bbf3e6c3b2e 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -4793,6 +4793,52 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } @Test + public void testSetNotificationsShownFromListener_protectsCrossUserInformation() + throws RemoteException { + Notification.Builder nb = new Notification.Builder( + mContext, mTestNotificationChannel.getId()) + .setContentTitle("foo") + .setSmallIcon(android.R.drawable.sym_def_app_icon); + StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1, + "tag" + System.currentTimeMillis(), UserHandle.PER_USER_RANGE, 0, + nb.build(), UserHandle.getUserHandleForUid(mUid + UserHandle.PER_USER_RANGE), + null, 0); + final NotificationRecord r = + new NotificationRecord(mContext, sbn, mTestNotificationChannel); + r.setTextChanged(true); + mService.addNotification(r); + + // no security exception! + mBinderService.setNotificationsShownFromListener(null, new String[] {r.getKey()}); + + verify(mAppUsageStats, never()).reportInterruptiveNotification( + anyString(), anyString(), anyInt()); + } + + @Test + public void testCancelNotificationsFromListener_protectsCrossUserInformation() + throws RemoteException { + Notification.Builder nb = new Notification.Builder( + mContext, mTestNotificationChannel.getId()) + .setContentTitle("foo") + .setSmallIcon(android.R.drawable.sym_def_app_icon); + StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1, + "tag" + System.currentTimeMillis(), UserHandle.PER_USER_RANGE, 0, + nb.build(), UserHandle.getUserHandleForUid(mUid + UserHandle.PER_USER_RANGE), + null, 0); + final NotificationRecord r = + new NotificationRecord(mContext, sbn, mTestNotificationChannel); + r.setTextChanged(true); + mService.addNotification(r); + + // no security exception! + mBinderService.cancelNotificationsFromListener(null, new String[] {r.getKey()}); + + waitForIdle(); + assertEquals(1, mService.getNotificationRecordCount()); + } + + @Test public void testMaybeRecordInterruptionLocked_doesNotRecordTwice() throws RemoteException { final NotificationRecord r = generateNotificationRecord( diff --git a/services/tests/wmtests/src/com/android/server/policy/SingleKeyGestureTests.java b/services/tests/wmtests/src/com/android/server/policy/SingleKeyGestureTests.java index 222c692a9778..6b36fe808a2e 100644 --- a/services/tests/wmtests/src/com/android/server/policy/SingleKeyGestureTests.java +++ b/services/tests/wmtests/src/com/android/server/policy/SingleKeyGestureTests.java @@ -69,7 +69,7 @@ public class SingleKeyGestureTests { @Before public void setUp() { - mDetector = new SingleKeyGestureDetector(mContext); + mDetector = new SingleKeyGestureDetector(); initSingleKeyGestureRules(); mWaitTimeout = ViewConfiguration.getMultiPressTimeout() + 50; mLongPressTime = ViewConfiguration.get(mContext).getDeviceGlobalActionKeyTimeout() + 50; @@ -78,7 +78,7 @@ public class SingleKeyGestureTests { } private void initSingleKeyGestureRules() { - mDetector.addRule(new SingleKeyGestureDetector.SingleKeyRule(KEYCODE_POWER, + mDetector.addRule(new SingleKeyGestureDetector.SingleKeyRule(mContext, KEYCODE_POWER, KEY_LONGPRESS | KEY_VERYLONGPRESS) { @Override int getMaxMultiPressCount() { diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java index b282cd7ce1cb..0b918025bb50 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java @@ -404,6 +404,7 @@ public class ActivityMetricsLaunchObserverTests extends WindowTestsBase { // Another round without setting visibility of the trampoline activity. onActivityLaunchedTrampoline(); + mTrampolineActivity.setState(ActivityRecord.State.PAUSING, "test"); notifyWindowsDrawn(mTopActivity); // If the transition can start, the invisible activities should be discarded and the launch // event be reported successfully. @@ -475,6 +476,16 @@ public class ActivityMetricsLaunchObserverTests extends WindowTestsBase { } @Test + public void testConsecutiveLaunch() { + mTrampolineActivity.setState(ActivityRecord.State.INITIALIZING, "test"); + onActivityLaunched(mTrampolineActivity); + mActivityMetricsLogger.notifyActivityLaunching(mTopActivity.intent, + mTrampolineActivity /* caller */, mTrampolineActivity.getUid()); + notifyActivityLaunched(START_SUCCESS, mTopActivity); + transitToDrawnAndVerifyOnLaunchFinished(mTopActivity); + } + + @Test public void testConsecutiveLaunchNewTask() { final IBinder launchCookie = mock(IBinder.class); final WindowContainerToken launchRootTask = mock(WindowContainerToken.class); diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java index 9ca09d20cd49..9ad479a261ef 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java @@ -40,6 +40,7 @@ import static android.os.Process.NOBODY_UID; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW; import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW; +import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; import static android.view.WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD; import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; @@ -218,7 +219,7 @@ public class ActivityRecordTests extends WindowTestsBase { public void testNoCleanupMovingActivityInSameStack() { final ActivityRecord activity = createActivityWith2LevelTask(); final Task rootTask = activity.getRootTask(); - final Task newTask = new TaskBuilder(mAtm.mTaskSupervisor).setParentTask(rootTask).build(); + final Task newTask = createTaskInRootTask(rootTask, 0 /* userId */); activity.reparent(newTask, 0, null /*reason*/); verify(rootTask, times(0)).cleanUpActivityReferences(any()); } @@ -2509,8 +2510,10 @@ public class ActivityRecordTests extends WindowTestsBase { @Test public void testTransferStartingWindow() { registerTestStartingWindowOrganizer(); - final ActivityRecord activity1 = new ActivityBuilder(mAtm).setCreateTask(true).build(); - final ActivityRecord activity2 = new ActivityBuilder(mAtm).setCreateTask(true).build(); + final ActivityRecord activity1 = new ActivityBuilder(mAtm).setCreateTask(true) + .setVisible(false).build(); + final ActivityRecord activity2 = new ActivityBuilder(mAtm).setCreateTask(true) + .setVisible(false).build(); activity1.addStartingWindow(mPackageName, android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true, false, false); @@ -2519,6 +2522,7 @@ public class ActivityRecordTests extends WindowTestsBase { android.R.style.Theme, null, "Test", 0, 0, 0, 0, activity1, true, true, false, true, false, false); waitUntilHandlersIdle(); + assertFalse(mDisplayContent.mSkipAppTransitionAnimation); assertNoStartingWindow(activity1); assertHasStartingWindow(activity2); } @@ -2626,6 +2630,7 @@ public class ActivityRecordTests extends WindowTestsBase { false /* newTask */, false /* isTaskSwitch */, null /* options */, null /* sourceRecord */); + assertTrue(mDisplayContent.mSkipAppTransitionAnimation); assertNull(middle.mStartingWindow); assertHasStartingWindow(top); assertTrue(top.isVisible()); @@ -2907,6 +2912,73 @@ public class ActivityRecordTests extends WindowTestsBase { assertFalse(activity.mDisplayContent.mClosingApps.contains(activity)); } + @Test + public void testImeInsetsFrozenFlag_resetWhenReparented() { + final ActivityRecord activity = createActivityWithTask(); + final WindowState app = createWindow(null, TYPE_APPLICATION, activity, "app"); + final WindowState imeWindow = createWindow(null, TYPE_APPLICATION, "imeWindow"); + final Task newTask = new TaskBuilder(mSupervisor).build(); + makeWindowVisible(app, imeWindow); + mDisplayContent.mInputMethodWindow = imeWindow; + mDisplayContent.setImeLayeringTarget(app); + mDisplayContent.setImeInputTarget(app); + + // Simulate app is closing and expect the last IME is shown and IME insets is frozen. + app.mActivityRecord.commitVisibility(false, false); + assertTrue(app.mActivityRecord.mLastImeShown); + assertTrue(app.mActivityRecord.mImeInsetsFrozenUntilStartInput); + + // Expect IME insets frozen state will reset when the activity is reparent to the new task. + activity.setState(RESUMED, "test"); + activity.reparent(newTask, 0 /* top */, "test"); + assertFalse(app.mActivityRecord.mImeInsetsFrozenUntilStartInput); + } + + @UseTestDisplay(addWindows = W_INPUT_METHOD) + @Test + public void testImeInsetsFrozenFlag_resetWhenResized() { + final WindowState app = createWindow(null, TYPE_APPLICATION, "app"); + makeWindowVisibleAndDrawn(app, mImeWindow); + mDisplayContent.setImeLayeringTarget(app); + mDisplayContent.setImeInputTarget(app); + + // Simulate app is closing and expect the last IME is shown and IME insets is frozen. + app.mActivityRecord.commitVisibility(false, false); + assertTrue(app.mActivityRecord.mLastImeShown); + assertTrue(app.mActivityRecord.mImeInsetsFrozenUntilStartInput); + + // Expect IME insets frozen state will reset when the activity is reparent to the new task. + app.mActivityRecord.onResize(); + assertFalse(app.mActivityRecord.mImeInsetsFrozenUntilStartInput); + } + + @UseTestDisplay(addWindows = W_INPUT_METHOD) + @Test + public void testImeInsetsFrozenFlag_resetWhenNoImeFocusableInActivity() { + final WindowState app = createWindow(null, TYPE_APPLICATION, "app"); + makeWindowVisibleAndDrawn(app, mImeWindow); + mDisplayContent.setImeLayeringTarget(app); + mDisplayContent.setImeInputTarget(app); + + // Simulate app is closing and expect the last IME is shown and IME insets is frozen. + app.mActivityRecord.commitVisibility(false, false); + app.mActivityRecord.onWindowsGone(); + + assertTrue(app.mActivityRecord.mLastImeShown); + assertTrue(app.mActivityRecord.mImeInsetsFrozenUntilStartInput); + + // Expect IME insets frozen state will reset when the activity has no IME focusable window. + app.mActivityRecord.forAllWindowsUnchecked(w -> { + w.mAttrs.flags |= FLAG_ALT_FOCUSABLE_IM; + return true; + }, true); + + app.mActivityRecord.commitVisibility(true, false); + app.mActivityRecord.onWindowsVisible(); + + assertFalse(app.mActivityRecord.mImeInsetsFrozenUntilStartInput); + } + private void assertHasStartingWindow(ActivityRecord atoken) { assertNotNull(atoken.mStartingSurface); assertNotNull(atoken.mStartingData); diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java index 1b4d0a4ab5a4..3e8a2e9b7b17 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java @@ -478,7 +478,7 @@ public class ActivityStarterTests extends WindowTestsBase { final ActivityRecord splitSecondActivity = new ActivityBuilder(mAtm).setCreateTask(true).build(); final ActivityRecord splitPrimaryActivity = new TaskBuilder(mSupervisor) - .setParentTask(splitOrg.mPrimary) + .setParentTaskFragment(splitOrg.mPrimary) .setCreateActivity(true) .build() .getTopMostActivity(); @@ -856,7 +856,7 @@ public class ActivityStarterTests extends WindowTestsBase { // Create another activity on top of the secondary display. final Task topStack = secondaryTaskContainer.createRootTask(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); - final Task topTask = new TaskBuilder(mSupervisor).setParentTask(topStack).build(); + final Task topTask = new TaskBuilder(mSupervisor).setParentTaskFragment(topStack).build(); new ActivityBuilder(mAtm).setTask(topTask).build(); doReturn(mActivityMetricsLogger).when(mSupervisor).getActivityMetricsLogger(); @@ -920,7 +920,7 @@ public class ActivityStarterTests extends WindowTestsBase { DEFAULT_COMPONENT_PACKAGE_NAME + ".SingleTaskActivity"); final Task task = new TaskBuilder(mSupervisor) .setComponent(componentName) - .setParentTask(stack) + .setParentTaskFragment(stack) .build(); return new ActivityBuilder(mAtm) .setComponent(componentName) @@ -1056,8 +1056,8 @@ public class ActivityStarterTests extends WindowTestsBase { final ActivityStarter starter = prepareStarter(0 /* flags */); starter.mStartActivity = new ActivityBuilder(mAtm).build(); final Task task = new TaskBuilder(mAtm.mTaskSupervisor) - .setParentTask(mAtm.mRootWindowContainer.getDefaultTaskDisplayArea().createRootTask( - WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */)) + .setParentTaskFragment(createTask(mDisplayContent, WINDOWING_MODE_FULLSCREEN, + ACTIVITY_TYPE_STANDARD)) .setUserId(10) .build(); diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java index 0d177c14427b..26a68821a672 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java @@ -49,6 +49,7 @@ import androidx.test.filters.MediumTest; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentMatchers; import java.util.concurrent.TimeUnit; @@ -108,16 +109,18 @@ public class ActivityTaskSupervisorTests extends WindowTestsBase { final ActivityMetricsLogger.LaunchingState launchingState = new ActivityMetricsLogger.LaunchingState(); spyOn(launchingState); - doReturn(true).when(launchingState).contains(eq(secondActivity)); + doReturn(true).when(launchingState).hasActiveTransitionInfo(); + doReturn(true).when(launchingState).contains( + ArgumentMatchers.argThat(r -> r == firstActivity || r == secondActivity)); // The test case already runs inside global lock, so above thread can only execute after // this waiting method that releases the lock. mSupervisor.waitActivityVisibleOrLaunched(taskToFrontWait, firstActivity, launchingState); // Assert that the thread is finished. assertTrue(condition.block(TIMEOUT_MS)); - assertEquals(taskToFrontWait.result, START_TASK_TO_FRONT); - assertEquals(taskToFrontWait.who, secondActivity.mActivityComponent); - assertEquals(taskToFrontWait.launchState, WaitResult.LAUNCH_STATE_HOT); + assertEquals(START_TASK_TO_FRONT, taskToFrontWait.result); + assertEquals(secondActivity.mActivityComponent, taskToFrontWait.who); + assertEquals(WaitResult.LAUNCH_STATE_HOT, taskToFrontWait.launchState); // START_TASK_TO_FRONT means that another component will be visible, so the component // should not be assigned as the first activity. assertNull(launchedComponent[0]); 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 3a6aac9d03d5..11e3fd4e050a 100644 --- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java @@ -375,7 +375,7 @@ public class AppTransitionControllerTest extends WindowTestsBase { final ArraySet<ActivityRecord> closing = new ArraySet<>(); closing.add(activity3); - // Promote animation targets to TaskStack level. Invisible ActivityRecords don't affect + // Promote animation targets to root Task level. Invisible ActivityRecords don't affect // promotion decision. assertEquals( new ArraySet<>(new WindowContainer[]{activity1.getRootTask()}), @@ -520,6 +520,128 @@ public class AppTransitionControllerTest extends WindowTestsBase { opening, closing, false /* visible */)); } + @Test + public void testGetAnimationTargets_openingClosingTaskFragment() { + // [DefaultTDA] - [Task] -+- [TaskFragment1] - [ActivityRecord1] (opening, invisible) + // +- [TaskFragment2] - [ActivityRecord2] (closing, visible) + final Task parentTask = createTask(mDisplayContent); + final TaskFragment taskFragment1 = createTaskFragmentWithParentTask(parentTask, + false /* createEmbeddedTask */); + final ActivityRecord activity1 = taskFragment1.getTopMostActivity(); + activity1.setVisible(false); + activity1.mVisibleRequested = true; + + final TaskFragment taskFragment2 = createTaskFragmentWithParentTask(parentTask, + false /* createEmbeddedTask */); + final ActivityRecord activity2 = taskFragment2.getTopMostActivity(); + activity2.setVisible(true); + activity2.mVisibleRequested = false; + + final ArraySet<ActivityRecord> opening = new ArraySet<>(); + opening.add(activity1); + final ArraySet<ActivityRecord> closing = new ArraySet<>(); + closing.add(activity2); + + // Promote animation targets up to TaskFragment level, not beyond. + assertEquals(new ArraySet<>(new WindowContainer[]{taskFragment1}), + AppTransitionController.getAnimationTargets( + opening, closing, true /* visible */)); + assertEquals(new ArraySet<>(new WindowContainer[]{taskFragment2}), + AppTransitionController.getAnimationTargets( + opening, closing, false /* visible */)); + } + + @Test + public void testGetAnimationTargets_openingClosingTaskFragmentWithEmbeddedTask() { + // [DefaultTDA] - [Task] -+- [TaskFragment1] - [ActivityRecord1] (opening, invisible) + // +- [TaskFragment2] - [ActivityRecord2] (closing, visible) + final Task parentTask = createTask(mDisplayContent); + final TaskFragment taskFragment1 = createTaskFragmentWithParentTask(parentTask, + true /* createEmbeddedTask */); + final ActivityRecord activity1 = taskFragment1.getTopMostActivity(); + activity1.setVisible(false); + activity1.mVisibleRequested = true; + + final TaskFragment taskFragment2 = createTaskFragmentWithParentTask(parentTask, + true /* createEmbeddedTask */); + final ActivityRecord activity2 = taskFragment2.getTopMostActivity(); + activity2.setVisible(true); + activity2.mVisibleRequested = false; + + final ArraySet<ActivityRecord> opening = new ArraySet<>(); + opening.add(activity1); + final ArraySet<ActivityRecord> closing = new ArraySet<>(); + closing.add(activity2); + + // Promote animation targets up to TaskFragment level, not beyond. + assertEquals(new ArraySet<>(new WindowContainer[]{taskFragment1}), + AppTransitionController.getAnimationTargets( + opening, closing, true /* visible */)); + assertEquals(new ArraySet<>(new WindowContainer[]{taskFragment2}), + AppTransitionController.getAnimationTargets( + opening, closing, false /* visible */)); + } + + @Test + public void testGetAnimationTargets_openingTheOnlyTaskFragmentInTask() { + // [DefaultTDA] -+- [Task1] - [TaskFragment1] - [ActivityRecord1] (opening, invisible) + // +- [Task2] - [ActivityRecord2] (closing, visible) + final Task task1 = createTask(mDisplayContent); + final TaskFragment taskFragment1 = createTaskFragmentWithParentTask(task1, + false /* createEmbeddedTask */); + final ActivityRecord activity1 = taskFragment1.getTopMostActivity(); + activity1.setVisible(false); + activity1.mVisibleRequested = true; + + final ActivityRecord activity2 = createActivityRecord(mDisplayContent); + activity2.setVisible(true); + activity2.mVisibleRequested = false; + + final ArraySet<ActivityRecord> opening = new ArraySet<>(); + opening.add(activity1); + final ArraySet<ActivityRecord> closing = new ArraySet<>(); + closing.add(activity2); + + // Promote animation targets up to leaf Task level because there's only one TaskFragment in + // the Task. + assertEquals(new ArraySet<>(new WindowContainer[]{task1}), + AppTransitionController.getAnimationTargets( + opening, closing, true /* visible */)); + assertEquals(new ArraySet<>(new WindowContainer[]{activity2.getTask()}), + AppTransitionController.getAnimationTargets( + opening, closing, false /* visible */)); + } + + @Test + public void testGetAnimationTargets_closingTheOnlyTaskFragmentInTask() { + // [DefaultTDA] -+- [Task1] - [TaskFragment1] - [ActivityRecord1] (closing, visible) + // +- [Task2] - [ActivityRecord2] (opening, invisible) + final Task task1 = createTask(mDisplayContent); + final TaskFragment taskFragment1 = createTaskFragmentWithParentTask(task1, + false /* createEmbeddedTask */); + final ActivityRecord activity1 = taskFragment1.getTopMostActivity(); + activity1.setVisible(true); + activity1.mVisibleRequested = false; + + final ActivityRecord activity2 = createActivityRecord(mDisplayContent); + activity2.setVisible(false); + activity2.mVisibleRequested = true; + + final ArraySet<ActivityRecord> opening = new ArraySet<>(); + opening.add(activity2); + final ArraySet<ActivityRecord> closing = new ArraySet<>(); + closing.add(activity1); + + // Promote animation targets up to leaf Task level because there's only one TaskFragment in + // the Task. + assertEquals(new ArraySet<>(new WindowContainer[]{activity2.getTask()}), + AppTransitionController.getAnimationTargets( + opening, closing, true /* visible */)); + assertEquals(new ArraySet<>(new WindowContainer[]{task1}), + AppTransitionController.getAnimationTargets( + opening, closing, false /* visible */)); + } + static class TestRemoteAnimationRunner implements IRemoteAnimationRunner { @Override public void onAnimationStart(int transit, RemoteAnimationTarget[] apps, 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 c3279bf05737..67aac13e3734 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,8 @@ 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_TASK_FRAGMENT_CLOSE; +import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_OPEN; import static android.view.WindowManager.TRANSIT_OLD_UNSET; import static android.view.WindowManager.TRANSIT_OPEN; @@ -40,6 +42,7 @@ import static org.mockito.ArgumentMatchers.any; import android.os.IBinder; import android.os.RemoteException; import android.platform.test.annotations.Presubmit; +import android.util.ArraySet; import android.view.Display; import android.view.IRemoteAnimationFinishedCallback; import android.view.IRemoteAnimationRunner; @@ -147,6 +150,91 @@ public class AppTransitionTests extends WindowTestsBase { } @Test + public void testTaskFragmentOpeningTransition() { + final ActivityRecord activity = createHierarchyForTaskFragmentTest( + false /* createEmbeddedTask */); + activity.setVisible(false); + + mDisplayContent.prepareAppTransition(TRANSIT_OPEN); + mDisplayContent.mOpeningApps.add(activity); + assertEquals(TRANSIT_OLD_TASK_FRAGMENT_OPEN, + AppTransitionController.getTransitCompatType(mDisplayContent.mAppTransition, + mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps, + null /* wallpaperTarget */, null /* oldWallpaper */, + false /* skipAppTransitionAnimation */)); + } + + @Test + public void testEmbeddedTaskOpeningTransition() { + final ActivityRecord activity = createHierarchyForTaskFragmentTest( + true /* createEmbeddedTask */); + activity.setVisible(false); + + mDisplayContent.prepareAppTransition(TRANSIT_OPEN); + mDisplayContent.mOpeningApps.add(activity); + assertEquals(TRANSIT_OLD_TASK_FRAGMENT_OPEN, + AppTransitionController.getTransitCompatType(mDisplayContent.mAppTransition, + mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps, + null /* wallpaperTarget */, null /* oldWallpaper */, + false /* skipAppTransitionAnimation */)); + } + + @Test + public void testTaskFragmentClosingTransition() { + final ActivityRecord activity = createHierarchyForTaskFragmentTest( + false /* createEmbeddedTask */); + activity.setVisible(true); + + mDisplayContent.prepareAppTransition(TRANSIT_CLOSE); + mDisplayContent.mClosingApps.add(activity); + assertEquals(TRANSIT_OLD_TASK_FRAGMENT_CLOSE, + AppTransitionController.getTransitCompatType(mDisplayContent.mAppTransition, + mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps, + null /* wallpaperTarget */, null /* oldWallpaper */, + false /* skipAppTransitionAnimation */)); + } + + @Test + public void testEmbeddedTaskClosingTransition() { + final ActivityRecord activity = createHierarchyForTaskFragmentTest( + true /* createEmbeddedTask */); + activity.setVisible(true); + + mDisplayContent.prepareAppTransition(TRANSIT_CLOSE); + mDisplayContent.mClosingApps.add(activity); + assertEquals(TRANSIT_OLD_TASK_FRAGMENT_CLOSE, + AppTransitionController.getTransitCompatType(mDisplayContent.mAppTransition, + mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps, + null /* wallpaperTarget */, null /* oldWallpaper */, + false /* skipAppTransitionAnimation */)); + } + + /** + * Creates a {@link Task} with two {@link TaskFragment TaskFragments}. + * The bottom TaskFragment is to prevent + * {@link AppTransitionController#getAnimationTargets(ArraySet, ArraySet, boolean) the animation + * target} to promote to Task or above. + * + * @param createEmbeddedTask {@code true} to create embedded Task for verified TaskFragment + * @return The Activity to be put in either opening or closing Activity + */ + private ActivityRecord createHierarchyForTaskFragmentTest(boolean createEmbeddedTask) { + final Task parentTask = createTask(mDisplayContent); + final TaskFragment bottomTaskFragment = createTaskFragmentWithParentTask(parentTask, + false /* createEmbeddedTask */); + final ActivityRecord bottomActivity = bottomTaskFragment.getTopMostActivity(); + bottomActivity.setOccludesParent(true); + bottomActivity.setVisible(true); + + final TaskFragment verifiedTaskFragment = createTaskFragmentWithParentTask(parentTask, + createEmbeddedTask); + final ActivityRecord activity = verifiedTaskFragment.getTopMostActivity(); + activity.setOccludesParent(true); + + return activity; + } + + @Test public void testAppTransitionStateForMultiDisplay() { // Create 2 displays & presume both display the state is ON for ready to display & animate. final DisplayContent dc1 = createNewDisplay(Display.STATE_ON); diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java index f3bb59c4598a..14dc33e5840b 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java @@ -139,6 +139,7 @@ import androidx.test.filters.SmallTest; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto; +import com.android.server.LocalServices; import com.android.server.policy.WindowManagerPolicy; import com.android.server.wm.utils.WmDisplayCutout; @@ -1307,7 +1308,7 @@ public class DisplayContentTests extends WindowTestsBase { } @UseTestDisplay(addWindows = { W_ACTIVITY, W_WALLPAPER, W_STATUS_BAR, W_NAVIGATION_BAR, - W_NOTIFICATION_SHADE }) + W_INPUT_METHOD, W_NOTIFICATION_SHADE }) @Test public void testApplyTopFixedRotationTransform() { final DisplayPolicy displayPolicy = mDisplayContent.getDisplayPolicy(); @@ -1411,6 +1412,14 @@ public class DisplayContentTests extends WindowTestsBase { assertEquals("The process should receive rotated configuration for compatibility", expectedProcConfig, app2.app.getConfiguration()); + // If the rotated activity requests to show IME, the IME window should use the + // transformation from activity to lay out in the same orientation. + mDisplayContent.setImeLayeringTarget(mAppWindow); + LocalServices.getService(WindowManagerInternal.class).onToggleImeRequested(true /* show */, + app.token, app.token, mDisplayContent.mDisplayId); + assertTrue(mImeWindow.mToken.hasFixedRotationTransform()); + assertTrue(mImeWindow.isAnimating(PARENTS, ANIMATION_TYPE_FIXED_TRANSFORM)); + // The fixed rotation transform can only be finished when all animation finished. doReturn(false).when(app2).isAnimating(anyInt(), anyInt()); mDisplayContent.mAppTransition.notifyAppTransitionFinishedLocked(app2.token); @@ -2343,10 +2352,10 @@ public class DisplayContentTests extends WindowTestsBase { ACTIVITY_TYPE_STANDARD, ON_TOP); final Task rootTask4 = taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, ON_TOP); - final Task task1 = new TaskBuilder(mAtm.mTaskSupervisor).setParentTask(rootTask1).build(); - final Task task2 = new TaskBuilder(mAtm.mTaskSupervisor).setParentTask(rootTask2).build(); - final Task task3 = new TaskBuilder(mAtm.mTaskSupervisor).setParentTask(rootTask3).build(); - final Task task4 = new TaskBuilder(mAtm.mTaskSupervisor).setParentTask(rootTask4).build(); + final Task task1 = new TaskBuilder(mSupervisor).setParentTaskFragment(rootTask1).build(); + final Task task2 = new TaskBuilder(mSupervisor).setParentTaskFragment(rootTask2).build(); + final Task task3 = new TaskBuilder(mSupervisor).setParentTaskFragment(rootTask3).build(); + final Task task4 = new TaskBuilder(mSupervisor).setParentTaskFragment(rootTask4).build(); // Reordering root tasks while removing root tasks. doAnswer(invocation -> { diff --git a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java index 7cb7c79d63a0..8a6db2c62a10 100644 --- a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java @@ -117,7 +117,7 @@ public class LaunchParamsPersisterTests extends WindowTestsBase { Task rootTask = mTestDisplay.getDefaultTaskDisplayArea() .createRootTask(TEST_WINDOWING_MODE, ACTIVITY_TYPE_STANDARD, /* onTop */ true); mTestTask = new TaskBuilder(mSupervisor).setComponent(TEST_COMPONENT) - .setParentTask(rootTask).build(); + .setParentTaskFragment(rootTask).build(); mTestTask.mUserId = TEST_USER_ID; mTestTask.mLastNonFullscreenBounds = TEST_BOUNDS; mTestTask.setHasBeenVisible(true); @@ -353,7 +353,7 @@ public class LaunchParamsPersisterTests extends WindowTestsBase { final Task anotherTaskOfTheSameUser = new TaskBuilder(mSupervisor) .setComponent(ALTERNATIVE_COMPONENT) .setUserId(TEST_USER_ID) - .setParentTask(stack) + .setParentTaskFragment(stack) .build(); anotherTaskOfTheSameUser.setWindowingMode(WINDOWING_MODE_FREEFORM); anotherTaskOfTheSameUser.setBounds(200, 300, 400, 500); @@ -365,7 +365,7 @@ public class LaunchParamsPersisterTests extends WindowTestsBase { final Task anotherTaskOfDifferentUser = new TaskBuilder(mSupervisor) .setComponent(TEST_COMPONENT) .setUserId(ALTERNATIVE_USER_ID) - .setParentTask(stack) + .setParentTaskFragment(stack) .build(); anotherTaskOfDifferentUser.setWindowingMode(WINDOWING_MODE_FREEFORM); anotherTaskOfDifferentUser.setBounds(300, 400, 500, 600); diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java index 1b078b7454b2..22e687a0bb15 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java @@ -331,7 +331,7 @@ public class RecentTasksTest extends WindowTestsBase { // other task Task task1 = createTaskBuilder(".Task1") .setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_MULTIPLE_TASK) - .setParentTask(mTaskContainer.getRootHomeTask()).build(); + .setParentTaskFragment(mTaskContainer.getRootHomeTask()).build(); Task task2 = createTaskBuilder(".Task1") .setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_MULTIPLE_TASK) .build(); @@ -471,8 +471,8 @@ public class RecentTasksTest extends WindowTestsBase { final Task root = createTaskBuilder(".CreatedByOrganizerRoot").build(); root.mCreatedByOrganizer = true; // Add organized and non-organized child. - final Task child1 = createTaskBuilder(".Task1").setParentTask(root).build(); - final Task child2 = createTaskBuilder(".Task2").setParentTask(root).build(); + final Task child1 = createTaskBuilder(".Task1").setParentTaskFragment(root).build(); + final Task child2 = createTaskBuilder(".Task2").setParentTaskFragment(root).build(); doReturn(true).when(child1).isOrganized(); doReturn(false).when(child2).isOrganized(); mRecentTasks.add(root); @@ -508,7 +508,8 @@ public class RecentTasksTest extends WindowTestsBase { // tasks because their intents are identical. mRecentTasks.add(task1); // Go home to trigger the removal of untracked tasks. - mRecentTasks.add(createTaskBuilder(".Home").setParentTask(mTaskContainer.getRootHomeTask()) + mRecentTasks.add(createTaskBuilder(".Home") + .setParentTaskFragment(mTaskContainer.getRootHomeTask()) .build()); triggerIdleToTrim(); @@ -675,7 +676,7 @@ public class RecentTasksTest extends WindowTestsBase { public void testVisibleTasks_excludedFromRecents_firstTaskNotVisible() { // Create some set of tasks, some of which are visible and some are not Task homeTask = createTaskBuilder("com.android.pkg1", ".HomeTask") - .setParentTask(mTaskContainer.getRootHomeTask()) + .setParentTaskFragment(mTaskContainer.getRootHomeTask()) .build(); homeTask.mUserSetupComplete = true; mRecentTasks.add(homeTask); @@ -696,7 +697,7 @@ public class RecentTasksTest extends WindowTestsBase { t1.mUserSetupComplete = true; mRecentTasks.add(t1); Task homeTask = createTaskBuilder("com.android.pkg1", ".HomeTask") - .setParentTask(mTaskContainer.getRootHomeTask()) + .setParentTaskFragment(mTaskContainer.getRootHomeTask()) .build(); homeTask.mUserSetupComplete = true; mRecentTasks.add(homeTask); @@ -949,10 +950,10 @@ public class RecentTasksTest extends WindowTestsBase { // Add a number of tasks (beyond the max) but ensure that nothing is trimmed because all // the tasks belong in stacks above the home stack - mRecentTasks.add(createTaskBuilder(".HomeTask1").setParentTask(homeStack).build()); - mRecentTasks.add(createTaskBuilder(".Task1").setParentTask(aboveHomeStack).build()); - mRecentTasks.add(createTaskBuilder(".Task2").setParentTask(aboveHomeStack).build()); - mRecentTasks.add(createTaskBuilder(".Task3").setParentTask(aboveHomeStack).build()); + mRecentTasks.add(createTaskBuilder(".HomeTask1").setParentTaskFragment(homeStack).build()); + mRecentTasks.add(createTaskBuilder(".Task1").setParentTaskFragment(aboveHomeStack).build()); + mRecentTasks.add(createTaskBuilder(".Task2").setParentTaskFragment(aboveHomeStack).build()); + mRecentTasks.add(createTaskBuilder(".Task3").setParentTaskFragment(aboveHomeStack).build()); triggerTrimAndAssertNoTasksTrimmed(); } @@ -970,11 +971,11 @@ public class RecentTasksTest extends WindowTestsBase { // Add a number of tasks (beyond the max) but ensure that only the task in the stack behind // the home stack is trimmed once a new task is added final Task behindHomeTask = createTaskBuilder(".Task1") - .setParentTask(behindHomeStack) + .setParentTaskFragment(behindHomeStack) .build(); mRecentTasks.add(behindHomeTask); - mRecentTasks.add(createTaskBuilder(".HomeTask1").setParentTask(homeStack).build()); - mRecentTasks.add(createTaskBuilder(".Task2").setParentTask(aboveHomeStack).build()); + mRecentTasks.add(createTaskBuilder(".HomeTask1").setParentTaskFragment(homeStack).build()); + mRecentTasks.add(createTaskBuilder(".Task2").setParentTaskFragment(aboveHomeStack).build()); triggerTrimAndAssertTrimmed(behindHomeTask); } @@ -990,10 +991,12 @@ public class RecentTasksTest extends WindowTestsBase { // Add a number of tasks (beyond the max) on each display, ensure that the tasks are not // removed - mRecentTasks.add(createTaskBuilder(".HomeTask1").setParentTask(homeTask).build()); - mRecentTasks.add(createTaskBuilder(".Task1").setParentTask(otherDisplayRootTask).build()); - mRecentTasks.add(createTaskBuilder(".Task2").setParentTask(otherDisplayRootTask).build()); - mRecentTasks.add(createTaskBuilder(".HomeTask2").setParentTask(homeTask).build()); + mRecentTasks.add(createTaskBuilder(".HomeTask1").setParentTaskFragment(homeTask).build()); + mRecentTasks.add(createTaskBuilder(".Task1").setParentTaskFragment(otherDisplayRootTask) + .build()); + mRecentTasks.add(createTaskBuilder(".Task2").setParentTaskFragment(otherDisplayRootTask) + .build()); + mRecentTasks.add(createTaskBuilder(".HomeTask2").setParentTaskFragment(homeTask).build()); triggerTrimAndAssertNoTasksTrimmed(); } @@ -1023,7 +1026,7 @@ public class RecentTasksTest extends WindowTestsBase { Task t1 = createTaskBuilder("com.android.pkg1", ".Task1").build(); mRecentTasks.add(t1); mRecentTasks.add(createTaskBuilder("com.android.pkg1", ".HomeTask") - .setParentTask(mTaskContainer.getRootHomeTask()).build()); + .setParentTaskFragment(mTaskContainer.getRootHomeTask()).build()); Task t2 = createTaskBuilder("com.android.pkg2", ".Task2").build(); mRecentTasks.add(t2); mRecentTasks.add(createTaskBuilder("com.android.pkg1", ".PipTask") diff --git a/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java b/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java index 56d01cd34e01..030733bf4424 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java @@ -329,7 +329,7 @@ public class RootTaskTests extends WindowTestsBase { // Create primary splitscreen root task. final Task primarySplitScreen = new TaskBuilder(mAtm.mTaskSupervisor) - .setParentTask(organizer.mPrimary) + .setParentTaskFragment(organizer.mPrimary) .setOnTop(true) .build(); @@ -505,8 +505,8 @@ public class RootTaskTests extends WindowTestsBase { targetActivity); final ComponentName alias = new ComponentName(DEFAULT_COMPONENT_PACKAGE_NAME, aliasActivity); - final Task parentTask = new TaskBuilder(mAtm.mTaskSupervisor).build(); - final Task task = new TaskBuilder(mAtm.mTaskSupervisor).setParentTask(parentTask).build(); + final Task parentTask = new TaskBuilder(mSupervisor).build(); + final Task task = new TaskBuilder(mSupervisor).setParentTaskFragment(parentTask).build(); task.origActivity = alias; task.realActivity = target; new ActivityBuilder(mAtm).setComponent(target).setTask(task).setTargetActivity( diff --git a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java index 8d4acbbac993..f2eb709b4bb8 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java @@ -168,7 +168,7 @@ public class RootWindowContainerTests extends WindowTestsBase { @Test public void testTaskLayerRank() { final Task rootTask = new TaskBuilder(mSupervisor).build(); - final Task task1 = new TaskBuilder(mSupervisor).setParentTask(rootTask).build(); + final Task task1 = new TaskBuilder(mSupervisor).setParentTaskFragment(rootTask).build(); new ActivityBuilder(mAtm).setTask(task1).build().mVisibleRequested = true; mWm.mRoot.rankTaskLayers(); @@ -523,7 +523,8 @@ public class RootWindowContainerTests extends WindowTestsBase { final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea(); final Task targetRootTask = taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */); - final Task targetTask = new TaskBuilder(mSupervisor).setParentTask(targetRootTask).build(); + final Task targetTask = new TaskBuilder(mSupervisor).setParentTaskFragment(targetRootTask) + .build(); // Create Recents on secondary display. final TestDisplayContent secondDisplay = addNewDisplayContentAt( diff --git a/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java index c44c22fefd7a..cb858845e03e 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java @@ -166,7 +166,7 @@ public class RunningTasksTest extends WindowTestsBase { final Task task = new TaskBuilder(mAtm.mTaskSupervisor) .setComponent(new ComponentName(mContext.getPackageName(), className)) .setTaskId(taskId) - .setParentTask(stack) + .setParentTaskFragment(stack) .build(); task.lastActiveTime = lastActiveTime; final ActivityRecord activity = new ActivityBuilder(mAtm) diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java index a8e17534e6ed..8e7ba4bc3293 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java +++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java @@ -258,6 +258,7 @@ public class SystemServicesTestRule implements TestRule { final ActivityManagerInternal amInternal = mAmService.mInternal; spyOn(amInternal); doNothing().when(amInternal).trimApplications(); + doNothing().when(amInternal).scheduleAppGcs(); doNothing().when(amInternal).updateCpuStats(); doNothing().when(amInternal).updateOomAdj(); doNothing().when(amInternal).updateBatteryStats(any(), anyInt(), anyInt(), anyBoolean()); diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java index c45c18d16c38..d68edbafb592 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java @@ -620,7 +620,7 @@ public class TaskDisplayAreaTests extends WindowTestsBase { final Task pinnedRootTask = mRootWindowContainer.getDefaultTaskDisplayArea() .createRootTask(WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, ON_TOP); final Task pinnedTask = new TaskBuilder(mAtm.mTaskSupervisor) - .setParentTask(pinnedRootTask).build(); + .setParentTaskFragment(pinnedRootTask).build(); new ActivityBuilder(mAtm).setActivityFlags(FLAG_ALWAYS_FOCUSABLE) .setTask(pinnedTask).build(); pinnedRootTask.moveToFront("movePinnedRootTaskToFront"); diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java index c35f31702961..5c79f5ca2c66 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java @@ -326,7 +326,8 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase { mTransaction.startActivityInTaskFragment( mFragmentToken, null /* callerToken */, new Intent(), null /* activityOptions */); mTransaction.reparentActivityToTaskFragment(mFragmentToken, mock(IBinder.class)); - mTransaction.setAdjacentTaskFragments(mFragmentToken, mock(IBinder.class)); + mTransaction.setAdjacentTaskFragments(mFragmentToken, mock(IBinder.class), + null /* options */); // It is expected to fail for the mock TaskFragmentCreationParams. It is ok as we are // testing the security check here. diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java index 5e4c67ce9e5c..e528a4a23e45 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java @@ -1716,7 +1716,7 @@ public class TaskLaunchParamsModifierTests extends WindowTestsBase { final Task rootTask = display.getDefaultTaskDisplayArea() .createRootTask(display.getWindowingMode(), ACTIVITY_TYPE_STANDARD, true); rootTask.setWindowingMode(WINDOWING_MODE_FREEFORM); - final Task task = new TaskBuilder(mSupervisor).setParentTask(rootTask).build(); + final Task task = new TaskBuilder(mSupervisor).setParentTaskFragment(rootTask).build(); // Just work around the unnecessary adjustments for bounds. task.getWindowConfiguration().setBounds(bounds); } diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java index 807494429999..67e8c879a3e0 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java @@ -427,7 +427,7 @@ public class TaskTests extends WindowTestsBase { TaskDisplayArea taskDisplayArea = mAtm.mRootWindowContainer.getDefaultTaskDisplayArea(); Task rootTask = taskDisplayArea.createRootTask(WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD, true /* onTop */); - Task task = new TaskBuilder(mSupervisor).setParentTask(rootTask).build(); + Task task = new TaskBuilder(mSupervisor).setParentTaskFragment(rootTask).build(); final Configuration parentConfig = rootTask.getConfiguration(); parentConfig.windowConfiguration.setBounds(parentBounds); parentConfig.densityDpi = DisplayMetrics.DENSITY_DEFAULT; @@ -757,7 +757,7 @@ public class TaskTests extends WindowTestsBase { DisplayInfo displayInfo = new DisplayInfo(); mAtm.mContext.getDisplay().getDisplayInfo(displayInfo); final int displayHeight = displayInfo.logicalHeight; - final Task task = new TaskBuilder(mSupervisor).setParentTask(rootTask).build(); + final Task task = new TaskBuilder(mSupervisor).setParentTaskFragment(rootTask).build(); final Configuration inOutConfig = new Configuration(); final Configuration parentConfig = new Configuration(); final int longSide = 1200; @@ -1375,7 +1375,7 @@ public class TaskTests extends WindowTestsBase { TaskDisplayArea taskDisplayArea = mAtm.mRootWindowContainer.getDefaultTaskDisplayArea(); Task rootTask = taskDisplayArea.createRootTask(windowingMode, ACTIVITY_TYPE_STANDARD, true /* onTop */); - Task task = new TaskBuilder(mSupervisor).setParentTask(rootTask).build(); + Task task = new TaskBuilder(mSupervisor).setParentTaskFragment(rootTask).build(); final Configuration parentConfig = rootTask.getConfiguration(); parentConfig.windowConfiguration.setAppBounds(parentBounds); 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 45e5f8e55f8a..6d60bcf1fce6 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java @@ -20,6 +20,7 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; +import static android.view.WindowManager.TRANSIT_CLOSE; import static android.view.WindowManager.TRANSIT_OLD_TASK_OPEN; import static android.view.WindowManager.TRANSIT_OPEN; import static android.view.WindowManager.TRANSIT_TO_BACK; @@ -41,6 +42,7 @@ import android.platform.test.annotations.Presubmit; import android.util.ArrayMap; import android.util.ArraySet; import android.window.ITaskOrganizer; +import android.window.ITransitionPlayer; import android.window.TransitionInfo; import androidx.test.filters.SmallTest; @@ -444,6 +446,71 @@ public class TransitionTests extends WindowTestsBase { info.getChange(openInChangeTask.mRemoteToken.toWindowContainerToken()), info)); } + @Test + public void testIntermediateVisibility() { + final TransitionController controller = new TransitionController(mAtm); + final ITransitionPlayer player = new ITransitionPlayer.Default(); + controller.registerTransitionPlayer(player); + ITaskOrganizer mockOrg = mock(ITaskOrganizer.class); + final Transition openTransition = controller.createTransition(TRANSIT_OPEN); + + // Start out with task2 visible and set up a transition that closes task2 and opens task1 + final Task task1 = createTask(mDisplayContent); + task1.mTaskOrganizer = mockOrg; + final ActivityRecord activity1 = createActivityRecord(task1); + activity1.mVisibleRequested = false; + activity1.setVisible(false); + final Task task2 = createTask(mDisplayContent); + task2.mTaskOrganizer = mockOrg; + final ActivityRecord activity2 = createActivityRecord(task1); + activity2.mVisibleRequested = true; + activity2.setVisible(true); + + openTransition.collectExistenceChange(task1); + openTransition.collectExistenceChange(activity1); + openTransition.collectExistenceChange(task2); + openTransition.collectExistenceChange(activity2); + + activity1.mVisibleRequested = true; + activity1.setVisible(true); + activity2.mVisibleRequested = false; + + // Using abort to force-finish the sync (since we can't wait for drawing in unit test). + // We didn't call abort on the transition itself, so it will still run onTransactionReady + // normally. + mWm.mSyncEngine.abort(openTransition.getSyncId()); + + // Before finishing openTransition, we are now going to simulate closing task1 to return + // back to (open) task2. + final Transition closeTransition = controller.createTransition(TRANSIT_CLOSE); + + closeTransition.collectExistenceChange(task1); + closeTransition.collectExistenceChange(activity1); + closeTransition.collectExistenceChange(task2); + closeTransition.collectExistenceChange(activity2); + + activity1.mVisibleRequested = false; + activity2.mVisibleRequested = true; + + openTransition.finishTransition(); + + // We finished the openTransition. Even though activity1 is visibleRequested=false, since + // the closeTransition animation hasn't played yet, make sure that we didn't commit + // visible=false on activity1 since it needs to remain visible for the animation. + assertTrue(activity1.isVisible()); + assertTrue(activity2.isVisible()); + + // Using abort to force-finish the sync (since we obviously can't wait for drawing). + // We didn't call abort on the actual transition, so it will still run onTransactionReady + // normally. + mWm.mSyncEngine.abort(closeTransition.getSyncId()); + + closeTransition.finishTransition(); + + assertFalse(activity1.isVisible()); + assertTrue(activity2.isVisible()); + } + /** 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) { diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java index 17288c21adc8..b17ea5e48db5 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java @@ -892,6 +892,40 @@ public class WindowStateTests extends WindowTestsBase { assertTrue(mAppWindow.getInsetsState().getSourceOrDefaultVisibility(ITYPE_NAVIGATION_BAR)); } + @Test + public void testAdjustImeInsetsVisibilityWhenSwitchingApps() { + final WindowState app = createWindow(null, TYPE_APPLICATION, "app"); + final WindowState app2 = createWindow(null, TYPE_APPLICATION, "app2"); + final WindowState imeWindow = createWindow(null, TYPE_APPLICATION, "imeWindow"); + spyOn(imeWindow); + doReturn(true).when(imeWindow).isVisible(); + mDisplayContent.mInputMethodWindow = imeWindow; + + final InsetsStateController controller = mDisplayContent.getInsetsStateController(); + controller.getImeSourceProvider().setWindow(imeWindow, null, null); + + // Simulate app requests IME with updating all windows Insets State when IME is above app. + mDisplayContent.setImeLayeringTarget(app); + mDisplayContent.setImeInputTarget(app); + assertTrue(mDisplayContent.shouldImeAttachedToApp()); + controller.getImeSourceProvider().scheduleShowImePostLayout(app); + controller.getImeSourceProvider().getSource().setVisible(true); + controller.updateAboveInsetsState(imeWindow, false); + + // Expect all app windows behind IME can receive IME insets visible. + assertTrue(app.getInsetsState().getSource(ITYPE_IME).isVisible()); + assertTrue(app2.getInsetsState().getSource(ITYPE_IME).isVisible()); + + // Simulate app plays closing transition to app2. + app.mActivityRecord.commitVisibility(false, false); + assertTrue(app.mActivityRecord.mLastImeShown); + assertTrue(app.mActivityRecord.mImeInsetsFrozenUntilStartInput); + + // Verify the IME insets is visible on app, but not for app2 during app task switching. + assertTrue(app.getInsetsState().getSource(ITYPE_IME).isVisible()); + assertFalse(app2.getInsetsState().getSource(ITYPE_IME).isVisible()); + } + @UseTestDisplay(addWindows = { W_ACTIVITY }) @Test public void testUpdateImeControlTargetWhenLeavingMultiWindow() { diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java index 050fd80411fc..e7ef6ae7cc0a 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java @@ -56,6 +56,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.server.wm.StartingSurfaceController.DEBUG_ENABLE_SHELL_DRAWER; import static com.android.server.wm.WindowContainer.POSITION_BOTTOM; +import static com.android.server.wm.WindowContainer.POSITION_TOP; import static com.android.server.wm.WindowStateAnimator.HAS_DRAWN; import static org.junit.Assert.assertEquals; @@ -589,7 +590,7 @@ class WindowTestsBase extends SystemServiceTestsBase { Task createTaskInRootTask(Task rootTask, int userId) { final Task task = new TaskBuilder(rootTask.mTaskSupervisor) .setUserId(userId) - .setParentTask(rootTask) + .setParentTaskFragment(rootTask) .build(); return task; } @@ -675,6 +676,26 @@ class WindowTestsBase extends SystemServiceTestsBase { activity.mVisibleRequested = true; } + /** + * Creates a {@link TaskFragment} and attach it to the {@code parentTask}. + * + * @param parentTask the {@link Task} this TaskFragment is going to be attached + * @param createEmbeddedTask Sets to {@code true} to create an embedded Task for this + * TaskFragment. Otherwise, create a {@link ActivityRecord}. + * @return the created TaskFragment + */ + static TaskFragment createTaskFragmentWithParentTask(@NonNull Task parentTask, + boolean createEmbeddedTask) { + final TaskFragmentBuilder builder = new TaskFragmentBuilder(parentTask.mAtmService) + .setParentTask(parentTask); + if (createEmbeddedTask) { + builder.createEmbeddedTask(); + } else { + builder.createActivityCount(1); + } + return builder.build(); + } + /** Creates a {@link DisplayContent} that supports IME and adds it to the system. */ DisplayContent createNewDisplay() { return createNewDisplayWithImeSupport(DISPLAY_IME_POLICY_LOCAL); @@ -1042,7 +1063,7 @@ class WindowTestsBase extends SystemServiceTestsBase { // Apply the root activity info and intent .setActivityInfo(aInfo) .setIntent(intent) - .setParentTask(mParentTask).build(); + .setParentTaskFragment(mParentTask).build(); } else if (mTask == null && mParentTask != null && DisplayContent.alwaysCreateRootTask( mParentTask.getWindowingMode(), mParentTask.getActivityType())) { // The parent task can be the task root. @@ -1101,6 +1122,64 @@ class WindowTestsBase extends SystemServiceTestsBase { } } + static class TaskFragmentBuilder { + private final ActivityTaskManagerService mAtm; + private Task mParentTask; + private boolean mCreateParentTask; + private boolean mCreateEmbeddedTask; + private int mCreateActivityCount = 0; + + TaskFragmentBuilder(ActivityTaskManagerService service) { + mAtm = service; + } + + TaskFragmentBuilder setCreateParentTask() { + mCreateParentTask = true; + return this; + } + + TaskFragmentBuilder setParentTask(Task task) { + mParentTask = task; + return this; + } + + /** Creates a child embedded Task and its Activity */ + TaskFragmentBuilder createEmbeddedTask() { + mCreateEmbeddedTask = true; + return this; + } + + TaskFragmentBuilder createActivityCount(int count) { + mCreateActivityCount = count; + return this; + } + + TaskFragment build() { + SystemServicesTestRule.checkHoldsLock(mAtm.mGlobalLock); + + final TaskFragment taskFragment = new TaskFragment(mAtm, null /* fragmentToken */, + false /* createdByOrganizer */); + if (mParentTask == null && mCreateParentTask) { + mParentTask = new TaskBuilder(mAtm.mTaskSupervisor).build(); + } + if (mParentTask != null) { + mParentTask.addChild(taskFragment, POSITION_TOP); + } + if (mCreateEmbeddedTask) { + new TaskBuilder(mAtm.mTaskSupervisor) + .setParentTaskFragment(taskFragment) + .setCreateActivity(true) + .build(); + } + while (mCreateActivityCount > 0) { + final ActivityRecord activity = new ActivityBuilder(mAtm).build(); + taskFragment.addChild(activity); + mCreateActivityCount--; + } + return taskFragment; + } + } + /** * Builder for creating new tasks. */ @@ -1121,7 +1200,7 @@ class WindowTestsBase extends SystemServiceTestsBase { private IVoiceInteractionSession mVoiceSession; private boolean mCreateParentTask = false; - private Task mParentTask; + private TaskFragment mParentTaskFragment; private boolean mCreateActivity = false; @@ -1205,8 +1284,8 @@ class WindowTestsBase extends SystemServiceTestsBase { return this; } - TaskBuilder setParentTask(Task parentTask) { - mParentTask = parentTask; + TaskBuilder setParentTaskFragment(TaskFragment parentTaskFragment) { + mParentTaskFragment = parentTaskFragment; return this; } @@ -1219,12 +1298,13 @@ class WindowTestsBase extends SystemServiceTestsBase { SystemServicesTestRule.checkHoldsLock(mSupervisor.mService.mGlobalLock); // Create parent task. - if (mParentTask == null && mCreateParentTask) { - mParentTask = mTaskDisplayArea.createRootTask( + if (mParentTaskFragment == null && mCreateParentTask) { + mParentTaskFragment = mTaskDisplayArea.createRootTask( WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); } - if (mParentTask != null && !Mockito.mockingDetails(mParentTask).isSpy()) { - spyOn(mParentTask); + if (mParentTaskFragment != null + && !Mockito.mockingDetails(mParentTaskFragment).isSpy()) { + spyOn(mParentTaskFragment); } // Create task. @@ -1252,13 +1332,15 @@ class WindowTestsBase extends SystemServiceTestsBase { .setOnTop(mOnTop) .setVoiceSession(mVoiceSession); final Task task; - if (mParentTask == null) { + if (mParentTaskFragment == null) { task = builder.setActivityType(mActivityType) .setParent(mTaskDisplayArea) .build(); } else { - task = builder.setParent(mParentTask).build(); - mParentTask.moveToFront("build-task"); + task = builder.setParent(mParentTaskFragment).build(); + if (mParentTaskFragment.asTask() != null) { + mParentTaskFragment.asTask().moveToFront("build-task"); + } } spyOn(task); task.mUserId = mUserId; diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java index 7f24c365237d..85f16eb00987 100644 --- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java +++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java @@ -1193,7 +1193,8 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser } else if (mCurrentFunctions == UsbManager.FUNCTION_MIDI) { titleRes = com.android.internal.R.string.usb_midi_notification_title; id = SystemMessage.NOTE_USB_MIDI; - } else if (mCurrentFunctions == UsbManager.FUNCTION_RNDIS) { + } else if ((mCurrentFunctions == UsbManager.FUNCTION_RNDIS) + || (mCurrentFunctions == UsbManager.FUNCTION_NCM)) { titleRes = com.android.internal.R.string.usb_tether_notification_title; id = SystemMessage.NOTE_USB_TETHER; } else if (mCurrentFunctions == UsbManager.FUNCTION_ACCESSORY) { diff --git a/services/usb/java/com/android/server/usb/UsbUserPermissionManager.java b/services/usb/java/com/android/server/usb/UsbUserPermissionManager.java index 5874b4b9fd3e..7b6ccd31adcc 100644 --- a/services/usb/java/com/android/server/usb/UsbUserPermissionManager.java +++ b/services/usb/java/com/android/server/usb/UsbUserPermissionManager.java @@ -25,12 +25,12 @@ import android.content.Context; import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; +import android.hardware.SensorPrivacyManager.Sensors; +import android.hardware.SensorPrivacyManagerInternal; import android.hardware.usb.AccessoryFilter; import android.hardware.usb.DeviceFilter; import android.hardware.usb.UsbAccessory; -import android.hardware.usb.UsbConstants; import android.hardware.usb.UsbDevice; -import android.hardware.usb.UsbInterface; import android.hardware.usb.UsbManager; import android.os.AsyncTask; import android.os.Binder; @@ -52,9 +52,9 @@ import android.util.TypedXmlSerializer; import android.util.Xml; import com.android.internal.annotations.GuardedBy; -import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.XmlUtils; import com.android.internal.util.dump.DualDumpOutputStream; +import com.android.server.LocalServices; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -64,7 +64,6 @@ import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; -import java.nio.charset.StandardCharsets; /** * UsbUserPermissionManager manages usb device or accessory access permissions. @@ -110,19 +109,20 @@ class UsbUserPermissionManager { */ @GuardedBy("mLock") private boolean mIsCopyPermissionsScheduled; + private final SensorPrivacyManagerInternal mSensorPrivacyMgrInternal; UsbUserPermissionManager(@NonNull Context context, @NonNull UsbUserSettingsManager usbUserSettingsManager) { mContext = context; mUser = context.getUser(); mUsbUserSettingsManager = usbUserSettingsManager; + mSensorPrivacyMgrInternal = LocalServices.getService(SensorPrivacyManagerInternal.class); mDisablePermissionDialogs = context.getResources().getBoolean( com.android.internal.R.bool.config_disableUsbPermissionDialogs); mPermissionsFile = new AtomicFile(new File( Environment.getUserSystemDirectory(mUser.getIdentifier()), "usb_permissions.xml"), "usb-permissions"); - synchronized (mLock) { readPermissionsLocked(); } @@ -195,11 +195,27 @@ class UsbUserPermissionManager { */ boolean hasPermission(@NonNull UsbDevice device, @NonNull String packageName, int pid, int uid) { - if (isCameraDevicePresent(device)) { - if (!isCameraPermissionGranted(packageName, pid, uid)) { + if (device.getHasVideoCapture()) { + boolean isCameraPrivacyEnabled = mSensorPrivacyMgrInternal.isSensorPrivacyEnabled( + UserHandle.getUserId(uid), Sensors.CAMERA); + if (DEBUG) { + Slog.d(TAG, "isCameraPrivacyEnabled: " + isCameraPrivacyEnabled); + } + if (isCameraPrivacyEnabled || !isCameraPermissionGranted(packageName, pid, uid)) { return false; } } + // Only check for microphone privacy and not RECORD_AUDIO permission, because access to usb + // camera device with audio recording capabilities may still be granted with a warning + if (device.getHasAudioCapture() && mSensorPrivacyMgrInternal.isSensorPrivacyEnabled( + UserHandle.getUserId(uid), Sensors.MICROPHONE)) { + if (DEBUG) { + Slog.d(TAG, + "Access to device with audio recording capabilities denied because " + + "microphone privacy is enabled."); + } + return false; + } synchronized (mLock) { if (uid == Process.SYSTEM_UID || mDisablePermissionDialogs) { return true; @@ -698,7 +714,10 @@ class UsbUserPermissionManager { } return; } - if (isCameraDevicePresent(device)) { + // If the app doesn't have camera permission do not request permission to the USB device. + // Note that if the USB camera also has a microphone, a warning will be shown to the user if + // the app doesn't have RECORD_AUDIO permission. + if (device.getHasVideoCapture()) { if (!isCameraPermissionGranted(packageName, pid, uid)) { intent.putExtra(UsbManager.EXTRA_DEVICE, device); intent.putExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false); @@ -733,27 +752,4 @@ class UsbUserPermissionManager { requestPermissionDialog(null, accessory, mUsbUserSettingsManager.canBeDefault(accessory, packageName), packageName, pi, uid); } - - /** - * Check whether a particular device or any of its interfaces - * is of class VIDEO. - * - * @param device The device that needs to get scanned - * @return True in case a VIDEO device or interface is present, - * False otherwise. - */ - private boolean isCameraDevicePresent(UsbDevice device) { - if (device.getDeviceClass() == UsbConstants.USB_CLASS_VIDEO) { - return true; - } - - for (int i = 0; i < device.getInterfaceCount(); i++) { - UsbInterface iface = device.getInterface(i); - if (iface.getInterfaceClass() == UsbConstants.USB_CLASS_VIDEO) { - return true; - } - } - - return false; - } } |