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