1 /* 2 * Copyright (C) 2018 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 com.android.systemui.shared.recents.utilities; 18 19 import static android.app.StatusBarManager.NAVIGATION_HINT_BACK_ALT; 20 import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SHOWN; 21 import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SWITCHER_SHOWN; 22 23 import android.annotation.TargetApi; 24 import android.content.Context; 25 import android.content.res.Resources; 26 import android.graphics.Color; 27 import android.graphics.Rect; 28 import android.inputmethodservice.InputMethodService; 29 import android.os.Build; 30 import android.os.Handler; 31 import android.os.Message; 32 import android.util.DisplayMetrics; 33 import android.view.Surface; 34 import android.view.WindowManager; 35 36 /* Common code */ 37 public class Utilities { 38 39 private static final float TABLET_MIN_DPS = 600; 40 41 /** 42 * Posts a runnable on a handler at the front of the queue ignoring any sync barriers. 43 */ postAtFrontOfQueueAsynchronously(Handler h, Runnable r)44 public static void postAtFrontOfQueueAsynchronously(Handler h, Runnable r) { 45 Message msg = h.obtainMessage().setCallback(r); 46 h.sendMessageAtFrontOfQueue(msg); 47 } 48 isRotationAnimationCCW(int from, int to)49 public static boolean isRotationAnimationCCW(int from, int to) { 50 // All 180deg WM rotation animations are CCW, match that 51 if (from == Surface.ROTATION_0 && to == Surface.ROTATION_90) return false; 52 if (from == Surface.ROTATION_0 && to == Surface.ROTATION_180) return true; //180d so CCW 53 if (from == Surface.ROTATION_0 && to == Surface.ROTATION_270) return true; 54 if (from == Surface.ROTATION_90 && to == Surface.ROTATION_0) return true; 55 if (from == Surface.ROTATION_90 && to == Surface.ROTATION_180) return false; 56 if (from == Surface.ROTATION_90 && to == Surface.ROTATION_270) return true; //180d so CCW 57 if (from == Surface.ROTATION_180 && to == Surface.ROTATION_0) return true; //180d so CCW 58 if (from == Surface.ROTATION_180 && to == Surface.ROTATION_90) return true; 59 if (from == Surface.ROTATION_180 && to == Surface.ROTATION_270) return false; 60 if (from == Surface.ROTATION_270 && to == Surface.ROTATION_0) return false; 61 if (from == Surface.ROTATION_270 && to == Surface.ROTATION_90) return true; //180d so CCW 62 if (from == Surface.ROTATION_270 && to == Surface.ROTATION_180) return true; 63 return false; // Default 64 } 65 66 /** 67 * Compares the ratio of two quantities and returns whether that ratio is greater than the 68 * provided bound. Order of quantities does not matter. Bound should be a decimal representation 69 * of a percentage. 70 */ isRelativePercentDifferenceGreaterThan(float first, float second, float bound)71 public static boolean isRelativePercentDifferenceGreaterThan(float first, float second, 72 float bound) { 73 return (Math.abs(first - second) / Math.abs((first + second) / 2.0f)) > bound; 74 } 75 76 /** Calculates the constrast between two colors, using the algorithm provided by the WCAG v2. */ computeContrastBetweenColors(int bg, int fg)77 public static float computeContrastBetweenColors(int bg, int fg) { 78 float bgR = Color.red(bg) / 255f; 79 float bgG = Color.green(bg) / 255f; 80 float bgB = Color.blue(bg) / 255f; 81 bgR = (bgR < 0.03928f) ? bgR / 12.92f : (float) Math.pow((bgR + 0.055f) / 1.055f, 2.4f); 82 bgG = (bgG < 0.03928f) ? bgG / 12.92f : (float) Math.pow((bgG + 0.055f) / 1.055f, 2.4f); 83 bgB = (bgB < 0.03928f) ? bgB / 12.92f : (float) Math.pow((bgB + 0.055f) / 1.055f, 2.4f); 84 float bgL = 0.2126f * bgR + 0.7152f * bgG + 0.0722f * bgB; 85 86 float fgR = Color.red(fg) / 255f; 87 float fgG = Color.green(fg) / 255f; 88 float fgB = Color.blue(fg) / 255f; 89 fgR = (fgR < 0.03928f) ? fgR / 12.92f : (float) Math.pow((fgR + 0.055f) / 1.055f, 2.4f); 90 fgG = (fgG < 0.03928f) ? fgG / 12.92f : (float) Math.pow((fgG + 0.055f) / 1.055f, 2.4f); 91 fgB = (fgB < 0.03928f) ? fgB / 12.92f : (float) Math.pow((fgB + 0.055f) / 1.055f, 2.4f); 92 float fgL = 0.2126f * fgR + 0.7152f * fgG + 0.0722f * fgB; 93 94 return Math.abs((fgL + 0.05f) / (bgL + 0.05f)); 95 } 96 97 /** 98 * @return the clamped {@param value} between the provided {@param min} and {@param max}. 99 */ clamp(float value, float min, float max)100 public static float clamp(float value, float min, float max) { 101 return Math.max(min, Math.min(max, value)); 102 } 103 104 /** 105 * @return updated set of flags from InputMethodService based off {@param oldHints} 106 * Leaves original hints unmodified 107 */ calculateBackDispositionHints(int oldHints, int backDisposition, boolean imeShown, boolean showImeSwitcher)108 public static int calculateBackDispositionHints(int oldHints, int backDisposition, 109 boolean imeShown, boolean showImeSwitcher) { 110 int hints = oldHints; 111 switch (backDisposition) { 112 case InputMethodService.BACK_DISPOSITION_DEFAULT: 113 case InputMethodService.BACK_DISPOSITION_WILL_NOT_DISMISS: 114 case InputMethodService.BACK_DISPOSITION_WILL_DISMISS: 115 if (imeShown) { 116 hints |= NAVIGATION_HINT_BACK_ALT; 117 } else { 118 hints &= ~NAVIGATION_HINT_BACK_ALT; 119 } 120 break; 121 case InputMethodService.BACK_DISPOSITION_ADJUST_NOTHING: 122 hints &= ~NAVIGATION_HINT_BACK_ALT; 123 break; 124 } 125 if (imeShown) { 126 hints |= NAVIGATION_HINT_IME_SHOWN; 127 } else { 128 hints &= ~NAVIGATION_HINT_IME_SHOWN; 129 } 130 if (showImeSwitcher) { 131 hints |= NAVIGATION_HINT_IME_SWITCHER_SHOWN; 132 } else { 133 hints &= ~NAVIGATION_HINT_IME_SWITCHER_SHOWN; 134 } 135 136 return hints; 137 } 138 139 /** @return whether or not {@param context} represents that of a large screen device or not */ 140 @TargetApi(Build.VERSION_CODES.R) isLargeScreen(Context context)141 public static boolean isLargeScreen(Context context) { 142 return isLargeScreen(context.getSystemService(WindowManager.class), context.getResources()); 143 } 144 145 /** @return whether or not {@param context} represents that of a large screen device or not */ isLargeScreen(WindowManager windowManager, Resources resources)146 public static boolean isLargeScreen(WindowManager windowManager, Resources resources) { 147 final Rect bounds = windowManager.getCurrentWindowMetrics().getBounds(); 148 149 float smallestWidth = dpiFromPx(Math.min(bounds.width(), bounds.height()), 150 resources.getConfiguration().densityDpi); 151 return smallestWidth >= TABLET_MIN_DPS; 152 } 153 dpiFromPx(float size, int densityDpi)154 public static float dpiFromPx(float size, int densityDpi) { 155 float densityRatio = (float) densityDpi / DisplayMetrics.DENSITY_DEFAULT; 156 return (size / densityRatio); 157 } 158 } 159