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