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