Fix problem of multiple stacked copies of "Select SIM" dialog
The SimDialogActivity is used to ask the user questions about which SIM card to use for various services like calls, SMS, and data. In some cases of SIM changes (eg when a SIM is added or removed), the telephony stack sends a broadcast that SimSelectNotification listens for so it can pop up a general "SIM cards changed" notification, and we additionally want to bring up an interruptive dialog to ask the user a specific question. This might happen for instance when we want to ask the user's permission to turn on data on a SIM. Recent DSDS changes in the telephony stack have meant that we accidentally create several stacked copies of this dialog, because they send several broadcast updates as information about SIMs asynchronously changes. For instance, we might initially detect a SIM with a generic name of "CARD 1", and shortly after discover the actual carrier name. So what we really want is to put up the dialog, and update it as information changes. This CL makes SimDialogActivity use launchMode="singleTop" so that additional copies of the activity won't be launched. Then it internally enforces only showing one dialog per type of request (calls, SMS, data, or preferred sim). If we get a request for a dialog that already exists, we just update it instead of creating a new one for that type. So there can still be a stack of more than one dialog, but each one will be asking a different question. This also refactors the monolithic, somewhat confusing code for showing the various types of dialogs into a more clearly separated class hierarchy, and switches to using DialogFragment for the dialog. Fixes: 126596081 Test: manual (start with device in DSDS mode with 2 subs, remove SIM card and re-insert it) Change-Id: I0dbc41dc3b15015389823a24df10bbff08ec6615
This commit is contained in:
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
* 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.sim;
|
||||
|
||||
import static com.android.settings.sim.SimDialogActivity.PREFERRED_PICK;
|
||||
import static com.android.settings.sim.SimDialogActivity.PREFERRED_SIM;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.Mockito.doNothing;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
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 androidx.appcompat.app.AlertDialog;
|
||||
|
||||
import com.android.settings.testutils.shadow.ShadowAlertDialogCompat;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.annotation.Config;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@Config(shadows = ShadowAlertDialogCompat.class)
|
||||
public class PreferredSimDialogFragmentTest extends
|
||||
SimDialogFragmentTestBase<PreferredSimDialogFragment> {
|
||||
|
||||
@Override
|
||||
public void setUp() {
|
||||
super.setUp();
|
||||
setDialogType(PREFERRED_PICK);
|
||||
mFragment = spy(PreferredSimDialogFragment.newInstance());
|
||||
doReturn(mSubscriptionManager).when(mFragment).getSubscriptionManager();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onCreateDialog_noSims_dismissed() {
|
||||
when(mSubscriptionManager.getActiveSubscriptionInfoForSimSlotIndex(anyInt()))
|
||||
.thenReturn(null);
|
||||
mIntent.putExtra(PREFERRED_SIM, 0);
|
||||
startDialog();
|
||||
verify(mFragment).dismiss();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onCreateDialog_oneSimWrongSlotArgument_dismissed() {
|
||||
when(mSubscriptionManager.getActiveSubscriptionInfoForSimSlotIndex(1)).thenReturn(null);
|
||||
mIntent.putExtra(PREFERRED_SIM, 1);
|
||||
startDialog();
|
||||
verify(mFragment).dismiss();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onCreateDialog_twoSimsSelectFirst_correctMessage() {
|
||||
mIntent.putExtra(PREFERRED_SIM, 0);
|
||||
|
||||
final AlertDialog alertDialog = startDialog();
|
||||
final ShadowAlertDialogCompat shadowDialog = ShadowAlertDialogCompat.shadowOf(alertDialog);
|
||||
final String message = (String) shadowDialog.getMessage();
|
||||
assertThat(message).contains(SIM1_NAME);
|
||||
assertThat(message).doesNotContain(SIM2_NAME);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onCreateDialog_twoSimsSelectSecond_correctMessage() {
|
||||
mIntent.putExtra(PREFERRED_SIM, 1);
|
||||
|
||||
final AlertDialog alertDialog = startDialog();
|
||||
final ShadowAlertDialogCompat shadowDialog = ShadowAlertDialogCompat.shadowOf(alertDialog);
|
||||
final String message = (String) shadowDialog.getMessage();
|
||||
assertThat(message).contains(SIM2_NAME);
|
||||
assertThat(message).doesNotContain(SIM1_NAME);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onClick_yesClicked_callsOnSubscriptionSelected() {
|
||||
mIntent.putExtra(PREFERRED_SIM, 0);
|
||||
|
||||
final AlertDialog alertDialog = startDialog();
|
||||
|
||||
final SimDialogActivity activity = (SimDialogActivity) spy(mFragment.getActivity());
|
||||
doReturn(activity).when(mFragment).getActivity();
|
||||
doNothing().when(activity).onSubscriptionSelected(anyInt(), anyInt());
|
||||
|
||||
mFragment.onClick(alertDialog, DialogInterface.BUTTON_POSITIVE);
|
||||
verify(activity).onSubscriptionSelected(PREFERRED_PICK, SIM1_ID);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onClick_noClicked_doesNotCallOnSubscriptionSelected() {
|
||||
mIntent.putExtra(PREFERRED_SIM, 0);
|
||||
|
||||
final AlertDialog alertDialog = startDialog();
|
||||
|
||||
final SimDialogActivity activity = (SimDialogActivity) spy(mFragment.getActivity());
|
||||
doReturn(activity).when(mFragment).getActivity();
|
||||
doNothing().when(activity).onSubscriptionSelected(anyInt(), anyInt());
|
||||
|
||||
mFragment.onClick(alertDialog, DialogInterface.BUTTON_NEGATIVE);
|
||||
verify(activity, never()).onSubscriptionSelected(anyInt(), anyInt());
|
||||
}
|
||||
}
|
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* 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.sim;
|
||||
|
||||
import static com.android.settings.sim.SimDialogActivity.DIALOG_TYPE_KEY;
|
||||
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.telephony.SubscriptionInfo;
|
||||
import android.telephony.SubscriptionManager;
|
||||
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
|
||||
import com.android.settings.testutils.shadow.ShadowAlertDialogCompat;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.shadows.androidx.fragment.FragmentController;
|
||||
|
||||
public abstract class SimDialogFragmentTestBase<T extends SimDialogFragment> {
|
||||
protected static final int SIM1_ID = 111;
|
||||
protected static final int SIM2_ID = 222;
|
||||
protected static final String SIM1_NAME = "sim111";
|
||||
protected static final String SIM2_NAME = "sim222";
|
||||
|
||||
@Mock
|
||||
protected SubscriptionManager mSubscriptionManager;
|
||||
@Mock
|
||||
protected SubscriptionInfo mSim1;
|
||||
@Mock
|
||||
protected SubscriptionInfo mSim2;
|
||||
|
||||
protected T mFragment;
|
||||
protected Intent mIntent;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mIntent = new Intent();
|
||||
|
||||
when(mSubscriptionManager.getActiveSubscriptionInfoForSimSlotIndex(0)).thenReturn(mSim1);
|
||||
when(mSubscriptionManager.getActiveSubscriptionInfoForSimSlotIndex(1)).thenReturn(mSim2);
|
||||
|
||||
when(mSim1.getSubscriptionId()).thenReturn(SIM1_ID);
|
||||
when(mSim1.getDisplayName()).thenReturn(SIM1_NAME);
|
||||
when(mSim2.getSubscriptionId()).thenReturn(SIM2_ID);
|
||||
when(mSim2.getDisplayName()).thenReturn(SIM2_NAME);
|
||||
}
|
||||
|
||||
protected void setDialogType(int dialogType) {
|
||||
mIntent.putExtra(DIALOG_TYPE_KEY, dialogType);
|
||||
}
|
||||
|
||||
protected AlertDialog startDialog() {
|
||||
final FragmentController controller = FragmentController.of(mFragment,
|
||||
SimDialogActivity.class, mIntent);
|
||||
controller.create(0 /* containerViewId */, null /* bundle */).start().visible();
|
||||
return ShadowAlertDialogCompat.getLatestAlertDialog();
|
||||
}
|
||||
}
|
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* 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.sim;
|
||||
|
||||
import static com.android.settings.sim.SimDialogActivity.DATA_PICK;
|
||||
import static com.android.settings.sim.SimDialogActivity.SMS_PICK;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.Mockito.doNothing;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import android.telephony.SubscriptionManager;
|
||||
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.testutils.shadow.ShadowAlertDialogCompat;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.annotation.Config;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@Config(shadows = ShadowAlertDialogCompat.class)
|
||||
public class SimListDialogFragmentTest extends SimDialogFragmentTestBase<SimListDialogFragment> {
|
||||
|
||||
@Test
|
||||
public void onCreateDialog_noSubscriptions_dismissed() {
|
||||
final int dialogType = DATA_PICK;
|
||||
setDialogType(dialogType);
|
||||
mFragment = spy(SimListDialogFragment.newInstance(dialogType, R.string.select_sim_for_data,
|
||||
false /* includeAskEveryTime */));
|
||||
doReturn(null).when(mFragment).getCurrentSubscriptions();
|
||||
startDialog();
|
||||
verify(mFragment).dismiss();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onCreateDialog_twoSubscriptionsNoAskEveryTime_twoSubsForDisplay() {
|
||||
final int dialogType = DATA_PICK;
|
||||
setDialogType(dialogType);
|
||||
mFragment = spy(SimListDialogFragment.newInstance(dialogType, R.string.select_sim_for_data,
|
||||
false /* includeAskEveryTime */));
|
||||
doReturn(Arrays.asList(mSim1, mSim2)).when(mFragment).getCurrentSubscriptions();
|
||||
// Avoid problems robolectric has with our real adapter.
|
||||
doNothing().when(mFragment).setAdapter(any());
|
||||
final AlertDialog alertDialog = startDialog();
|
||||
assertThat(mFragment.mSubscriptions).hasSize(2);
|
||||
|
||||
final SimDialogActivity activity = (SimDialogActivity) spy(mFragment.getActivity());
|
||||
doReturn(activity).when(mFragment).getActivity();
|
||||
doNothing().when(activity).onSubscriptionSelected(anyInt(), anyInt());
|
||||
|
||||
mFragment.onClick(alertDialog, 1);
|
||||
verify(activity).onSubscriptionSelected(dialogType, SIM2_ID);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onCreateDialog_twoSubscriptionsAskEveryTime_threeSubsForDisplay() {
|
||||
final int dialogType = SMS_PICK;
|
||||
setDialogType(dialogType);
|
||||
mFragment = spy(SimListDialogFragment.newInstance(dialogType, R.string.select_sim_for_sms,
|
||||
true /* includeAskEveryTime */));
|
||||
doReturn(Arrays.asList(mSim1, mSim2)).when(mFragment).getCurrentSubscriptions();
|
||||
// Avoid problems robolectric has with our real adapter.
|
||||
doNothing().when(mFragment).setAdapter(any());
|
||||
final AlertDialog alertDialog = startDialog();
|
||||
assertThat(mFragment.mSubscriptions).hasSize(3);
|
||||
assertThat(mFragment.mSubscriptions.get(0)).isNull();
|
||||
|
||||
final SimDialogActivity activity = (SimDialogActivity) spy(mFragment.getActivity());
|
||||
doReturn(activity).when(mFragment).getActivity();
|
||||
doNothing().when(activity).onSubscriptionSelected(anyInt(), anyInt());
|
||||
|
||||
mFragment.onClick(alertDialog, 0);
|
||||
verify(activity).onSubscriptionSelected(dialogType,
|
||||
SubscriptionManager.INVALID_SUBSCRIPTION_ID);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user