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