Add dialog for changing display name for mobile network
This CL adds a pencil/edit icon at the top of the mobile network details page. When clicked, it brings up a dialog that allows the user to set the display name used for that mobile network - this is most useful in multi-SIM situations. This dialog also displays the mobile network operator name and phone number which can help the user keep straight which one they are renaming. Bug: 122670283 Test: make RunSettingsRoboTests Change-Id: Ifd63cfb66ce743bd5ba8a06b52229f06ed56b7bd
This commit is contained in:
64
res/layout/dialog_mobile_network_rename.xml
Normal file
64
res/layout/dialog_mobile_network_rename.xml
Normal file
@@ -0,0 +1,64 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 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.
|
||||
-->
|
||||
<ScrollView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:padding="@dimen/sim_content_padding">
|
||||
|
||||
<EditText
|
||||
android:id="@+id/edittext"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="text"
|
||||
android:maxLength="50"
|
||||
android:singleLine="true">
|
||||
<requestFocus/>
|
||||
</EditText>
|
||||
|
||||
<TextView
|
||||
style="@style/device_info_dialog_label"
|
||||
android:id="@+id/operator_name_label"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/status_operator"/>
|
||||
<TextView
|
||||
style="@style/device_info_dialog_value"
|
||||
android:id="@+id/operator_name_value"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/device_info_not_available"/>
|
||||
|
||||
<TextView
|
||||
style="@style/device_info_dialog_label"
|
||||
android:id="@+id/number_label"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/status_number_sim_status"/>
|
||||
<TextView
|
||||
style="@style/device_info_dialog_value"
|
||||
android:id="@+id/number_value"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/device_info_not_available"/>
|
||||
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
@@ -31,4 +31,7 @@
|
||||
<item type="id" name="action_drag_move_top" />
|
||||
<item type="id" name="action_drag_move_bottom" />
|
||||
<item type="id" name="action_drag_remove" />
|
||||
|
||||
<!-- For a menu item allowing users to edit a SIM display name -->
|
||||
<item type="id" name="edit_sim_name" />
|
||||
</resources>
|
||||
|
@@ -10446,6 +10446,14 @@
|
||||
<!-- Summary for an item in the page listing multiple mobile service subscriptions, indicating
|
||||
that service is inactive and is tied to an eSIM profile [CHAR LIMIT=40] -->
|
||||
<string name="mobile_network_inactive_esim">Inactive eSIM</string>
|
||||
<!-- Title of a dialog that lets a user modify the display name used for a mobile network
|
||||
subscription in various places in the Settings app. The default name is typically just the
|
||||
carrier name, but especially in multi-SIM configurations users may want to use a different
|
||||
name. [CHAR LIMIT=40] -->
|
||||
<string name="mobile_network_sim_name">SIM name</string>
|
||||
<!-- Label on the confirmation button of a dialog that lets a user set the display name of a
|
||||
mobile network subscription [CHAR LIMIT=20] -->
|
||||
<string name="mobile_network_sim_name_rename">Rename</string>
|
||||
|
||||
<!-- Title for preferred network type [CHAR LIMIT=NONE] -->
|
||||
<string name="preferred_network_mode_title">Preferred network type</string>
|
||||
|
@@ -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)) {
|
||||
|
@@ -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
|
||||
|
@@ -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;
|
||||
}
|
||||
}
|
@@ -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<String> 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();
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user