• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.isInAreas;
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.Rect;
28 import android.util.AttributeSet;
29 import android.view.Gravity;
30 import android.view.LayoutInflater;
31 import android.view.View;
32 import android.widget.ImageView;
33 import android.widget.LinearLayout;
34 
35 import com.android.internal.annotations.VisibleForTesting;
36 import com.android.settingslib.graph.SignalDrawable;
37 import com.android.systemui.DualToneHandler;
38 import com.android.systemui.R;
39 import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
40 import com.android.systemui.statusbar.phone.StatusBarIconController;
41 import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState;
42 
43 import java.util.ArrayList;
44 
45 /**
46  * View group for the mobile icon in the status bar
47  */
48 public class StatusBarMobileView extends BaseStatusBarFrameLayout implements DarkReceiver,
49         StatusIconDisplayable {
50     private static final String TAG = "StatusBarMobileView";
51 
52     /// Used to show etc dots
53     private StatusBarIconView mDotView;
54     /// The main icon view
55     private LinearLayout mMobileGroup;
56     private String mSlot;
57     private MobileIconState mState;
58     private SignalDrawable mMobileDrawable;
59     private View mInoutContainer;
60     private ImageView mIn;
61     private ImageView mOut;
62     private ImageView mMobile, mMobileType, mMobileRoaming;
63     private View mMobileRoamingSpace;
64     @StatusBarIconView.VisibleState
65     private int mVisibleState = STATE_HIDDEN;
66     private DualToneHandler mDualToneHandler;
67     private boolean mForceHidden;
68 
69     /**
70      * Designated constructor
71      *
72      * This view is special, in that it is the only view in SystemUI that allows for a configuration
73      * override on a MCC/MNC-basis. This means that for every mobile view inflated, we have to
74      * construct a context with that override, since the resource system doesn't have a way to
75      * handle this for us.
76      *
77      * @param context A context with resources configured by MCC/MNC
78      * @param slot The string key defining which slot this icon refers to. Always "mobile" for the
79      *             mobile icon
80      */
fromContext( Context context, String slot )81     public static StatusBarMobileView fromContext(
82             Context context,
83             String slot
84     ) {
85         LayoutInflater inflater = LayoutInflater.from(context);
86         StatusBarMobileView v = (StatusBarMobileView)
87                 inflater.inflate(R.layout.status_bar_mobile_signal_group, null);
88         v.setSlot(slot);
89         v.init();
90         v.setVisibleState(STATE_ICON);
91         return v;
92     }
93 
StatusBarMobileView(Context context)94     public StatusBarMobileView(Context context) {
95         super(context);
96     }
97 
StatusBarMobileView(Context context, AttributeSet attrs)98     public StatusBarMobileView(Context context, AttributeSet attrs) {
99         super(context, attrs);
100     }
101 
StatusBarMobileView(Context context, AttributeSet attrs, int defStyleAttr)102     public StatusBarMobileView(Context context, AttributeSet attrs, int defStyleAttr) {
103         super(context, attrs, defStyleAttr);
104     }
105 
106     @Override
getDrawingRect(Rect outRect)107     public void getDrawingRect(Rect outRect) {
108         super.getDrawingRect(outRect);
109         float translationX = getTranslationX();
110         float translationY = getTranslationY();
111         outRect.left += translationX;
112         outRect.right += translationX;
113         outRect.top += translationY;
114         outRect.bottom += translationY;
115     }
116 
init()117     private void init() {
118         mDualToneHandler = new DualToneHandler(getContext());
119         mMobileGroup = findViewById(R.id.mobile_group);
120         mMobile = findViewById(R.id.mobile_signal);
121         mMobileType = findViewById(R.id.mobile_type);
122         mMobileRoaming = findViewById(R.id.mobile_roaming);
123         mMobileRoamingSpace = findViewById(R.id.mobile_roaming_space);
124         mIn = findViewById(R.id.mobile_in);
125         mOut = findViewById(R.id.mobile_out);
126         mInoutContainer = findViewById(R.id.inout_container);
127 
128         mMobileDrawable = new SignalDrawable(getContext());
129         mMobile.setImageDrawable(mMobileDrawable);
130 
131         initDotView();
132     }
133 
initDotView()134     private void initDotView() {
135         mDotView = new StatusBarIconView(mContext, mSlot, null);
136         mDotView.setVisibleState(STATE_DOT);
137 
138         int width = mContext.getResources().getDimensionPixelSize(R.dimen.status_bar_icon_size);
139         LayoutParams lp = new LayoutParams(width, width);
140         lp.gravity = Gravity.CENTER_VERTICAL | Gravity.START;
141         addView(mDotView, lp);
142     }
143 
applyMobileState(MobileIconState state)144     public void applyMobileState(MobileIconState state) {
145         boolean requestLayout = false;
146         if (state == null) {
147             requestLayout = getVisibility() != View.GONE;
148             setVisibility(View.GONE);
149             mState = null;
150         } else if (mState == null) {
151             requestLayout = true;
152             mState = state.copy();
153             initViewState();
154         } else if (!mState.equals(state)) {
155             requestLayout = updateState(state.copy());
156         }
157 
158         if (requestLayout) {
159             requestLayout();
160         }
161     }
162 
initViewState()163     private void initViewState() {
164         setContentDescription(mState.contentDescription);
165         if (!mState.visible || mForceHidden) {
166             mMobileGroup.setVisibility(View.GONE);
167         } else {
168             mMobileGroup.setVisibility(View.VISIBLE);
169         }
170         mMobileDrawable.setLevel(mState.strengthId);
171         if (mState.typeId > 0) {
172             mMobileType.setContentDescription(mState.typeContentDescription);
173             mMobileType.setImageResource(mState.typeId);
174             mMobileType.setVisibility(View.VISIBLE);
175         } else {
176             mMobileType.setVisibility(View.GONE);
177         }
178         mMobile.setVisibility(mState.showTriangle ? View.VISIBLE : View.GONE);
179         mMobileRoaming.setVisibility(mState.roaming ? View.VISIBLE : View.GONE);
180         mMobileRoamingSpace.setVisibility(mState.roaming ? View.VISIBLE : View.GONE);
181         mIn.setVisibility(mState.activityIn ? View.VISIBLE : View.GONE);
182         mOut.setVisibility(mState.activityOut ? View.VISIBLE : View.GONE);
183         mInoutContainer.setVisibility((mState.activityIn || mState.activityOut)
184                 ? View.VISIBLE : View.GONE);
185     }
186 
updateState(MobileIconState state)187     private boolean updateState(MobileIconState state) {
188         boolean needsLayout = false;
189 
190         setContentDescription(state.contentDescription);
191         int newVisibility = state.visible && !mForceHidden ? View.VISIBLE : View.GONE;
192         if (newVisibility != mMobileGroup.getVisibility() && STATE_ICON == mVisibleState) {
193             mMobileGroup.setVisibility(newVisibility);
194             needsLayout = true;
195         }
196         if (mState.strengthId != state.strengthId) {
197             mMobileDrawable.setLevel(state.strengthId);
198         }
199         if (mState.typeId != state.typeId) {
200             needsLayout |= state.typeId == 0 || mState.typeId == 0;
201             if (state.typeId != 0) {
202                 mMobileType.setContentDescription(state.typeContentDescription);
203                 mMobileType.setImageResource(state.typeId);
204                 mMobileType.setVisibility(View.VISIBLE);
205             } else {
206                 mMobileType.setVisibility(View.GONE);
207             }
208         }
209 
210         mMobile.setVisibility(state.showTriangle ? View.VISIBLE : View.GONE);
211         mMobileRoaming.setVisibility(state.roaming ? View.VISIBLE : View.GONE);
212         mMobileRoamingSpace.setVisibility(state.roaming ? View.VISIBLE : View.GONE);
213         mIn.setVisibility(state.activityIn ? View.VISIBLE : View.GONE);
214         mOut.setVisibility(state.activityOut ? View.VISIBLE : View.GONE);
215         mInoutContainer.setVisibility((state.activityIn || state.activityOut)
216                 ? View.VISIBLE : View.GONE);
217 
218         needsLayout |= state.roaming != mState.roaming
219                 || state.activityIn != mState.activityIn
220                 || state.activityOut != mState.activityOut
221                 || state.showTriangle != mState.showTriangle;
222 
223         mState = state;
224         return needsLayout;
225     }
226 
227     @Override
onDarkChanged(ArrayList<Rect> areas, float darkIntensity, int tint)228     public void onDarkChanged(ArrayList<Rect> areas, float darkIntensity, int tint) {
229         float intensity = isInAreas(areas, this) ? darkIntensity : 0;
230         mMobileDrawable.setTintList(
231                 ColorStateList.valueOf(mDualToneHandler.getSingleColor(intensity)));
232         ColorStateList color = ColorStateList.valueOf(getTint(areas, this, tint));
233         mIn.setImageTintList(color);
234         mOut.setImageTintList(color);
235         mMobileType.setImageTintList(color);
236         mMobileRoaming.setImageTintList(color);
237         mDotView.setDecorColor(tint);
238         mDotView.setIconColor(tint, false);
239     }
240 
241     @Override
getSlot()242     public String getSlot() {
243         return mSlot;
244     }
245 
setSlot(String slot)246     public void setSlot(String slot) {
247         mSlot = slot;
248     }
249 
250     @Override
setStaticDrawableColor(int color)251     public void setStaticDrawableColor(int color) {
252         ColorStateList list = ColorStateList.valueOf(color);
253         mMobileDrawable.setTintList(list);
254         mIn.setImageTintList(list);
255         mOut.setImageTintList(list);
256         mMobileType.setImageTintList(list);
257         mMobileRoaming.setImageTintList(list);
258         mDotView.setDecorColor(color);
259     }
260 
261     @Override
setDecorColor(int color)262     public void setDecorColor(int color) {
263         mDotView.setDecorColor(color);
264     }
265 
266     @Override
isIconVisible()267     public boolean isIconVisible() {
268         return mState.visible && !mForceHidden;
269     }
270 
271     @Override
setVisibleState(@tatusBarIconView.VisibleState int state, boolean animate)272     public void setVisibleState(@StatusBarIconView.VisibleState int state, boolean animate) {
273         if (state == mVisibleState) {
274             return;
275         }
276 
277         mVisibleState = state;
278         switch (state) {
279             case STATE_ICON:
280                 mMobileGroup.setVisibility(View.VISIBLE);
281                 mDotView.setVisibility(View.GONE);
282                 break;
283             case STATE_DOT:
284                 mMobileGroup.setVisibility(View.INVISIBLE);
285                 mDotView.setVisibility(View.VISIBLE);
286                 break;
287             case STATE_HIDDEN:
288             default:
289                 mMobileGroup.setVisibility(View.INVISIBLE);
290                 mDotView.setVisibility(View.INVISIBLE);
291                 break;
292         }
293     }
294 
295     /**
296      * Forces the state to be hidden (views will be GONE) and if necessary updates the layout.
297      *
298      * Makes sure that the {@link StatusBarIconController} cannot make it visible while this flag
299      * is enabled.
300      * @param forceHidden {@code true} if the icon should be GONE in its view regardless of its
301      *                                state.
302      *               {@code false} if the icon should show as determined by its controller.
303      */
forceHidden(boolean forceHidden)304     public void forceHidden(boolean forceHidden) {
305         if (mForceHidden != forceHidden) {
306             mForceHidden = forceHidden;
307             updateState(mState);
308             requestLayout();
309         }
310     }
311 
312     @Override
313     @StatusBarIconView.VisibleState
getVisibleState()314     public int getVisibleState() {
315         return mVisibleState;
316     }
317 
318     @VisibleForTesting
getState()319     public MobileIconState getState() {
320         return mState;
321     }
322 
323     @Override
toString()324     public String toString() {
325         return "StatusBarMobileView(slot=" + mSlot + " state=" + mState + ")";
326     }
327 }
328