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