1 /* 2 * Copyright (C) 2020 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 package com.android.settings.network; 17 18 import static com.android.settings.network.MobileIconGroupExtKt.maybeToHtml; 19 import static com.android.settings.network.telephony.MobileNetworkUtils.NO_CELL_DATA_TYPE_ICON; 20 21 import android.app.PendingIntent; 22 import android.content.Context; 23 import android.content.Intent; 24 import android.graphics.Color; 25 import android.graphics.drawable.ColorDrawable; 26 import android.graphics.drawable.Drawable; 27 import android.net.Uri; 28 import android.telephony.AccessNetworkConstants; 29 import android.telephony.NetworkRegistrationInfo; 30 import android.telephony.ServiceState; 31 import android.telephony.SignalStrength; 32 import android.telephony.SubscriptionInfo; 33 import android.telephony.SubscriptionManager; 34 import android.telephony.TelephonyManager; 35 import android.text.TextUtils; 36 import android.util.Log; 37 38 import androidx.annotation.Nullable; 39 import androidx.annotation.VisibleForTesting; 40 import androidx.core.graphics.drawable.IconCompat; 41 import androidx.slice.builders.ListBuilder; 42 import androidx.slice.builders.SliceAction; 43 44 import com.android.settings.R; 45 import com.android.settings.Utils; 46 import com.android.settings.network.telephony.MobileNetworkUtils; 47 import com.android.settings.slices.CustomSliceable; 48 import com.android.settings.wifi.slice.WifiSliceItem; 49 import com.android.settingslib.WirelessUtils; 50 import com.android.settingslib.net.SignalStrengthUtil; 51 import com.android.settingslib.utils.ThreadUtils; 52 import com.android.wifitrackerlib.WifiEntry; 53 54 import java.util.Arrays; 55 import java.util.List; 56 import java.util.Optional; 57 import java.util.Set; 58 import java.util.concurrent.Semaphore; 59 import java.util.concurrent.atomic.AtomicReference; 60 import java.util.stream.Collectors; 61 62 /** 63 * The helper is for slice of carrier and non-Carrier, used by ProviderModelSlice. 64 * TODO: Remove the class in U because Settings does not use slice anymore. 65 */ 66 public class ProviderModelSliceHelper { 67 private static final String TAG = "ProviderModelSlice"; 68 private final SubscriptionManager mSubscriptionManager; 69 private TelephonyManager mTelephonyManager; 70 protected final Context mContext; 71 private CustomSliceable mSliceable; 72 ProviderModelSliceHelper(Context context, CustomSliceable sliceable)73 public ProviderModelSliceHelper(Context context, CustomSliceable sliceable) { 74 mContext = context; 75 mSliceable = sliceable; 76 mSubscriptionManager = context.getSystemService(SubscriptionManager.class); 77 mTelephonyManager = context.getSystemService(TelephonyManager.class); 78 } 79 80 /** 81 * @return whether there is the carrier item in the slice. 82 */ hasCarrier()83 public boolean hasCarrier() { 84 if (isAirplaneModeEnabled() 85 || mSubscriptionManager == null || mTelephonyManager == null 86 || mSubscriptionManager.getActiveSubscriptionIdList().length <= 0) { 87 return false; 88 } 89 return true; 90 } 91 92 /** 93 * @return whether the MobileData's is enabled. 94 */ isMobileDataEnabled()95 public boolean isMobileDataEnabled() { 96 return mTelephonyManager.isDataEnabled(); 97 } 98 99 /** 100 * To check the carrier data status. 101 * 102 * @return whether the carrier data is active. 103 */ isDataSimActive()104 public boolean isDataSimActive() { 105 return MobileNetworkUtils.activeNetworkIsCellular(mContext); 106 } 107 108 /** 109 * @return whether the ServiceState's data state is in-service. 110 */ isDataStateInService()111 public boolean isDataStateInService() { 112 final ServiceState serviceState = mTelephonyManager.getServiceState(); 113 NetworkRegistrationInfo regInfo = 114 (serviceState == null) ? null : serviceState.getNetworkRegistrationInfo( 115 NetworkRegistrationInfo.DOMAIN_PS, 116 AccessNetworkConstants.TRANSPORT_TYPE_WWAN); 117 return (regInfo == null) ? false : regInfo.isRegistered(); 118 } 119 120 /** 121 * @return whether the ServiceState's voice state is in-service. 122 */ isVoiceStateInService()123 public boolean isVoiceStateInService() { 124 final ServiceState serviceState = mTelephonyManager.getServiceState(); 125 return serviceState != null 126 && serviceState.getState() == serviceState.STATE_IN_SERVICE; 127 } 128 129 /** 130 * To get the signal bar icon with level. 131 * 132 * @return The Drawable which is a signal bar icon with level. 133 */ getDrawableWithSignalStrength()134 public Drawable getDrawableWithSignalStrength() { 135 final SignalStrength strength = mTelephonyManager.getSignalStrength(); 136 int level = (strength == null) ? 0 : strength.getLevel(); 137 int numLevels = SignalStrength.NUM_SIGNAL_STRENGTH_BINS; 138 if (mSubscriptionManager != null && shouldInflateSignalStrength( 139 mSubscriptionManager.getDefaultDataSubscriptionId())) { 140 level += 1; 141 numLevels += 1; 142 } 143 return MobileNetworkUtils.getSignalStrengthIcon(mContext, level, numLevels, 144 NO_CELL_DATA_TYPE_ICON, false, false); 145 } 146 147 /** 148 * To update the telephony with subid. 149 */ updateTelephony()150 public void updateTelephony() { 151 if (mSubscriptionManager == null || mSubscriptionManager.getDefaultDataSubscriptionId() 152 == mSubscriptionManager.INVALID_SUBSCRIPTION_ID) { 153 return; 154 } 155 mTelephonyManager = mTelephonyManager.createForSubscriptionId( 156 mSubscriptionManager.getDefaultDataSubscriptionId()); 157 } 158 createListBuilder(Uri uri)159 protected ListBuilder createListBuilder(Uri uri) { 160 final ListBuilder builder = new ListBuilder(mContext, uri, ListBuilder.INFINITY) 161 .setAccentColor(-1) 162 .setKeywords(getKeywords()); 163 return builder; 164 } 165 166 @Nullable getConnectedWifiItem(List<WifiSliceItem> wifiList)167 protected WifiSliceItem getConnectedWifiItem(List<WifiSliceItem> wifiList) { 168 if (wifiList == null) { 169 return null; 170 } 171 Optional<WifiSliceItem> item = wifiList.stream() 172 .filter(x -> x.getConnectedState() == WifiEntry.CONNECTED_STATE_CONNECTED) 173 .findFirst(); 174 return item.isPresent() ? item.get() : null; 175 } 176 createCarrierRow(String networkTypeDescription)177 protected ListBuilder.RowBuilder createCarrierRow(String networkTypeDescription) { 178 final String title = getMobileTitle(); 179 final CharSequence summary = getMobileSummary(networkTypeDescription); 180 Drawable drawable = mContext.getDrawable( 181 R.drawable.ic_signal_strength_zero_bar_no_internet); 182 try { 183 drawable = getMobileDrawable(drawable); 184 } catch (Throwable e) { 185 e.printStackTrace(); 186 } 187 final IconCompat levelIcon = Utils.createIconWithDrawable(drawable); 188 final PendingIntent rowIntent = mSliceable.getBroadcastIntent(mContext); 189 final SliceAction primaryAction = SliceAction.create(rowIntent, 190 levelIcon, ListBuilder.ICON_IMAGE, title); 191 final SliceAction toggleAction = SliceAction.createToggle(rowIntent, 192 "mobile_toggle" /* actionTitle */, isMobileDataEnabled()); 193 final ListBuilder.RowBuilder rowBuilder = new ListBuilder.RowBuilder() 194 .setTitle(title) 195 .setTitleItem(levelIcon, ListBuilder.ICON_IMAGE) 196 .addEndItem(toggleAction) 197 .setPrimaryAction(primaryAction) 198 .setSubtitle(summary); 199 return rowBuilder; 200 } 201 getPrimarySliceAction(String intentAction)202 protected SliceAction getPrimarySliceAction(String intentAction) { 203 return SliceAction.createDeeplink( 204 getPrimaryAction(intentAction), 205 Utils.createIconWithDrawable(new ColorDrawable(Color.TRANSPARENT)), 206 ListBuilder.ICON_IMAGE, mContext.getText(R.string.summary_placeholder)); 207 } 208 isAirplaneModeEnabled()209 protected boolean isAirplaneModeEnabled() { 210 return WirelessUtils.isAirplaneModeOn(mContext); 211 } 212 getSubscriptionManager()213 protected SubscriptionManager getSubscriptionManager() { 214 return mSubscriptionManager; 215 } 216 log(String s)217 private static void log(String s) { 218 Log.d(TAG, s); 219 } 220 getPrimaryAction(String intentAction)221 private PendingIntent getPrimaryAction(String intentAction) { 222 final Intent intent = new Intent(intentAction) 223 .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 224 return PendingIntent.getActivity(mContext, 0 /* requestCode */, 225 intent, PendingIntent.FLAG_IMMUTABLE /* flags */); 226 } 227 shouldInflateSignalStrength(int subId)228 private boolean shouldInflateSignalStrength(int subId) { 229 return SignalStrengthUtil.shouldInflateSignalStrength(mContext, subId); 230 } 231 232 @VisibleForTesting getMobileDrawable(Drawable drawable)233 Drawable getMobileDrawable(Drawable drawable) throws Throwable { 234 // set color and drawable 235 if (mTelephonyManager == null) { 236 log("mTelephonyManager == null"); 237 return drawable; 238 } 239 if (isDataStateInService() || isVoiceStateInService()) { 240 Semaphore lock = new Semaphore(0); 241 AtomicReference<Drawable> shared = new AtomicReference<>(); 242 ThreadUtils.postOnMainThread(() -> { 243 shared.set(getDrawableWithSignalStrength()); 244 lock.release(); 245 }); 246 lock.acquire(); 247 drawable = shared.get(); 248 } 249 250 drawable.setTint( 251 Utils.getColorAttrDefaultColor(mContext, android.R.attr.colorControlNormal)); 252 if (isDataSimActive()) { 253 drawable.setTint(Utils.getColorAccentDefaultColor(mContext)); 254 } 255 return drawable; 256 } 257 getMobileSummary(String networkTypeDescription)258 private CharSequence getMobileSummary(String networkTypeDescription) { 259 if (!isMobileDataEnabled()) { 260 return mContext.getString(R.string.mobile_data_off_summary); 261 } 262 if (!isDataStateInService()) { 263 return mContext.getString(R.string.mobile_data_no_connection); 264 } 265 String summary = networkTypeDescription; 266 if (isDataSimActive()) { 267 summary = mContext.getString(R.string.preference_summary_default_combination, 268 mContext.getString(R.string.mobile_data_connection_active), 269 networkTypeDescription); 270 } 271 return maybeToHtml(summary); 272 } 273 getMobileTitle()274 protected String getMobileTitle() { 275 String title = mContext.getText(R.string.mobile_data_settings_title).toString(); 276 if (mSubscriptionManager == null) { 277 return title; 278 } 279 final SubscriptionInfo defaultSubscription = mSubscriptionManager.getActiveSubscriptionInfo( 280 mSubscriptionManager.getDefaultDataSubscriptionId()); 281 if (defaultSubscription != null) { 282 title = SubscriptionUtil.getUniqueSubscriptionDisplayName( 283 defaultSubscription, mContext).toString(); 284 } 285 return title; 286 } 287 getKeywords()288 private Set<String> getKeywords() { 289 final String keywords = mContext.getString(R.string.keywords_internet); 290 return Arrays.stream(TextUtils.split(keywords, ",")) 291 .map(String::trim) 292 .collect(Collectors.toSet()); 293 } 294 } 295