1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
5  * except in compliance with the License. You may obtain a copy of the License at
6  *
7  *      http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software distributed under the
10  * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
11  * KIND, either express or implied. See the License for the specific language governing
12  * permissions and limitations under the License.
13  */
14 
15 package com.android.systemui.statusbar.phone;
16 
17 import static com.android.systemui.plugins.DarkIconDispatcher.getTint;
18 
19 import android.animation.ArgbEvaluator;
20 import android.content.Context;
21 import android.content.res.ColorStateList;
22 import android.graphics.Rect;
23 import android.util.ArrayMap;
24 import android.widget.ImageView;
25 
26 import com.android.systemui.R;
27 import com.android.systemui.dagger.SysUISingleton;
28 import com.android.systemui.dump.DumpManager;
29 
30 import java.io.PrintWriter;
31 import java.util.ArrayList;
32 
33 import javax.inject.Inject;
34 
35 import kotlinx.coroutines.flow.FlowKt;
36 import kotlinx.coroutines.flow.MutableStateFlow;
37 import kotlinx.coroutines.flow.StateFlow;
38 import kotlinx.coroutines.flow.StateFlowKt;
39 
40 /**
41  */
42 @SysUISingleton
43 public class DarkIconDispatcherImpl implements SysuiDarkIconDispatcher,
44         LightBarTransitionsController.DarkIntensityApplier {
45 
46     private final LightBarTransitionsController mTransitionsController;
47     private final ArrayList<Rect> mTintAreas = new ArrayList<>();
48     private final ArrayMap<Object, DarkReceiver> mReceivers = new ArrayMap<>();
49 
50     private int mIconTint = DEFAULT_ICON_TINT;
51     private float mDarkIntensity;
52     private int mDarkModeIconColorSingleTone;
53     private int mLightModeIconColorSingleTone;
54 
55     private final MutableStateFlow<DarkChange> mDarkChangeFlow = StateFlowKt.MutableStateFlow(
56             DarkChange.EMPTY);
57 
58     /**
59      */
60     @Inject
DarkIconDispatcherImpl( Context context, LightBarTransitionsController.Factory lightBarTransitionsControllerFactory, DumpManager dumpManager)61     public DarkIconDispatcherImpl(
62             Context context,
63             LightBarTransitionsController.Factory lightBarTransitionsControllerFactory,
64             DumpManager dumpManager) {
65         mDarkModeIconColorSingleTone = context.getColor(R.color.dark_mode_icon_color_single_tone);
66         mLightModeIconColorSingleTone = context.getColor(R.color.light_mode_icon_color_single_tone);
67 
68         mTransitionsController = lightBarTransitionsControllerFactory.create(this);
69 
70         dumpManager.registerDumpable(getClass().getSimpleName(), this);
71     }
72 
getTransitionsController()73     public LightBarTransitionsController getTransitionsController() {
74         return mTransitionsController;
75     }
76 
77     @Override
darkChangeFlow()78     public StateFlow<DarkChange> darkChangeFlow() {
79         return FlowKt.asStateFlow(mDarkChangeFlow);
80     }
81 
addDarkReceiver(DarkReceiver receiver)82     public void addDarkReceiver(DarkReceiver receiver) {
83         mReceivers.put(receiver, receiver);
84         receiver.onDarkChanged(mTintAreas, mDarkIntensity, mIconTint);
85     }
86 
addDarkReceiver(ImageView imageView)87     public void addDarkReceiver(ImageView imageView) {
88         DarkReceiver receiver = (area, darkIntensity, tint) -> imageView.setImageTintList(
89                 ColorStateList.valueOf(getTint(mTintAreas, imageView, mIconTint)));
90         mReceivers.put(imageView, receiver);
91         receiver.onDarkChanged(mTintAreas, mDarkIntensity, mIconTint);
92     }
93 
removeDarkReceiver(DarkReceiver object)94     public void removeDarkReceiver(DarkReceiver object) {
95         mReceivers.remove(object);
96     }
97 
removeDarkReceiver(ImageView object)98     public void removeDarkReceiver(ImageView object) {
99         mReceivers.remove(object);
100     }
101 
applyDark(DarkReceiver object)102     public void applyDark(DarkReceiver object) {
103         mReceivers.get(object).onDarkChanged(mTintAreas, mDarkIntensity, mIconTint);
104     }
105 
106     /**
107      * Sets the dark area so {@link #applyDark} only affects the icons in the specified area.
108      *
109      * @param darkAreas the areas in which icons should change it's tint, in logical screen
110      *                  coordinates
111      */
setIconsDarkArea(ArrayList<Rect> darkAreas)112     public void setIconsDarkArea(ArrayList<Rect> darkAreas) {
113         if (darkAreas == null && mTintAreas.isEmpty()) {
114             return;
115         }
116 
117         mTintAreas.clear();
118         if (darkAreas != null) {
119             mTintAreas.addAll(darkAreas);
120         }
121         applyIconTint();
122     }
123 
124     @Override
applyDarkIntensity(float darkIntensity)125     public void applyDarkIntensity(float darkIntensity) {
126         mDarkIntensity = darkIntensity;
127         mIconTint = (int) ArgbEvaluator.getInstance().evaluate(darkIntensity,
128                 mLightModeIconColorSingleTone, mDarkModeIconColorSingleTone);
129         applyIconTint();
130     }
131 
132     @Override
getTintAnimationDuration()133     public int getTintAnimationDuration() {
134         return LightBarTransitionsController.DEFAULT_TINT_ANIMATION_DURATION;
135     }
136 
applyIconTint()137     private void applyIconTint() {
138         mDarkChangeFlow.setValue(new DarkChange(mTintAreas, mDarkIntensity, mIconTint));
139         for (int i = 0; i < mReceivers.size(); i++) {
140             mReceivers.valueAt(i).onDarkChanged(mTintAreas, mDarkIntensity, mIconTint);
141         }
142     }
143 
144     @Override
dump(PrintWriter pw, String[] args)145     public void dump(PrintWriter pw, String[] args) {
146         pw.println("DarkIconDispatcher: ");
147         pw.println("  mIconTint: 0x" + Integer.toHexString(mIconTint));
148         pw.println("  mDarkIntensity: " + mDarkIntensity + "f");
149         pw.println("  mTintAreas: " + mTintAreas);
150     }
151 }
152