1 /* 2 * Copyright (C) 2021 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.system.helpers; 18 19 import static android.app.WallpaperManager.FLAG_SYSTEM; 20 21 import android.app.WallpaperManager; 22 import android.content.Context; 23 import android.graphics.Bitmap; 24 import android.graphics.BitmapRegionDecoder; 25 import android.graphics.Color; 26 import android.graphics.Rect; 27 import android.text.TextUtils; 28 29 import androidx.annotation.ColorInt; 30 import androidx.annotation.NonNull; 31 import androidx.annotation.Nullable; 32 import androidx.test.uiautomator.UiDevice; 33 34 import java.io.ByteArrayInputStream; 35 import java.io.ByteArrayOutputStream; 36 import java.io.File; 37 import java.io.IOException; 38 import java.util.ArrayList; 39 import java.util.Comparator; 40 import java.util.HashMap; 41 import java.util.List; 42 import java.util.Map; 43 import java.util.Objects; 44 45 /** A common helper class for theme scenario tests. */ 46 public class ThemeHelper { 47 48 private static final float COLOR_TOLERANCE = 0.01F; 49 50 @NonNull private final UiDevice mUiDevice; 51 @NonNull private final Context mContext; 52 @NonNull private final WallpaperManager mWallpaperManager; 53 ThemeHelper(@onNull UiDevice uiDevice, @NonNull Context context)54 public ThemeHelper(@NonNull UiDevice uiDevice, @NonNull Context context) { 55 mUiDevice = uiDevice; 56 mContext = context; 57 mWallpaperManager = 58 Objects.requireNonNull(mContext.getSystemService(WallpaperManager.class)); 59 } 60 61 /** 62 * Sets wallpaper 63 * 64 * @param color the color for wallpaper 65 * @throws IOException exception during setWallpaper 66 */ setWallpaper(@olorInt int color)67 public void setWallpaper(@ColorInt int color) throws IOException { 68 final Bitmap bitmap = 69 Bitmap.createBitmap( 70 mUiDevice.getDisplayWidth(), 71 mUiDevice.getDisplayHeight(), 72 Bitmap.Config.ARGB_8888); 73 bitmap.eraseColor(color); 74 75 final byte[] byteArray; 76 try { 77 final ByteArrayOutputStream outStream = new ByteArrayOutputStream(); 78 final boolean compressResult = 79 bitmap.compress(Bitmap.CompressFormat.PNG, 100, outStream); 80 if (!compressResult) { 81 throw new IOException("Fail to compress bitmap"); 82 } 83 byteArray = outStream.toByteArray(); 84 mWallpaperManager.setStream( 85 new ByteArrayInputStream(byteArray), null, false, FLAG_SYSTEM); 86 } finally { 87 bitmap.recycle(); 88 } 89 } 90 91 /** 92 * Gets primary color from WallpaperColors 93 * 94 * @return primary color 95 */ 96 @ColorInt getWallpaperPrimaryColor()97 public int getWallpaperPrimaryColor() { 98 return mWallpaperManager.getWallpaperColors(FLAG_SYSTEM).getPrimaryColor().toArgb(); 99 } 100 deleteFileIfExist(@ullable String fileAbsPath)101 private void deleteFileIfExist(@Nullable String fileAbsPath) { 102 if (TextUtils.isEmpty(fileAbsPath)) { 103 return; 104 } 105 new File(fileAbsPath).deleteOnExit(); 106 } 107 108 /** 109 * Gets highest count of color inside this bitmap 110 * 111 * @param bitmap the bitmap for analysis 112 * @return most rendered color 113 */ 114 @NonNull getMostColor(@onNull Bitmap bitmap)115 public Color getMostColor(@NonNull Bitmap bitmap) { 116 HashMap<Integer, Integer> colors = new HashMap<>(); 117 for (int x = 0; x < bitmap.getWidth(); ++x) { 118 for (int y = 0; y < bitmap.getHeight(); ++y) { 119 @ColorInt int color = bitmap.getColor(x, y).toArgb(); 120 colors.put(color, colors.containsKey(color) ? colors.get(color) + 1 : 1); 121 } 122 } 123 List<Map.Entry<Integer, Integer>> colorList = new ArrayList<>(colors.entrySet()); 124 final Map.Entry<Integer, Integer> mostColorCountEntry = 125 colorList.stream().max(Comparator.comparingInt(Map.Entry::getValue)).get(); 126 return Color.valueOf(mostColorCountEntry.getKey()); 127 } 128 takeScreenshotToFile(@onNull String filename)129 private String takeScreenshotToFile(@NonNull String filename) { 130 File f = new File(mContext.getFilesDir(), filename); 131 mUiDevice.takeScreenshot(f); 132 if (f.exists()) { 133 return f.getAbsolutePath(); 134 } 135 return null; 136 } 137 138 /** 139 * Takes a screenshot and calculates in the specific rect 140 * 141 * @param rect the rect for calculating the most rendered color 142 * @return most rendered color 143 * @throws IOException exception if taking screenshot fails 144 */ getScreenshotMostColorAsRect(@onNull Rect rect)145 public Color getScreenshotMostColorAsRect(@NonNull Rect rect) throws IOException { 146 String fileAbsPath = null; 147 Bitmap bitmap = null; 148 BitmapRegionDecoder bitmapRegionDecoder = null; 149 try { 150 fileAbsPath = takeScreenshotToFile("test1"); 151 bitmapRegionDecoder = BitmapRegionDecoder.newInstance(fileAbsPath); 152 bitmap = bitmapRegionDecoder.decodeRegion(rect, null); 153 return getMostColor(bitmap); 154 } finally { 155 if (bitmap != null) { 156 bitmap.recycle(); 157 } 158 if (bitmapRegionDecoder != null) { 159 bitmapRegionDecoder.recycle(); 160 } 161 deleteFileIfExist(fileAbsPath); 162 } 163 } 164 165 /** 166 * Gets current activated Quick Setting background color. 167 * 168 * @return color The theme color for activated quick setting tile background 169 */ 170 @NonNull getSysuiActivatedThemeColor()171 public Color getSysuiActivatedThemeColor() { 172 return Color.valueOf(mContext.getColor(android.R.color.system_accent1_100)); 173 } 174 175 /** 176 * Validate the colors in between are similar or not. 177 * 178 * @param color1 the first color 179 * @param color2 the second color 180 * @return true if the colors in between are similar, false otherwise 181 */ isSimilarColor(@onNull Color color1, @NonNull Color color2)182 public boolean isSimilarColor(@NonNull Color color1, @NonNull Color color2) { 183 return color1.alpha() == color2.alpha() 184 && Math.abs(color1.red() - color2.red()) < COLOR_TOLERANCE 185 && Math.abs(color1.green() - color2.green()) < COLOR_TOLERANCE 186 && Math.abs(color1.blue() - color2.blue()) < COLOR_TOLERANCE; 187 } 188 } 189