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.statusbar;
18 
19 import static com.android.systemui.plugins.DarkIconDispatcher.getTint;
20 import static com.android.systemui.plugins.DarkIconDispatcher.isInArea;
21 import static com.android.systemui.statusbar.StatusBarIconView.STATE_DOT;
22 import static com.android.systemui.statusbar.StatusBarIconView.STATE_HIDDEN;
23 import static com.android.systemui.statusbar.StatusBarIconView.STATE_ICON;
24 
25 import android.content.Context;
26 import android.content.res.ColorStateList;
27 import android.graphics.Color;
28 import android.graphics.Rect;
29 import android.util.AttributeSet;
30 import android.view.Gravity;
31 import android.view.LayoutInflater;
32 import android.view.View;
33 import android.widget.FrameLayout;
34 import android.widget.ImageView;
35 import android.widget.LinearLayout;
36 
37 import com.android.internal.annotations.VisibleForTesting;
38 import com.android.settingslib.graph.SignalDrawable;
39 import com.android.systemui.DualToneHandler;
40 import com.android.systemui.R;
41 import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
42 import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState;
43 
44 public class StatusBarMobileView extends FrameLayout implements DarkReceiver,
45         StatusIconDisplayable {
46     private static final String TAG = "StatusBarMobileView";
47 
48     /// Used to show etc dots
49     private StatusBarIconView mDotView;
50     /// The main icon view
51     private LinearLayout mMobileGroup;
52     private String mSlot;
53     private MobileIconState mState;
54     private SignalDrawable mMobileDrawable;
55     private View mInoutContainer;
56     private ImageView mIn;
57     private ImageView mOut;
58     private ImageView mMobile, mMobileType, mMobileRoaming;
59     private View mMobileRoamingSpace;
60     private int mVisibleState = -1;
61     private DualToneHandler mDualToneHandler;
62 
fromContext(Context context, String slot)63     public static StatusBarMobileView fromContext(Context context, String slot) {
64         LayoutInflater inflater = LayoutInflater.from(context);
65         StatusBarMobileView v = (StatusBarMobileView)
66                 inflater.inflate(R.layout.status_bar_mobile_signal_group, null);
67 
68         v.setSlot(slot);
69         v.init();
70         v.setVisibleState(STATE_ICON);
71         return v;
72     }
73 
StatusBarMobileView(Context context)74     public StatusBarMobileView(Context context) {
75         super(context);
76     }
77 
StatusBarMobileView(Context context, AttributeSet attrs)78     public StatusBarMobileView(Context context, AttributeSet attrs) {
79         super(context, attrs);
80     }
81 
StatusBarMobileView(Context context, AttributeSet attrs, int defStyleAttr)82     public StatusBarMobileView(Context context, AttributeSet attrs, int defStyleAttr) {
83         super(context, attrs, defStyleAttr);
84     }
85 
StatusBarMobileView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)86     public StatusBarMobileView(Context context, AttributeSet attrs, int defStyleAttr,
87             int defStyleRes) {
88         super(context, attrs, defStyleAttr, defStyleRes);
89     }
90 
91     @Override
getDrawingRect(Rect outRect)92     public void getDrawingRect(Rect outRect) {
93         super.getDrawingRect(outRect);
94         float translationX = getTranslationX();
95         float translationY = getTranslationY();
96         outRect.left += translationX;
97         outRect.right += translationX;
98         outRect.top += translationY;
99         outRect.bottom += translationY;
100     }
101 
init()102     private void init() {
103         mDualToneHandler = new DualToneHandler(getContext());
104         mMobileGroup = findViewById(R.id.mobile_group);
105         mMobile = findViewById(R.id.mobile_signal);
106         mMobileType = findViewById(R.id.mobile_type);
107         mMobileRoaming = findViewById(R.id.mobile_roaming);
108         mMobileRoamingSpace = findViewById(R.id.mobile_roaming_space);
109         mIn = findViewById(R.id.mobile_in);
110         mOut = findViewById(R.id.mobile_out);
111         mInoutContainer = findViewById(R.id.inout_container);
112 
113         mMobileDrawable = new SignalDrawable(getContext());
114         mMobile.setImageDrawable(mMobileDrawable);
115 
116         initDotView();
117     }
118 
initDotView()119     private void initDotView() {
120         mDotView = new StatusBarIconView(mContext, mSlot, null);
121         mDotView.setVisibleState(STATE_DOT);
122 
123         int width = mContext.getResources().getDimensionPixelSize(R.dimen.status_bar_icon_size);
124         LayoutParams lp = new LayoutParams(width, width);
125         lp.gravity = Gravity.CENTER_VERTICAL | Gravity.START;
126         addView(mDotView, lp);
127     }
128 
applyMobileState(MobileIconState state)129     public void applyMobileState(MobileIconState state) {
130         boolean requestLayout = false;
131         if (state == null) {
132             requestLayout = getVisibility() != View.GONE;
133             setVisibility(View.GONE);
134             mState = null;
135         } else if (mState == null) {
136             requestLayout = true;
137             mState = state.copy();
138             initViewState();
139         } else if (!mState.equals(state)) {
140             requestLayout = updateState(state.copy());
141         }
142 
143         if (requestLayout) {
144             requestLayout();
145         }
146     }
147 
initViewState()148     private void initViewState() {
149         setContentDescription(mState.contentDescription);
150         if (!mState.visible) {
151             mMobileGroup.setVisibility(View.GONE);
152         } else {
153             mMobileGroup.setVisibility(View.VISIBLE);
154         }
155         mMobileDrawable.setLevel(mState.strengthId);
156         if (mState.typeId > 0) {
157             mMobileType.setContentDescription(mState.typeContentDescription);
158             mMobileType.setImageResource(mState.typeId);
159             mMobileType.setVisibility(View.VISIBLE);
160         } else {
161             mMobileType.setVisibility(View.GONE);
162         }
163 
164         mMobileRoaming.setVisibility(mState.roaming ? View.VISIBLE : View.GONE);
165         mMobileRoamingSpace.setVisibility(mState.roaming ? View.VISIBLE : View.GONE);
166         mIn.setVisibility(mState.activityIn ? View.VISIBLE : View.GONE);
167         mOut.setVisibility(mState.activityOut ? View.VISIBLE : View.GONE);
168         mInoutContainer.setVisibility((mState.activityIn || mState.activityOut)
169                 ? View.VISIBLE : View.GONE);
170     }
171 
updateState(MobileIconState state)172     private boolean updateState(MobileIconState state) {
173         boolean needsLayout = false;
174 
175         setContentDescription(state.contentDescription);
176         if (mState.visible != state.visible) {
177             mMobileGroup.setVisibility(state.visible ? View.VISIBLE : View.GONE);
178             needsLayout = true;
179         }
180         if (mState.strengthId != state.strengthId) {
181             mMobileDrawable.setLevel(state.strengthId);
182         }
183         if (mState.typeId != state.typeId) {
184             needsLayout |= state.typeId == 0 || mState.typeId == 0;
185             if (state.typeId != 0) {
186                 mMobileType.setContentDescription(state.typeContentDescription);
187                 mMobileType.setImageResource(state.typeId);
188                 mMobileType.setVisibility(View.VISIBLE);
189             } else {
190                 mMobileType.setVisibility(View.GONE);
191             }
192         }
193 
194         mMobileRoaming.setVisibility(state.roaming ? View.VISIBLE : View.GONE);
195         mMobileRoamingSpace.setVisibility(state.roaming ? View.VISIBLE : View.GONE);
196         mIn.setVisibility(state.activityIn ? View.VISIBLE : View.GONE);
197         mOut.setVisibility(state.activityOut ? View.VISIBLE : View.GONE);
198         mInoutContainer.setVisibility((state.activityIn || state.activityOut)
199                 ? View.VISIBLE : View.GONE);
200 
201         needsLayout |= state.roaming != mState.roaming
202                 || state.activityIn != mState.activityIn
203                 || state.activityOut != mState.activityOut;
204 
205         mState = state;
206         return needsLayout;
207     }
208 
209     @Override
onDarkChanged(Rect area, float darkIntensity, int tint)210     public void onDarkChanged(Rect area, float darkIntensity, int tint) {
211         float intensity = isInArea(area, this) ? darkIntensity : 0;
212         mMobileDrawable.setTintList(
213                 ColorStateList.valueOf(mDualToneHandler.getSingleColor(intensity)));
214         ColorStateList color = ColorStateList.valueOf(getTint(area, this, tint));
215         mIn.setImageTintList(color);
216         mOut.setImageTintList(color);
217         mMobileType.setImageTintList(color);
218         mMobileRoaming.setImageTintList(color);
219         mDotView.setDecorColor(tint);
220         mDotView.setIconColor(tint, false);
221     }
222 
223     @Override
getSlot()224     public String getSlot() {
225         return mSlot;
226     }
227 
setSlot(String slot)228     public void setSlot(String slot) {
229         mSlot = slot;
230     }
231 
232     @Override
setStaticDrawableColor(int color)233     public void setStaticDrawableColor(int color) {
234         ColorStateList list = ColorStateList.valueOf(color);
235         float intensity = color == Color.WHITE ? 0 : 1;
236         // We want the ability to change the theme from the one set by SignalDrawable in certain
237         // surfaces. In this way, we can pass a theme to the view.
238         mMobileDrawable.setTintList(
239                 ColorStateList.valueOf(mDualToneHandler.getSingleColor(intensity)));
240         mIn.setImageTintList(list);
241         mOut.setImageTintList(list);
242         mMobileType.setImageTintList(list);
243         mMobileRoaming.setImageTintList(list);
244         mDotView.setDecorColor(color);
245     }
246 
247     @Override
setDecorColor(int color)248     public void setDecorColor(int color) {
249         mDotView.setDecorColor(color);
250     }
251 
252     @Override
isIconVisible()253     public boolean isIconVisible() {
254         return mState.visible;
255     }
256 
257     @Override
setVisibleState(int state, boolean animate)258     public void setVisibleState(int state, boolean animate) {
259         if (state == mVisibleState) {
260             return;
261         }
262 
263         mVisibleState = state;
264         switch (state) {
265             case STATE_ICON:
266                 mMobileGroup.setVisibility(View.VISIBLE);
267                 mDotView.setVisibility(View.GONE);
268                 break;
269             case STATE_DOT:
270                 mMobileGroup.setVisibility(View.INVISIBLE);
271                 mDotView.setVisibility(View.VISIBLE);
272                 break;
273             case STATE_HIDDEN:
274             default:
275                 mMobileGroup.setVisibility(View.INVISIBLE);
276                 mDotView.setVisibility(View.INVISIBLE);
277                 break;
278         }
279     }
280 
281     @Override
getVisibleState()282     public int getVisibleState() {
283         return mVisibleState;
284     }
285 
286     @VisibleForTesting
getState()287     public MobileIconState getState() {
288         return mState;
289     }
290 
291     @Override
toString()292     public String toString() {
293         return "StatusBarMobileView(slot=" + mSlot + " state=" + mState + ")";
294     }
295 }
296