1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 * except in compliance with the License. You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software distributed under the 10 * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 11 * KIND, either express or implied. See the License for the specific language governing 12 * permissions and limitations under the License. 13 */ 14 15 package com.android.systemui.util; 16 17 import static android.view.Display.DEFAULT_DISPLAY; 18 19 import android.Manifest; 20 import android.content.Context; 21 import android.content.Intent; 22 import android.content.pm.PackageManager; 23 import android.content.res.Resources; 24 import android.content.res.TypedArray; 25 import android.provider.Settings; 26 import android.view.ContextThemeWrapper; 27 import android.view.View; 28 29 import com.android.systemui.R; 30 import com.android.systemui.shared.system.QuickStepContract; 31 import com.android.systemui.statusbar.CommandQueue; 32 import com.android.systemui.statusbar.FeatureFlags; 33 34 import java.util.List; 35 import java.util.function.Consumer; 36 37 public class Utils { 38 39 /** 40 * Allows lambda iteration over a list. It is done in reverse order so it is safe 41 * to add or remove items during the iteration. Skips over null items. 42 */ safeForeach(List<T> list, Consumer<T> c)43 public static <T> void safeForeach(List<T> list, Consumer<T> c) { 44 for (int i = list.size() - 1; i >= 0; i--) { 45 T item = list.get(i); 46 if (item != null) { 47 c.accept(item); 48 } 49 } 50 } 51 52 /** 53 * Sets the visibility of an UI element according to the DISABLE_* flags in 54 * {@link android.app.StatusBarManager}. 55 */ 56 public static class DisableStateTracker implements CommandQueue.Callbacks, 57 View.OnAttachStateChangeListener { 58 private final int mMask1; 59 private final int mMask2; 60 private final CommandQueue mCommandQueue; 61 private View mView; 62 private boolean mDisabled; 63 DisableStateTracker(int disableMask, int disable2Mask, CommandQueue commandQueue)64 public DisableStateTracker(int disableMask, int disable2Mask, CommandQueue commandQueue) { 65 mMask1 = disableMask; 66 mMask2 = disable2Mask; 67 mCommandQueue = commandQueue; 68 } 69 70 @Override onViewAttachedToWindow(View v)71 public void onViewAttachedToWindow(View v) { 72 mView = v; 73 mCommandQueue.addCallback(this); 74 } 75 76 @Override onViewDetachedFromWindow(View v)77 public void onViewDetachedFromWindow(View v) { 78 mCommandQueue.removeCallback(this); 79 mView = null; 80 } 81 82 /** 83 * Sets visibility of this {@link View} given the states passed from 84 * {@link com.android.systemui.statusbar.CommandQueue.Callbacks#disable(int, int, int)}. 85 */ 86 @Override disable(int displayId, int state1, int state2, boolean animate)87 public void disable(int displayId, int state1, int state2, boolean animate) { 88 if (displayId != mView.getDisplay().getDisplayId()) { 89 return; 90 } 91 final boolean disabled = ((state1 & mMask1) != 0) || ((state2 & mMask2) != 0); 92 if (disabled == mDisabled) return; 93 mDisabled = disabled; 94 mView.setVisibility(disabled ? View.GONE : View.VISIBLE); 95 } 96 97 /** @return {@code true} if and only if this {@link View} is currently disabled */ isDisabled()98 public boolean isDisabled() { 99 return mDisabled; 100 } 101 } 102 103 104 /** 105 * Returns {@code true} iff the package {@code packageName} is a headless remote display 106 * provider, i.e, that the package holds the privileged {@code REMOTE_DISPLAY_PROVIDER} 107 * permission and that it doesn't host a launcher icon. 108 */ isHeadlessRemoteDisplayProvider(PackageManager pm, String packageName)109 public static boolean isHeadlessRemoteDisplayProvider(PackageManager pm, String packageName) { 110 if (pm.checkPermission(Manifest.permission.REMOTE_DISPLAY_PROVIDER, packageName) 111 != PackageManager.PERMISSION_GRANTED) { 112 return false; 113 } 114 115 Intent homeIntent = new Intent(Intent.ACTION_MAIN); 116 homeIntent.addCategory(Intent.CATEGORY_LAUNCHER); 117 homeIntent.setPackage(packageName); 118 119 return pm.queryIntentActivities(homeIntent, 0).isEmpty(); 120 } 121 122 /** 123 * Returns {@code true} if the navMode is that of 124 * {@link android.view.WindowManagerPolicyConstants#NAV_BAR_MODE_GESTURAL} AND 125 * the context is that of the default display 126 */ isGesturalModeOnDefaultDisplay(Context context, int navMode)127 public static boolean isGesturalModeOnDefaultDisplay(Context context, int navMode) { 128 return context.getDisplayId() == DEFAULT_DISPLAY 129 && QuickStepContract.isGesturalMode(navMode); 130 } 131 132 /** 133 * Allow the media player to be shown in the QS area, controlled by 2 flags. 134 * Off by default, but can be disabled by setting to 0 135 */ useQsMediaPlayer(Context context)136 public static boolean useQsMediaPlayer(Context context) { 137 int flag = Settings.Global.getInt(context.getContentResolver(), 138 Settings.Global.SHOW_MEDIA_ON_QUICK_SETTINGS, 1); 139 return flag > 0; 140 } 141 142 /** 143 * Allow media resumption controls. Requires {@link #useQsMediaPlayer(Context)} to be enabled. 144 * On by default, but can be disabled by setting to 0 145 */ useMediaResumption(Context context)146 public static boolean useMediaResumption(Context context) { 147 int flag = Settings.Secure.getInt(context.getContentResolver(), 148 Settings.Secure.MEDIA_CONTROLS_RESUME, 1); 149 return useQsMediaPlayer(context) && flag > 0; 150 } 151 152 /** 153 * Allow recommendations from smartspace to show in media controls. 154 * Requires {@link #useQsMediaPlayer(Context)} to be enabled. 155 * On by default, but can be disabled by setting to 0 156 */ allowMediaRecommendations(Context context)157 public static boolean allowMediaRecommendations(Context context) { 158 int flag = Settings.Secure.getInt(context.getContentResolver(), 159 Settings.Secure.MEDIA_CONTROLS_RECOMMENDATION, 1); 160 return useQsMediaPlayer(context) && flag > 0; 161 } 162 163 /** 164 * Returns true if the device should use the split notification shade, based on feature flags, 165 * orientation and screen width. 166 */ shouldUseSplitNotificationShade(FeatureFlags featureFlags, Resources resources)167 public static boolean shouldUseSplitNotificationShade(FeatureFlags featureFlags, 168 Resources resources) { 169 return featureFlags.isTwoColumnNotificationShadeEnabled() 170 && resources.getBoolean(R.bool.config_use_split_notification_shade); 171 } 172 173 /** 174 * Returns the color provided at the specified {@param attrIndex} in {@param a} if it exists, 175 * otherwise, returns the color from the private attribute {@param privAttrId}. 176 */ getPrivateAttrColorIfUnset(ContextThemeWrapper ctw, TypedArray a, int attrIndex, int defColor, int privAttrId)177 public static int getPrivateAttrColorIfUnset(ContextThemeWrapper ctw, TypedArray a, 178 int attrIndex, int defColor, int privAttrId) { 179 // If the index is specified, use that value 180 if (a.hasValue(attrIndex)) { 181 return a.getColor(attrIndex, defColor); 182 } 183 184 // Otherwise fallback to the value of the private attribute 185 int[] customAttrs = { privAttrId }; 186 a = ctw.obtainStyledAttributes(customAttrs); 187 int color = a.getColor(0, defColor); 188 a.recycle(); 189 return color; 190 } 191 192 } 193