diff --git a/res/layout/dialog_mobile_network_rename.xml b/res/layout/dialog_mobile_network_rename.xml new file mode 100644 index 00000000000..d67f0dc6824 --- /dev/null +++ b/res/layout/dialog_mobile_network_rename.xml @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + diff --git a/res/values/ids.xml b/res/values/ids.xml index 66af163e2b8..ba14e855286 100644 --- a/res/values/ids.xml +++ b/res/values/ids.xml @@ -31,4 +31,7 @@ + + + diff --git a/res/values/strings.xml b/res/values/strings.xml index 19f6d9e8477..7877f30231f 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -10446,6 +10446,14 @@ Inactive eSIM + + SIM name + + Rename Preferred network type diff --git a/src/com/android/settings/network/telephony/MobileNetworkActivity.java b/src/com/android/settings/network/telephony/MobileNetworkActivity.java index a5f011c8120..821b1e1e8d8 100644 --- a/src/com/android/settings/network/telephony/MobileNetworkActivity.java +++ b/src/com/android/settings/network/telephony/MobileNetworkActivity.java @@ -97,13 +97,6 @@ public class MobileNetworkActivity extends SettingsBaseActivity { actionBar.setDisplayHomeAsUpEnabled(true); } - // 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()); - } updateSubscriptions(savedInstanceState); } @@ -136,6 +129,14 @@ public class MobileNetworkActivity extends SettingsBaseActivity { @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 (!FeatureFlagPersistent.isEnabled(this, FeatureFlags.NETWORK_INTERNET_V2)) { diff --git a/src/com/android/settings/network/telephony/MobileNetworkSettings.java b/src/com/android/settings/network/telephony/MobileNetworkSettings.java index 52015868cf8..a4156f565dd 100644 --- a/src/com/android/settings/network/telephony/MobileNetworkSettings.java +++ b/src/com/android/settings/network/telephony/MobileNetworkSettings.java @@ -28,9 +28,9 @@ import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; import android.text.TextUtils; import android.util.Log; - -import androidx.annotation.VisibleForTesting; -import androidx.preference.Preference; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; import com.android.internal.telephony.TelephonyIntents; import com.android.settings.R; @@ -52,6 +52,9 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import androidx.annotation.VisibleForTesting; +import androidx.preference.Preference; + @SearchIndexable(forTarget = SearchIndexable.ALL & ~SearchIndexable.ARC) public class MobileNetworkSettings extends RestrictedDashboardFragment { @@ -219,6 +222,31 @@ public class MobileNetworkSettings extends RestrictedDashboardFragment { } } + @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + if (FeatureFlagPersistent.isEnabled(getContext(), FeatureFlags.NETWORK_INTERNET_V2) && + mSubId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) { + final MenuItem item = menu.add(Menu.NONE, R.id.edit_sim_name, Menu.NONE, + R.string.mobile_network_sim_name); + item.setIcon(com.android.internal.R.drawable.ic_mode_edit); + item.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS); + } + super.onCreateOptionsMenu(menu, inflater); + } + + @Override + public boolean onOptionsItemSelected(MenuItem menuItem) { + if (FeatureFlagPersistent.isEnabled(getContext(), FeatureFlags.NETWORK_INTERNET_V2) && + mSubId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) { + if (menuItem.getItemId() == R.id.edit_sim_name) { + RenameMobileNetworkDialogFragment.newInstance(mSubId).show( + getFragmentManager(), RenameMobileNetworkDialogFragment.TAG); + return true; + } + } + return super.onOptionsItemSelected(menuItem); + } + public static final Indexable.SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = new BaseSearchIndexProvider() { @Override diff --git a/src/com/android/settings/network/telephony/RenameMobileNetworkDialogFragment.java b/src/com/android/settings/network/telephony/RenameMobileNetworkDialogFragment.java new file mode 100644 index 00000000000..488f9300e2e --- /dev/null +++ b/src/com/android/settings/network/telephony/RenameMobileNetworkDialogFragment.java @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2019 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.Dialog; +import android.app.settings.SettingsEnums; +import android.content.Context; +import android.os.Bundle; +import android.telephony.ServiceState; +import android.telephony.SubscriptionInfo; +import android.telephony.SubscriptionManager; +import android.telephony.TelephonyManager; +import android.text.BidiFormatter; +import android.text.TextDirectionHeuristics; +import android.text.TextUtils; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.EditText; +import android.widget.TextView; + +import com.android.settings.R; +import com.android.settings.core.instrumentation.InstrumentedDialogFragment; +import com.android.settingslib.DeviceInfoUtils; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.VisibleForTesting; +import androidx.appcompat.app.AlertDialog; + +/** A dialog allowing the display name of a mobile network subscription to be changed */ +public class RenameMobileNetworkDialogFragment extends InstrumentedDialogFragment { + public static final String TAG ="RenameMobileNetwork"; + + private static final String KEY_SUBSCRIPTION_ID = "subscription_id"; + + private TelephonyManager mTelephonyManager; + private SubscriptionManager mSubscriptionManager; + private int mSubId; + private EditText mNameView; + + public static RenameMobileNetworkDialogFragment newInstance(int subscriptionId) { + final Bundle args = new Bundle(1); + args.putInt(KEY_SUBSCRIPTION_ID, subscriptionId); + final RenameMobileNetworkDialogFragment fragment = new RenameMobileNetworkDialogFragment(); + fragment.setArguments(args); + return fragment; + } + + @VisibleForTesting + protected TelephonyManager getTelephonyManager(Context context) { + return context.getSystemService(TelephonyManager.class); + } + + @VisibleForTesting + protected SubscriptionManager getSubscriptionManager(Context context) { + return context.getSystemService(SubscriptionManager.class); + } + + @VisibleForTesting + protected EditText getNameView() { + return mNameView; + } + + @Override + public void onAttach(Context context) { + super.onAttach(context); + mTelephonyManager = getTelephonyManager(context); + mSubscriptionManager = getSubscriptionManager(context); + mSubId = getArguments().getInt(KEY_SUBSCRIPTION_ID); + } + + @NonNull + @Override + public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) { + final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); + final LayoutInflater layoutInflater = builder.getContext().getSystemService( + LayoutInflater.class); + final View view = layoutInflater.inflate(R.layout.dialog_mobile_network_rename, null); + populateView(view); + builder.setTitle(R.string.mobile_network_sim_name) + .setView(view) + .setPositiveButton(R.string.mobile_network_sim_name_rename, (dialog, which) -> { + SubscriptionInfo currentInfo = mSubscriptionManager.getActiveSubscriptionInfo( + mSubId); + String newName = mNameView.getText().toString(); + if (currentInfo != null && !currentInfo.getDisplayName().equals(newName)) { + mSubscriptionManager.setDisplayName(newName, mSubId); + } + }) + .setNegativeButton(android.R.string.cancel, null); + return builder.create(); + } + + @VisibleForTesting + protected void populateView(View view) { + mNameView = (EditText) view.findViewById(R.id.edittext); + final SubscriptionInfo info = mSubscriptionManager.getActiveSubscriptionInfo(mSubId); + if (info == null) { + Log.w(TAG, "got null SubscriptionInfo for mSubId:" + mSubId); + return; + } + final CharSequence displayName = info.getDisplayName(); + mNameView.setText(displayName); + if (!TextUtils.isEmpty(displayName)) { + mNameView.setSelection(displayName.length()); + } + + final TextView operatorName = view.findViewById(R.id.operator_name_value); + final ServiceState serviceState = mTelephonyManager.getServiceStateForSubscriber(mSubId); + operatorName.setText(serviceState.getOperatorAlphaLong()); + + final TextView phoneNumber = view.findViewById(R.id.number_value); + final String formattedNumber = DeviceInfoUtils.getFormattedPhoneNumber(getContext(), info); + phoneNumber.setText(BidiFormatter.getInstance().unicodeWrap(formattedNumber, + TextDirectionHeuristics.LTR)); + } + + @Override + public int getMetricsCategory() { + return SettingsEnums.MOBILE_NETWORK_RENAME_DIALOG; + } +} diff --git a/tests/robotests/src/com/android/settings/network/telephony/RenameMobileNetworkDialogFragmentTest.java b/tests/robotests/src/com/android/settings/network/telephony/RenameMobileNetworkDialogFragmentTest.java new file mode 100644 index 00000000000..df523029414 --- /dev/null +++ b/tests/robotests/src/com/android/settings/network/telephony/RenameMobileNetworkDialogFragmentTest.java @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2019 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 static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.DialogInterface; +import android.telephony.ServiceState; +import android.telephony.SubscriptionInfo; +import android.telephony.SubscriptionManager; +import android.telephony.TelephonyManager; +import android.widget.Button; +import android.widget.EditText; + +import com.android.settings.testutils.shadow.ShadowAlertDialogCompat; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.Robolectric; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; + +import androidx.appcompat.app.AlertDialog; +import androidx.fragment.app.FragmentActivity; + +@RunWith(RobolectricTestRunner.class) +@Config(shadows = ShadowAlertDialogCompat.class) +public class RenameMobileNetworkDialogFragmentTest { + @Mock + private TelephonyManager mTelephonyMgr; + @Mock + private SubscriptionManager mSubscriptionMgr; + @Mock + private SubscriptionInfo mSubscriptionInfo; + + private FragmentActivity mActivity; + private RenameMobileNetworkDialogFragment mFragment; + private int mSubscriptionId = 1234; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mActivity = spy(Robolectric.buildActivity(FragmentActivity.class).setup().get()); + + when(mSubscriptionInfo.getSubscriptionId()).thenReturn(mSubscriptionId); + when(mSubscriptionInfo.getDisplayName()).thenReturn("test"); + + mFragment = spy(RenameMobileNetworkDialogFragment.newInstance(mSubscriptionId)); + doReturn(mTelephonyMgr).when(mFragment).getTelephonyManager(any()); + doReturn(mSubscriptionMgr).when(mFragment).getSubscriptionManager(any()); + + final ServiceState serviceState = mock(ServiceState.class); + when(serviceState.getOperatorAlphaLong()).thenReturn("fake carrier name"); + when(mTelephonyMgr.getServiceStateForSubscriber(mSubscriptionId)).thenReturn(serviceState); + } + + @Test + public void dialog_subscriptionMissing_noCrash() { + final AlertDialog dialog = startDialog(); + final Button negativeButton = dialog.getButton(DialogInterface.BUTTON_NEGATIVE); + assertThat(negativeButton).isNotNull(); + negativeButton.performClick(); + } + + @Test + public void dialog_cancelButtonClicked_setDisplayNameNotCalled() { + when(mSubscriptionMgr.getActiveSubscriptionInfo(mSubscriptionId)).thenReturn( + mSubscriptionInfo); + final AlertDialog dialog = startDialog(); + final EditText nameView = mFragment.getNameView(); + nameView.setText("test2"); + + final Button negativeButton = dialog.getButton(DialogInterface.BUTTON_NEGATIVE); + negativeButton.performClick(); + + verify(mSubscriptionMgr, never()).setDisplayName(anyString(), anyInt()); + } + + @Test + public void dialog_renameButtonClicked_setDisplayNameCalled() { + when(mSubscriptionMgr.getActiveSubscriptionInfo(mSubscriptionId)).thenReturn( + mSubscriptionInfo); + + final AlertDialog dialog = startDialog(); + final EditText nameView = mFragment.getNameView(); + nameView.setText("test2"); + + final Button positiveButton = dialog.getButton(DialogInterface.BUTTON_POSITIVE); + positiveButton.performClick(); + + final ArgumentCaptor captor = ArgumentCaptor.forClass(String.class); + verify(mSubscriptionMgr).setDisplayName(captor.capture(), eq(mSubscriptionId)); + assertThat(captor.getValue()).isEqualTo("test2"); + } + + /** Helper method to start the dialog */ + private AlertDialog startDialog() { + mFragment.show(mActivity.getSupportFragmentManager(), null); + return ShadowAlertDialogCompat.getLatestAlertDialog(); + } +}