| /* |
| * Copyright (C) 2018 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.telephony; |
| |
| import android.app.ActionBar; |
| import android.content.BroadcastReceiver; |
| import android.content.Context; |
| import android.content.Intent; |
| import android.content.IntentFilter; |
| import android.os.Bundle; |
| import android.provider.Settings; |
| import android.telephony.CarrierConfigManager; |
| import android.telephony.SubscriptionInfo; |
| import android.telephony.SubscriptionManager; |
| import android.telephony.ims.ImsRcsManager; |
| import android.text.TextUtils; |
| |
| import androidx.annotation.NonNull; |
| import androidx.annotation.VisibleForTesting; |
| import androidx.fragment.app.Fragment; |
| import androidx.fragment.app.FragmentManager; |
| import androidx.fragment.app.FragmentTransaction; |
| |
| import com.android.internal.telephony.TelephonyIntents; |
| import com.android.internal.util.CollectionUtils; |
| import com.android.settings.R; |
| import com.android.settings.core.SettingsBaseActivity; |
| import com.android.settings.network.SubscriptionUtil; |
| |
| import java.util.ArrayList; |
| import java.util.List; |
| import java.util.Objects; |
| |
| public class MobileNetworkActivity extends SettingsBaseActivity { |
| |
| private static final String TAG = "MobileNetworkActivity"; |
| @VisibleForTesting |
| static final String MOBILE_SETTINGS_TAG = "mobile_settings:"; |
| @VisibleForTesting |
| static final int SUB_ID_NULL = Integer.MIN_VALUE; |
| |
| @VisibleForTesting |
| SubscriptionManager mSubscriptionManager; |
| @VisibleForTesting |
| int mCurSubscriptionId; |
| @VisibleForTesting |
| List<SubscriptionInfo> mSubscriptionInfos = new ArrayList<>(); |
| private PhoneChangeReceiver mPhoneChangeReceiver; |
| |
| private final SubscriptionManager.OnSubscriptionsChangedListener |
| mOnSubscriptionsChangeListener |
| = new SubscriptionManager.OnSubscriptionsChangedListener() { |
| @Override |
| public void onSubscriptionsChanged() { |
| if (!Objects.equals(mSubscriptionInfos, |
| mSubscriptionManager.getActiveSubscriptionInfoList(true))) { |
| int oldSubIndex = mCurSubscriptionId; |
| updateSubscriptions(null); |
| // Remove the dialog if the subscription associated with this activity changes. |
| if (mCurSubscriptionId != oldSubIndex) { |
| removeContactDiscoveryDialog(oldSubIndex); |
| } |
| } |
| } |
| }; |
| |
| @Override |
| protected void onNewIntent(Intent intent) { |
| super.onNewIntent(intent); |
| validate(intent); |
| setIntent(intent); |
| |
| int updateSubscriptionIndex = SUB_ID_NULL; |
| if (intent != null) { |
| updateSubscriptionIndex = intent.getIntExtra(Settings.EXTRA_SUB_ID, SUB_ID_NULL); |
| } |
| int oldSubId = mCurSubscriptionId; |
| updateSubscriptions(null); |
| |
| // If the subscription has changed or the new intent doesnt contain the opt in action, |
| // remove the old discovery dialog. If the activity is being recreated, we will see |
| // onCreate -> onNewIntent, so the dialog will first be recreated for the old subscription |
| // and then removed. |
| if (updateSubscriptionIndex != oldSubId || !doesIntentContainOptInAction(intent)) { |
| removeContactDiscoveryDialog(oldSubId); |
| } |
| // evaluate showing the new discovery dialog if this intent contains an action to show the |
| // opt-in. |
| if (doesIntentContainOptInAction(intent)) { |
| maybeShowContactDiscoveryDialog(updateSubscriptionIndex); |
| } |
| } |
| |
| @Override |
| protected void onCreate(Bundle savedInstanceState) { |
| super.onCreate(savedInstanceState); |
| setContentView(R.layout.mobile_network_settings_container_v2); |
| setActionBar(findViewById(R.id.mobile_action_bar)); |
| mPhoneChangeReceiver = new PhoneChangeReceiver(this, new PhoneChangeReceiver.Client() { |
| @Override |
| public void onPhoneChange() { |
| // When the radio or carrier config changes (ex: CDMA->GSM), refresh the fragment. |
| switchFragment(new MobileNetworkSettings(), mCurSubscriptionId, |
| true /* forceUpdate */); |
| } |
| |
| @Override |
| public int getSubscriptionId() { |
| return mCurSubscriptionId; |
| } |
| }); |
| mSubscriptionManager = getSystemService(SubscriptionManager.class); |
| mSubscriptionInfos = mSubscriptionManager.getActiveSubscriptionInfoList(true); |
| final Intent startIntent = getIntent(); |
| validate(startIntent); |
| mCurSubscriptionId = savedInstanceState != null |
| ? savedInstanceState.getInt(Settings.EXTRA_SUB_ID, SUB_ID_NULL) |
| : SUB_ID_NULL; |
| |
| final ActionBar actionBar = getActionBar(); |
| if (actionBar != null) { |
| actionBar.setDisplayHomeAsUpEnabled(true); |
| } |
| updateSubscriptions(savedInstanceState); |
| maybeShowContactDiscoveryDialog(mCurSubscriptionId); |
| } |
| |
| @Override |
| protected void onStart() { |
| super.onStart(); |
| mPhoneChangeReceiver.register(); |
| mSubscriptionManager.addOnSubscriptionsChangedListener(mOnSubscriptionsChangeListener); |
| } |
| |
| @Override |
| protected void onStop() { |
| super.onStop(); |
| mPhoneChangeReceiver.unregister(); |
| mSubscriptionManager.removeOnSubscriptionsChangedListener(mOnSubscriptionsChangeListener); |
| } |
| |
| @Override |
| protected void onSaveInstanceState(@NonNull Bundle outState) { |
| super.onSaveInstanceState(outState); |
| saveInstanceState(outState); |
| } |
| |
| @VisibleForTesting |
| void saveInstanceState(@NonNull Bundle outState) { |
| outState.putInt(Settings.EXTRA_SUB_ID, mCurSubscriptionId); |
| } |
| |
| @VisibleForTesting |
| void updateSubscriptions(Bundle savedInstanceState) { |
| // Set the title to the name of the subscription. If we don't have subscription info, the |
| // title will just default to the label for this activity that's already specified in |
| // AndroidManifest.xml. |
| final SubscriptionInfo subscription = getSubscription(); |
| if (subscription != null) { |
| setTitle(subscription.getDisplayName()); |
| } |
| |
| mSubscriptionInfos = mSubscriptionManager.getActiveSubscriptionInfoList(true); |
| |
| if (savedInstanceState == null) { |
| switchFragment(new MobileNetworkSettings(), getSubscriptionId()); |
| } |
| } |
| |
| /** |
| * Get the current subscription to display. First check whether intent has {@link |
| * Settings#EXTRA_SUB_ID} and if so find the subscription with that id. If not, just return the |
| * first one in the mSubscriptionInfos list since it is already sorted by sim slot. |
| */ |
| @VisibleForTesting |
| SubscriptionInfo getSubscription() { |
| final Intent intent = getIntent(); |
| if (intent != null) { |
| final int subId = intent.getIntExtra(Settings.EXTRA_SUB_ID, SUB_ID_NULL); |
| SubscriptionInfo info = getSubscriptionInfo(subId); |
| if (info != null) return info; |
| } |
| if (CollectionUtils.isEmpty(mSubscriptionInfos)) { |
| return null; |
| } |
| return mSubscriptionInfos.get(0); |
| } |
| |
| /** |
| * @return the subscription associated with a given subscription ID or null if none can be |
| * found. |
| */ |
| SubscriptionInfo getSubscriptionInfo(int subId) { |
| if (subId == SUB_ID_NULL) { |
| return null; |
| } |
| |
| for (SubscriptionInfo subscription : SubscriptionUtil.getAvailableSubscriptions(this)) { |
| if (subscription.getSubscriptionId() == subId) { |
| return subscription; |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Get the current subId to display. |
| */ |
| @VisibleForTesting |
| int getSubscriptionId() { |
| final SubscriptionInfo subscription = getSubscription(); |
| if (subscription != null) { |
| return subscription.getSubscriptionId(); |
| } |
| return SubscriptionManager.INVALID_SUBSCRIPTION_ID; |
| } |
| |
| @VisibleForTesting |
| void switchFragment(Fragment fragment, int subscriptionId) { |
| switchFragment(fragment, subscriptionId, false /* forceUpdate */); |
| } |
| |
| @VisibleForTesting |
| void switchFragment(Fragment fragment, int subscriptionId, boolean forceUpdate) { |
| if (mCurSubscriptionId != SUB_ID_NULL && subscriptionId == mCurSubscriptionId |
| && !forceUpdate) { |
| return; |
| } |
| final FragmentManager fragmentManager = getSupportFragmentManager(); |
| final FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction(); |
| final Bundle bundle = new Bundle(); |
| bundle.putInt(Settings.EXTRA_SUB_ID, subscriptionId); |
| |
| fragment.setArguments(bundle); |
| fragmentTransaction.replace(R.id.main_content, fragment, |
| buildFragmentTag(subscriptionId)); |
| fragmentTransaction.commit(); |
| mCurSubscriptionId = subscriptionId; |
| } |
| |
| @VisibleForTesting |
| private String buildFragmentTag(int subscriptionId) { |
| return MOBILE_SETTINGS_TAG + subscriptionId; |
| } |
| |
| @VisibleForTesting |
| static class PhoneChangeReceiver extends BroadcastReceiver { |
| private Context mContext; |
| private Client mClient; |
| |
| interface Client { |
| void onPhoneChange(); |
| int getSubscriptionId(); |
| } |
| |
| public PhoneChangeReceiver(Context context, Client client) { |
| mContext = context; |
| mClient = client; |
| } |
| |
| public void register() { |
| final IntentFilter intentFilter = new IntentFilter(); |
| intentFilter.addAction(TelephonyIntents.ACTION_RADIO_TECHNOLOGY_CHANGED); |
| intentFilter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED); |
| mContext.registerReceiver(this, intentFilter); |
| } |
| |
| public void unregister() { |
| mContext.unregisterReceiver(this); |
| } |
| |
| @Override |
| public void onReceive(Context context, Intent intent) { |
| if (isInitialStickyBroadcast()) { |
| return; |
| } |
| if (intent.getAction().equals(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)) { |
| if (!intent.hasExtra(CarrierConfigManager.EXTRA_SUBSCRIPTION_INDEX) || |
| intent.getIntExtra(CarrierConfigManager.EXTRA_SUBSCRIPTION_INDEX, -1) |
| != mClient.getSubscriptionId()) { |
| return; |
| } |
| } |
| mClient.onPhoneChange(); |
| } |
| } |
| |
| private void removeContactDiscoveryDialog(int subId) { |
| ContactDiscoveryDialogFragment fragment = getContactDiscoveryFragment(subId); |
| if (fragment != null) { |
| fragment.dismiss(); |
| } |
| } |
| |
| private ContactDiscoveryDialogFragment getContactDiscoveryFragment(int subId) { |
| // In the case that we are rebuilding this activity after it has been destroyed and |
| // recreated, look up the dialog in the fragment manager. |
| return (ContactDiscoveryDialogFragment) getSupportFragmentManager() |
| .findFragmentByTag(ContactDiscoveryDialogFragment.getFragmentTag(subId)); |
| } |
| |
| private void maybeShowContactDiscoveryDialog(int subId) { |
| SubscriptionInfo info = getSubscriptionInfo(subId); |
| CharSequence carrierName = ""; |
| if (info != null) { |
| carrierName = info.getDisplayName(); |
| } |
| // If this activity was launched using ACTION_SHOW_CAPABILITY_DISCOVERY_OPT_IN, show the |
| // associated dialog only if the opt-in has not been granted yet. |
| boolean showOptInDialog = doesIntentContainOptInAction(getIntent()) |
| // has the carrier config enabled capability discovery? |
| && MobileNetworkUtils.isContactDiscoveryVisible(this, subId) |
| // has the user already enabled this configuration? |
| && !MobileNetworkUtils.isContactDiscoveryEnabled(this, subId); |
| ContactDiscoveryDialogFragment fragment = getContactDiscoveryFragment(subId); |
| if (showOptInDialog) { |
| if (fragment == null) { |
| fragment = ContactDiscoveryDialogFragment.newInstance(subId, carrierName); |
| } |
| // Only try to show the dialog if it has not already been added, otherwise we may |
| // accidentally add it multiple times, causing multiple dialogs. |
| if (!fragment.isAdded()) { |
| fragment.show(getSupportFragmentManager(), |
| ContactDiscoveryDialogFragment.getFragmentTag(subId)); |
| } |
| } |
| } |
| |
| private boolean doesIntentContainOptInAction(Intent intent) { |
| String intentAction = (intent != null ? intent.getAction() : null); |
| return TextUtils.equals(intentAction, |
| ImsRcsManager.ACTION_SHOW_CAPABILITY_DISCOVERY_OPT_IN); |
| } |
| |
| private void validate(Intent intent) { |
| // Do not allow ACTION_SHOW_CAPABILITY_DISCOVERY_OPT_IN without a subscription id specified, |
| // since we do not want the user to accidentally turn on capability polling for the wrong |
| // subscription. |
| if (doesIntentContainOptInAction(intent)) { |
| if (SubscriptionManager.INVALID_SUBSCRIPTION_ID == intent.getIntExtra( |
| Settings.EXTRA_SUB_ID, SubscriptionManager.INVALID_SUBSCRIPTION_ID)) { |
| throw new IllegalArgumentException("Intent with action " |
| + "SHOW_CAPABILITY_DISCOVERY_OPT_IN must also include the extra " |
| + "Settings#EXTRA_SUB_ID"); |
| } |
| } |
| } |
| } |