/* * Copyright (C) 2020 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.settings.network; import static com.android.settings.network.telephony.MobileNetworkUtils.NO_CELL_DATA_TYPE_ICON; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.graphics.Color; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.net.Uri; import android.telephony.AccessNetworkConstants; import android.telephony.NetworkRegistrationInfo; import android.telephony.ServiceState; import android.telephony.SignalStrength; import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; import android.text.Html; import android.text.TextUtils; import android.util.Log; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import androidx.core.graphics.drawable.IconCompat; import androidx.slice.builders.ListBuilder; import androidx.slice.builders.SliceAction; import com.android.settings.R; import com.android.settings.Utils; import com.android.settings.network.telephony.MobileNetworkUtils; import com.android.settings.slices.CustomSliceable; import com.android.settings.wifi.slice.WifiSliceItem; import com.android.settingslib.WirelessUtils; import com.android.settingslib.net.SignalStrengthUtil; import com.android.settingslib.utils.ThreadUtils; import com.android.wifitrackerlib.WifiEntry; import java.util.Arrays; import java.util.List; import java.util.Optional; import java.util.Set; import java.util.concurrent.Semaphore; import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Collectors; /** * The helper is for slice of carrier and non-Carrier, used by ProviderModelSlice. */ public class ProviderModelSliceHelper { private static final String TAG = "ProviderModelSlice"; private final SubscriptionManager mSubscriptionManager; private TelephonyManager mTelephonyManager; protected final Context mContext; private CustomSliceable mSliceable; public ProviderModelSliceHelper(Context context, CustomSliceable sliceable) { mContext = context; mSliceable = sliceable; mSubscriptionManager = context.getSystemService(SubscriptionManager.class); mTelephonyManager = context.getSystemService(TelephonyManager.class); } /** * @return whether there is the carrier item in the slice. */ public boolean hasCarrier() { if (isAirplaneModeEnabled() || mSubscriptionManager == null || mTelephonyManager == null || mSubscriptionManager.getActiveSubscriptionIdList().length <= 0) { return false; } return true; } /** * @return whether the MobileData's is enabled. */ public boolean isMobileDataEnabled() { return mTelephonyManager.isDataEnabled(); } /** * To check the carrier data status. * * @return whether the carrier data is active. */ public boolean isDataSimActive() { return MobileNetworkUtils.activeNetworkIsCellular(mContext); } /** * @return whether the ServiceState's data state is in-service. */ public boolean isDataStateInService() { final ServiceState serviceState = mTelephonyManager.getServiceState(); NetworkRegistrationInfo regInfo = (serviceState == null) ? null : serviceState.getNetworkRegistrationInfo( NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN); return (regInfo == null) ? false : regInfo.isRegistered(); } /** * @return whether the ServiceState's voice state is in-service. */ public boolean isVoiceStateInService() { final ServiceState serviceState = mTelephonyManager.getServiceState(); return serviceState != null && serviceState.getState() == serviceState.STATE_IN_SERVICE; } /** * To get the signal bar icon with level. * * @return The Drawable which is a signal bar icon with level. */ public Drawable getDrawableWithSignalStrength() { final SignalStrength strength = mTelephonyManager.getSignalStrength(); int level = (strength == null) ? 0 : strength.getLevel(); int numLevels = SignalStrength.NUM_SIGNAL_STRENGTH_BINS; if (mSubscriptionManager != null && shouldInflateSignalStrength( mSubscriptionManager.getDefaultDataSubscriptionId())) { level += 1; numLevels += 1; } return MobileNetworkUtils.getSignalStrengthIcon(mContext, level, numLevels, NO_CELL_DATA_TYPE_ICON, false); } /** * To update the telephony with subid. */ public void updateTelephony() { if (mSubscriptionManager == null || mSubscriptionManager.getDefaultDataSubscriptionId() == mSubscriptionManager.INVALID_SUBSCRIPTION_ID) { return; } mTelephonyManager = mTelephonyManager.createForSubscriptionId( mSubscriptionManager.getDefaultDataSubscriptionId()); } protected ListBuilder createListBuilder(Uri uri) { final ListBuilder builder = new ListBuilder(mContext, uri, ListBuilder.INFINITY) .setAccentColor(-1) .setKeywords(getKeywords()); return builder; } @Nullable protected WifiSliceItem getConnectedWifiItem(List wifiList) { if (wifiList == null) { return null; } Optional item = wifiList.stream() .filter(x -> x.getConnectedState() == WifiEntry.CONNECTED_STATE_CONNECTED) .findFirst(); return item.isPresent() ? item.get() : null; } protected ListBuilder.RowBuilder createCarrierRow(String networkTypeDescription) { final String title = getMobileTitle(); final String summary = getMobileSummary(networkTypeDescription); Drawable drawable = mContext.getDrawable( R.drawable.ic_signal_strength_zero_bar_no_internet); try { drawable = getMobileDrawable(drawable); } catch (Throwable e) { e.printStackTrace(); } final IconCompat levelIcon = Utils.createIconWithDrawable(drawable); final PendingIntent rowIntent = mSliceable.getBroadcastIntent(mContext); final SliceAction primaryAction = SliceAction.create(rowIntent, levelIcon, ListBuilder.ICON_IMAGE, title); final SliceAction toggleAction = SliceAction.createToggle(rowIntent, "mobile_toggle" /* actionTitle */, isMobileDataEnabled()); final ListBuilder.RowBuilder rowBuilder = new ListBuilder.RowBuilder() .setTitle(title) .setTitleItem(levelIcon, ListBuilder.ICON_IMAGE) .addEndItem(toggleAction) .setPrimaryAction(primaryAction) .setSubtitle(Html.fromHtml(summary, Html.FROM_HTML_MODE_LEGACY)); return rowBuilder; } protected SliceAction getPrimarySliceAction(String intentAction) { return SliceAction.createDeeplink( getPrimaryAction(intentAction), Utils.createIconWithDrawable(new ColorDrawable(Color.TRANSPARENT)), ListBuilder.ICON_IMAGE, mContext.getText(R.string.summary_placeholder)); } protected boolean isAirplaneModeEnabled() { return WirelessUtils.isAirplaneModeOn(mContext); } protected SubscriptionManager getSubscriptionManager() { return mSubscriptionManager; } private static void log(String s) { Log.d(TAG, s); } private PendingIntent getPrimaryAction(String intentAction) { final Intent intent = new Intent(intentAction) .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); return PendingIntent.getActivity(mContext, 0 /* requestCode */, intent, PendingIntent.FLAG_IMMUTABLE /* flags */); } private boolean shouldInflateSignalStrength(int subId) { return SignalStrengthUtil.shouldInflateSignalStrength(mContext, subId); } @VisibleForTesting Drawable getMobileDrawable(Drawable drawable) throws Throwable { // set color and drawable if (mTelephonyManager == null) { log("mTelephonyManager == null"); return drawable; } if (isDataStateInService() || isVoiceStateInService()) { Semaphore lock = new Semaphore(0); AtomicReference shared = new AtomicReference<>(); ThreadUtils.postOnMainThread(() -> { shared.set(getDrawableWithSignalStrength()); lock.release(); }); lock.acquire(); drawable = shared.get(); } drawable.setTint( Utils.getColorAttrDefaultColor(mContext, android.R.attr.colorControlNormal)); if (isDataSimActive()) { drawable.setTint(Utils.getColorAccentDefaultColor(mContext)); } return drawable; } private String getMobileSummary(String networkTypeDescription) { if (!isMobileDataEnabled()) { return mContext.getString(R.string.mobile_data_off_summary); } if (!isDataStateInService()) { return mContext.getString(R.string.mobile_data_no_connection); } String summary = networkTypeDescription; if (isDataSimActive()) { summary = mContext.getString(R.string.preference_summary_default_combination, mContext.getString(R.string.mobile_data_connection_active), networkTypeDescription); } return summary; } protected String getMobileTitle() { String title = mContext.getText(R.string.mobile_data_settings_title).toString(); if (mSubscriptionManager == null) { return title; } final SubscriptionInfo defaultSubscription = mSubscriptionManager.getActiveSubscriptionInfo( mSubscriptionManager.getDefaultDataSubscriptionId()); if (defaultSubscription != null) { title = SubscriptionUtil.getUniqueSubscriptionDisplayName( defaultSubscription, mContext).toString(); } return title; } private Set getKeywords() { final String keywords = mContext.getString(R.string.keywords_internet); return Arrays.stream(TextUtils.split(keywords, ",")) .map(String::trim) .collect(Collectors.toSet()); } }