1 /*
2  * Copyright (C) 2024 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.providers.media.photopicker;
18 
19 import android.app.Application;
20 import android.content.res.Configuration;
21 import android.graphics.Color;
22 import android.provider.MediaStore;
23 import android.util.Log;
24 
25 /**
26  * This class holds the parameters and the utility methods for setting a custom picker accent
27  * color.
28  * Only valid input color codes with luminance greater than 0.6 will be set on the major picker
29  * components. All other colors are set based on Material3 baseline theme for both the dark and
30  * light theme.
31  */
32 public class PickerAccentColorParameters {
33     public static final String TAG = "PhotoPicker";
34     private int mPickerAccentColor = -1;
35     private boolean mIsNightModeEnabled = false;
36     private float mAccentColorLuminance = 0;
37 
PickerAccentColorParameters()38     public PickerAccentColorParameters() {}
39 
PickerAccentColorParameters(int color, Application application)40     public PickerAccentColorParameters(int color, Application application) {
41         mPickerAccentColor = color;
42         // Needs to be set here since the PickerAccentColorParameters object gets initialised again
43         // after color validity check therefore the value if set then will be lost.
44         mAccentColorLuminance = Color.luminance(color);
45         setNightModeFlag(application);
46     }
47 
setNightModeFlag(Application application)48     private void setNightModeFlag(Application application) {
49         if (application == null) {
50             return;
51         }
52         int nightModeFlag =
53                 application.getApplicationContext().getResources().getConfiguration().uiMode
54                         & Configuration.UI_MODE_NIGHT_MASK;
55         mIsNightModeEnabled = nightModeFlag == Configuration.UI_MODE_NIGHT_YES;
56     }
57 
58     /**
59      * Return dark or light color variant based on whether night mode is enabled or not.
60      */
getThemeBasedColor(String lightThemeVariant, String darkThemeVariant)61     public int getThemeBasedColor(String lightThemeVariant, String darkThemeVariant) {
62         return mIsNightModeEnabled
63                 ? Color.parseColor(darkThemeVariant) : Color.parseColor(lightThemeVariant);
64     }
65 
66     /**
67      * Returns whether the accent color is set or not
68      */
isCustomPickerColorSet()69     public boolean isCustomPickerColorSet() {
70         return mPickerAccentColor != -1;
71     }
72 
73     /**
74      * Checks whether the input color to be used as the picker accent color is valid or not and
75      * returns the int color value if it is valid after dropping the alpha component
76      * or -1 otherwise.
77      */
checkColorValidityAndGetColor(long color)78     public static int checkColorValidityAndGetColor(long color) {
79         // Gives us the formatted color string where the mask gives us the color in the RRGGBB
80         // format and the %06X gives zero-padded hex (6 characters long)
81         String hexColor = String.format("#%06X", (0xFFFFFF & color));
82         int inputColor = Color.parseColor(hexColor);
83         if (!isColorFeasibleForBothBackgrounds(inputColor)) {
84             // Fall back to the android theme
85             Log.w(TAG, "Value set for " + MediaStore.EXTRA_PICK_IMAGES_ACCENT_COLOR
86                     + " is not within the permitted brightness range. Please refer to the "
87                     + "docs for more details. Setting android theme on the picker.");
88             return -1;
89         }
90         return inputColor;
91     }
92 
isColorFeasibleForBothBackgrounds(int color)93     private static boolean isColorFeasibleForBothBackgrounds(int color) {
94         // Returns the luminance(can also be thought of brightness)
95         // Returned value ranges from 0(black) to 1(white)
96         // Colors within this range will work both on light and dark background. Range set by
97         // testing with different colors.
98         float luminance = Color.luminance(color);
99         return luminance >= 0.05 && luminance < 0.9;
100     }
101 
102     /**
103      * Returns whether the accent color is bright or not based on the luminance of the color.
104      * Lower luminance bound set by testing with different colors.
105      */
isAccentColorBright()106     public boolean isAccentColorBright() {
107         return mAccentColorLuminance >= 0.6;
108     }
109 
110     /**
111      * Returns the accent color to be set. We can ignore the alpha component when using the color
112      * to be set on various picker components. Also, all of these components require integer color
113      * value to set their respective colors.
114      */
getPickerAccentColor()115     public int getPickerAccentColor() {
116         return mPickerAccentColor;
117     }
118 }
119