blob: 96ec4c5434740d0de38a6f65c035dae59e29a9a5 [file] [log] [blame]
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.systemui.testing.screenshot;
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
import android.app.UiAutomation;
import android.content.Context;
import android.provider.Settings;
import android.util.Log;
import androidx.annotation.ColorInt;
import androidx.annotation.ColorRes;
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import androidx.core.content.ContextCompat;
import androidx.test.espresso.Espresso;
import androidx.test.espresso.IdlingRegistry;
import androidx.test.espresso.IdlingResource;
import org.json.JSONObject;
import org.junit.function.ThrowingRunnable;
import java.util.HashMap;
import java.util.Map;
/*
* Note: This file was forked from
* google3/third_party/java_src/android_libs/material_components/screenshot_tests/java/android/
* support/design/scuba/color/DynamicColorsTestUtils.java.
*/
/** Utility that helps change the dynamic system colors for testing. */
@RequiresApi(32)
public class DynamicColorsTestUtils {
private static final String TAG = DynamicColorsTestUtils.class.getSimpleName();
private static final String THEME_CUSTOMIZATION_KEY = "theme_customization_overlay_packages";
private static final String THEME_CUSTOMIZATION_SYSTEM_PALETTE_KEY =
"android.theme.customization.system_palette";
private static final int ORANGE_SYSTEM_SEED_COLOR = 0xA66800;
private static final int ORANGE_EXPECTED_SYSTEM_ACCENT1_600_COLOR = -8235756;
private DynamicColorsTestUtils() {
}
/**
* Update system dynamic colors (e.g., android.R.color.system_accent1_600) based on an orange
* seed color, and then wait for the change to propagate to the app by comparing
* android.R.color.system_accent1_600 to the expected orange value.
*/
public static void updateSystemColorsToOrange() {
updateSystemColors(ORANGE_SYSTEM_SEED_COLOR, ORANGE_EXPECTED_SYSTEM_ACCENT1_600_COLOR);
}
/**
* Update system dynamic colors (e.g., android.R.color.system_accent1_600) based on the provided
* {@code seedColor}, and then wait for the change to propagate to the app by comparing
* android.R.color.system_accent1_600 to {@code expectedSystemAccent1600}.
*/
public static void updateSystemColors(
@ColorInt int seedColor, @ColorInt int expectedSystemAccent1600) {
Context context = getInstrumentation().getTargetContext();
int actualSystemAccent1600 =
ContextCompat.getColor(context, android.R.color.system_accent1_600);
if (expectedSystemAccent1600 == actualSystemAccent1600) {
String expectedColorString = Integer.toHexString(expectedSystemAccent1600);
Log.d(
TAG,
"Skipped updating system colors since system_accent1_600 is already equal to "
+ "expected: "
+ expectedColorString);
return;
}
updateSystemColors(seedColor);
}
/**
* Update system dynamic colors (e.g., android.R.color.system_accent1_600) based on the provided
* {@code seedColor}, and then wait for the change to propagate to the app by checking
* android.R.color.system_accent1_600 for any change.
*/
public static void updateSystemColors(@ColorInt int seedColor) {
Context context = getInstrumentation().getTargetContext();
// Initialize system color idling resource with original system_accent1_600 value.
ColorChangeIdlingResource systemColorIdlingResource =
new ColorChangeIdlingResource(context, android.R.color.system_accent1_600);
// Update system theme color setting to trigger fabricated resource overlay.
runWithShellPermissionIdentity(
() ->
Settings.Secure.putString(
context.getContentResolver(),
THEME_CUSTOMIZATION_KEY,
buildThemeCustomizationString(seedColor)));
// Wait for system color update to propagate to app.
IdlingRegistry idlingRegistry = IdlingRegistry.getInstance();
idlingRegistry.register(systemColorIdlingResource);
Espresso.onIdle();
idlingRegistry.unregister(systemColorIdlingResource);
Log.d(TAG,
Settings.Secure.getString(context.getContentResolver(), THEME_CUSTOMIZATION_KEY));
}
private static String buildThemeCustomizationString(@ColorInt int seedColor) {
String seedColorHex = Integer.toHexString(seedColor);
Map<String, String> themeCustomizationMap = new HashMap<>();
themeCustomizationMap.put(THEME_CUSTOMIZATION_SYSTEM_PALETTE_KEY, seedColorHex);
return new JSONObject(themeCustomizationMap).toString();
}
private static void runWithShellPermissionIdentity(@NonNull ThrowingRunnable runnable) {
UiAutomation uiAutomation = getInstrumentation().getUiAutomation();
uiAutomation.adoptShellPermissionIdentity();
try {
runnable.run();
} catch (Throwable e) {
throw new RuntimeException(e);
} finally {
uiAutomation.dropShellPermissionIdentity();
}
}
private static class ColorChangeIdlingResource implements IdlingResource {
private final Context mContext;
private final int mColorResId;
private final int mInitialColorInt;
private ResourceCallback mResourceCallback;
private boolean mIdleNow;
ColorChangeIdlingResource(Context context, @ColorRes int colorResId) {
this.mContext = context;
this.mColorResId = colorResId;
this.mInitialColorInt = ContextCompat.getColor(context, colorResId);
}
@Override
public String getName() {
return ColorChangeIdlingResource.class.getName();
}
@Override
public boolean isIdleNow() {
if (mIdleNow) {
return true;
}
int currentColorInt = ContextCompat.getColor(mContext, mColorResId);
String initialColorString = Integer.toHexString(mInitialColorInt);
String currentColorString = Integer.toHexString(currentColorInt);
Log.d(TAG, String.format("Initial=%s, Current=%s", initialColorString,
currentColorString));
mIdleNow = currentColorInt != mInitialColorInt;
Log.d(TAG, String.format("idleNow=%b", mIdleNow));
if (mIdleNow) {
mResourceCallback.onTransitionToIdle();
}
return mIdleNow;
}
@Override
public void registerIdleTransitionCallback(ResourceCallback resourceCallback) {
this.mResourceCallback = resourceCallback;
}
}
}