/* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software distributed under the * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the specific language governing * permissions and limitations under the License. */ package com.android.systemui.statusbar.phone; import static android.app.StatusBarManager.DISABLE2_SYSTEM_ICONS; import static android.app.StatusBarManager.DISABLE_NONE; import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_ICON; import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_MOBILE; import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_WIFI; import android.content.Context; import android.os.Bundle; import android.text.TextUtils; import android.util.ArraySet; import android.view.Gravity; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.LinearLayout.LayoutParams; import androidx.annotation.VisibleForTesting; import com.android.internal.statusbar.StatusBarIcon; import com.android.systemui.DemoMode; import com.android.systemui.Dependency; import com.android.systemui.R; import com.android.systemui.plugins.DarkIconDispatcher; import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver; import com.android.systemui.statusbar.StatusBarIconView; import com.android.systemui.statusbar.StatusBarMobileView; import com.android.systemui.statusbar.StatusBarWifiView; import com.android.systemui.statusbar.StatusIconDisplayable; import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState; import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.WifiIconState; import com.android.systemui.util.Utils.DisableStateTracker; import java.util.List; public interface StatusBarIconController { /** * When an icon is added with TAG_PRIMARY, it will be treated as the primary icon * in that slot and not added as a sub slot. */ public static final int TAG_PRIMARY = 0; public void addIconGroup(IconManager iconManager); public void removeIconGroup(IconManager iconManager); public void setExternalIcon(String slot); public void setIcon(String slot, int resourceId, CharSequence contentDescription); public void setIcon(String slot, StatusBarIcon icon); public void setSignalIcon(String slot, WifiIconState state); public void setMobileIcons(String slot, List states); public void setIconVisibility(String slot, boolean b); /** * If you don't know what to pass for `tag`, either remove all icons for slot, or use * TAG_PRIMARY to refer to the first icon at a given slot. */ public void removeIcon(String slot, int tag); public void removeAllIconsForSlot(String slot); public static final String ICON_BLACKLIST = "icon_blacklist"; public static ArraySet getIconBlacklist(String blackListStr) { ArraySet ret = new ArraySet<>(); if (blackListStr == null) { blackListStr = "rotate,headset"; } String[] blacklist = blackListStr.split(","); for (String slot : blacklist) { if (!TextUtils.isEmpty(slot)) { ret.add(slot); } } return ret; } /** * Version of ViewGroup that observes state from the DarkIconDispatcher. */ public static class DarkIconManager extends IconManager { private final DarkIconDispatcher mDarkIconDispatcher; private int mIconHPadding; public DarkIconManager(LinearLayout linearLayout) { super(linearLayout); mIconHPadding = mContext.getResources().getDimensionPixelSize( R.dimen.status_bar_icon_padding); mDarkIconDispatcher = Dependency.get(DarkIconDispatcher.class); } @Override protected void onIconAdded(int index, String slot, boolean blocked, StatusBarIconHolder holder) { StatusIconDisplayable view = addHolder(index, slot, blocked, holder); mDarkIconDispatcher.addDarkReceiver((DarkReceiver) view); } @Override protected LayoutParams onCreateLayoutParams() { LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams( ViewGroup.LayoutParams.WRAP_CONTENT, mIconSize); lp.setMargins(mIconHPadding, 0, mIconHPadding, 0); return lp; } @Override protected void destroy() { for (int i = 0; i < mGroup.getChildCount(); i++) { mDarkIconDispatcher.removeDarkReceiver((DarkReceiver) mGroup.getChildAt(i)); } mGroup.removeAllViews(); } @Override protected void onRemoveIcon(int viewIndex) { mDarkIconDispatcher.removeDarkReceiver((DarkReceiver) mGroup.getChildAt(viewIndex)); super.onRemoveIcon(viewIndex); } @Override public void onSetIcon(int viewIndex, StatusBarIcon icon) { super.onSetIcon(viewIndex, icon); mDarkIconDispatcher.applyDark((DarkReceiver) mGroup.getChildAt(viewIndex)); } @Override protected DemoStatusIcons createDemoStatusIcons() { DemoStatusIcons icons = super.createDemoStatusIcons(); mDarkIconDispatcher.addDarkReceiver(icons); return icons; } @Override protected void exitDemoMode() { mDarkIconDispatcher.removeDarkReceiver(mDemoStatusIcons); super.exitDemoMode(); } } public static class TintedIconManager extends IconManager { private int mColor; public TintedIconManager(ViewGroup group) { super(group); } @Override protected void onIconAdded(int index, String slot, boolean blocked, StatusBarIconHolder holder) { StatusIconDisplayable view = addHolder(index, slot, blocked, holder); view.setStaticDrawableColor(mColor); view.setDecorColor(mColor); } public void setTint(int color) { mColor = color; for (int i = 0; i < mGroup.getChildCount(); i++) { View child = mGroup.getChildAt(i); if (child instanceof StatusIconDisplayable) { StatusIconDisplayable icon = (StatusIconDisplayable) child; icon.setStaticDrawableColor(mColor); icon.setDecorColor(mColor); } } } @Override protected DemoStatusIcons createDemoStatusIcons() { DemoStatusIcons icons = super.createDemoStatusIcons(); icons.setColor(mColor); return icons; } } /** * Turns info from StatusBarIconController into ImageViews in a ViewGroup. */ public static class IconManager implements DemoMode { protected final ViewGroup mGroup; protected final Context mContext; protected final int mIconSize; // Whether or not these icons show up in dumpsys protected boolean mShouldLog = false; // Enables SystemUI demo mode to take effect in this group protected boolean mDemoable = true; private boolean mIsInDemoMode; protected DemoStatusIcons mDemoStatusIcons; public IconManager(ViewGroup group) { mGroup = group; mContext = group.getContext(); mIconSize = mContext.getResources().getDimensionPixelSize( com.android.internal.R.dimen.status_bar_icon_size); DisableStateTracker tracker = new DisableStateTracker(DISABLE_NONE, DISABLE2_SYSTEM_ICONS); mGroup.addOnAttachStateChangeListener(tracker); if (mGroup.isAttachedToWindow()) { // In case we miss the first onAttachedToWindow event tracker.onViewAttachedToWindow(mGroup); } } public boolean isDemoable() { return mDemoable; } public void setIsDemoable(boolean demoable) { mDemoable = demoable; } public void setShouldLog(boolean should) { mShouldLog = should; } public boolean shouldLog() { return mShouldLog; } protected void onIconAdded(int index, String slot, boolean blocked, StatusBarIconHolder holder) { addHolder(index, slot, blocked, holder); } protected StatusIconDisplayable addHolder(int index, String slot, boolean blocked, StatusBarIconHolder holder) { switch (holder.getType()) { case TYPE_ICON: return addIcon(index, slot, blocked, holder.getIcon()); case TYPE_WIFI: return addSignalIcon(index, slot, holder.getWifiState()); case TYPE_MOBILE: return addMobileIcon(index, slot, holder.getMobileState()); } return null; } @VisibleForTesting protected StatusBarIconView addIcon(int index, String slot, boolean blocked, StatusBarIcon icon) { StatusBarIconView view = onCreateStatusBarIconView(slot, blocked); view.set(icon); mGroup.addView(view, index, onCreateLayoutParams()); return view; } @VisibleForTesting protected StatusBarWifiView addSignalIcon(int index, String slot, WifiIconState state) { StatusBarWifiView view = onCreateStatusBarWifiView(slot); view.applyWifiState(state); mGroup.addView(view, index, onCreateLayoutParams()); if (mIsInDemoMode) { mDemoStatusIcons.addDemoWifiView(state); } return view; } @VisibleForTesting protected StatusBarMobileView addMobileIcon(int index, String slot, MobileIconState state) { StatusBarMobileView view = onCreateStatusBarMobileView(slot); view.applyMobileState(state); mGroup.addView(view, index, onCreateLayoutParams()); if (mIsInDemoMode) { mDemoStatusIcons.addMobileView(state); } return view; } private StatusBarIconView onCreateStatusBarIconView(String slot, boolean blocked) { return new StatusBarIconView(mContext, slot, null, blocked); } private StatusBarWifiView onCreateStatusBarWifiView(String slot) { StatusBarWifiView view = StatusBarWifiView.fromContext(mContext, slot); return view; } private StatusBarMobileView onCreateStatusBarMobileView(String slot) { StatusBarMobileView view = StatusBarMobileView.fromContext(mContext, slot); return view; } protected LinearLayout.LayoutParams onCreateLayoutParams() { return new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, mIconSize); } protected void destroy() { mGroup.removeAllViews(); } protected void onIconExternal(int viewIndex, int height) { ImageView imageView = (ImageView) mGroup.getChildAt(viewIndex); imageView.setScaleType(ImageView.ScaleType.FIT_CENTER); imageView.setAdjustViewBounds(true); setHeightAndCenter(imageView, height); } protected void onDensityOrFontScaleChanged() { for (int i = 0; i < mGroup.getChildCount(); i++) { View child = mGroup.getChildAt(i); LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams( ViewGroup.LayoutParams.WRAP_CONTENT, mIconSize); child.setLayoutParams(lp); } } private void setHeightAndCenter(ImageView imageView, int height) { ViewGroup.LayoutParams params = imageView.getLayoutParams(); params.height = height; if (params instanceof LinearLayout.LayoutParams) { ((LinearLayout.LayoutParams) params).gravity = Gravity.CENTER_VERTICAL; } imageView.setLayoutParams(params); } protected void onRemoveIcon(int viewIndex) { if (mIsInDemoMode) { mDemoStatusIcons.onRemoveIcon((StatusIconDisplayable) mGroup.getChildAt(viewIndex)); } mGroup.removeViewAt(viewIndex); } public void onSetIcon(int viewIndex, StatusBarIcon icon) { StatusBarIconView view = (StatusBarIconView) mGroup.getChildAt(viewIndex); view.set(icon); } public void onSetIconHolder(int viewIndex, StatusBarIconHolder holder) { switch (holder.getType()) { case TYPE_ICON: onSetIcon(viewIndex, holder.getIcon()); return; case TYPE_WIFI: onSetSignalIcon(viewIndex, holder.getWifiState()); return; case TYPE_MOBILE: onSetMobileIcon(viewIndex, holder.getMobileState()); default: break; } } public void onSetSignalIcon(int viewIndex, WifiIconState state) { StatusBarWifiView wifiView = (StatusBarWifiView) mGroup.getChildAt(viewIndex); if (wifiView != null) { wifiView.applyWifiState(state); } if (mIsInDemoMode) { mDemoStatusIcons.updateWifiState(state); } } public void onSetMobileIcon(int viewIndex, MobileIconState state) { StatusBarMobileView view = (StatusBarMobileView) mGroup.getChildAt(viewIndex); if (view != null) { view.applyMobileState(state); } if (mIsInDemoMode) { mDemoStatusIcons.updateMobileState(state); } } @Override public void dispatchDemoCommand(String command, Bundle args) { if (!mDemoable) { return; } if (command.equals(COMMAND_EXIT)) { if (mDemoStatusIcons != null) { mDemoStatusIcons.dispatchDemoCommand(command, args); exitDemoMode(); } mIsInDemoMode = false; } else { if (mDemoStatusIcons == null) { mIsInDemoMode = true; mDemoStatusIcons = createDemoStatusIcons(); } mDemoStatusIcons.dispatchDemoCommand(command, args); } } protected void exitDemoMode() { mDemoStatusIcons.remove(); mDemoStatusIcons = null; } protected DemoStatusIcons createDemoStatusIcons() { return new DemoStatusIcons((LinearLayout) mGroup, mIconSize); } } }