Add subscriptions list to the multi-network header

The new UX for showing multiple active mobile plan subscriptions (SIMs,
eSIMs, etc.)  needs a header at the top of the Network & internet page
that shows wifi status and a list of active mobile subscriptions in the
form of one Preference per subscription.

This CL adds display of the mobile subscriptions to this header. It does
not yet show the correct summary text or do anything when you tap on
them - that will be coming in subsequent CLs. Also, since adding a
variable number of items to the top of the page messes up our current
strategy of having a fixed number of hidden items at the bottom based on
overall item count, this CL just makes all items visible. Subsequent CLs
can restore this behavior with dynamic adjustment.

Bug: 116349402
Test: make RunSettingsRoboTests
Change-Id: Ibfadf8cb61f6f5aff6ce38b7974267b1e4ddc719
This commit is contained in:
Antony Sargent
2018-12-12 09:09:45 -08:00
parent 3f5966bb48
commit e92e07eb97
7 changed files with 594 additions and 5 deletions

View File

@@ -0,0 +1,137 @@
/*
* 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;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atLeastOnce;
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.Context;
import android.telephony.SubscriptionManager;
import com.android.settingslib.core.lifecycle.Lifecycle;
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.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import java.util.List;
import androidx.lifecycle.LifecycleOwner;
import androidx.preference.PreferenceCategory;
import androidx.preference.PreferenceScreen;
@RunWith(RobolectricTestRunner.class)
public class MultiNetworkHeaderControllerTest {
private static final String KEY_HEADER = "multi_network_header";
@Mock
private PreferenceScreen mPreferenceScreen;
@Mock
private PreferenceCategory mPreferenceCategory;
@Mock
private SubscriptionsPreferenceController mSubscriptionsController;
@Mock
private SubscriptionManager mSubscriptionManager;
private Context mContext;
private LifecycleOwner mLifecycleOwner;
private Lifecycle mLifecycle;
private MultiNetworkHeaderController mHeaderController;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = spy(RuntimeEnvironment.application);
mLifecycleOwner = () -> mLifecycle;
mLifecycle = new Lifecycle(mLifecycleOwner);
when(mContext.getSystemService(SubscriptionManager.class)).thenReturn(mSubscriptionManager);
when(mPreferenceScreen.findPreference(eq(KEY_HEADER))).thenReturn(mPreferenceCategory);
mHeaderController = spy(new MultiNetworkHeaderController(mContext, KEY_HEADER));
doReturn(mSubscriptionsController).when(mHeaderController).createSubscriptionsController(
mLifecycle);
}
@Test
public void isAvailable_beforeInitIsCalled_notAvailable() {
assertThat(mHeaderController.isAvailable()).isFalse();
}
// When calling displayPreference, the header itself should only be visible if the
// subscriptions controller says it is available. This is a helper for test cases of this logic.
private void displayPreferenceTest(boolean subscriptionsAvailable,
boolean setVisibleExpectedValue) {
when(mSubscriptionsController.isAvailable()).thenReturn(subscriptionsAvailable);
mHeaderController.init(mLifecycle);
mHeaderController.displayPreference(mPreferenceScreen);
verify(mPreferenceCategory, never()).setVisible(eq(!setVisibleExpectedValue));
verify(mPreferenceCategory, atLeastOnce()).setVisible(eq(setVisibleExpectedValue));
}
@Test
public void displayPreference_subscriptionsNotAvailable_categoryIsNotVisible() {
displayPreferenceTest(false, false);
}
@Test
public void displayPreference_subscriptionsAvailable_categoryIsVisible() {
displayPreferenceTest(true, true);
}
@Test
public void onChildUpdated_subscriptionsBecameAvailable_categoryIsVisible() {
when(mSubscriptionsController.isAvailable()).thenReturn(false);
mHeaderController.init(mLifecycle);
mHeaderController.displayPreference(mPreferenceScreen);
when(mSubscriptionsController.isAvailable()).thenReturn(true);
mHeaderController.onChildrenUpdated();
ArgumentCaptor<Boolean> captor = ArgumentCaptor.forClass(Boolean.class);
verify(mPreferenceCategory, atLeastOnce()).setVisible(captor.capture());
List<Boolean> values = captor.getAllValues();
assertThat(values.get(values.size()-1)).isEqualTo(Boolean.TRUE);
}
@Test
public void onChildUpdated_subscriptionsBecameUnavailable_categoryIsNotVisible() {
when(mSubscriptionsController.isAvailable()).thenReturn(true);
mHeaderController.init(mLifecycle);
mHeaderController.displayPreference(mPreferenceScreen);
when(mSubscriptionsController.isAvailable()).thenReturn(false);
mHeaderController.onChildrenUpdated();
ArgumentCaptor<Boolean> captor = ArgumentCaptor.forClass(Boolean.class);
verify(mPreferenceCategory, atLeastOnce()).setVisible(captor.capture());
List<Boolean> values = captor.getAllValues();
assertThat(values.get(values.size()-1)).isEqualTo(Boolean.FALSE);
}
}

View File

@@ -0,0 +1,218 @@
/*
* 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;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.provider.Settings;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import com.android.settingslib.core.lifecycle.Lifecycle;
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.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import java.util.ArrayList;
import java.util.Arrays;
import androidx.lifecycle.LifecycleOwner;
import androidx.preference.Preference;
import androidx.preference.PreferenceCategory;
import androidx.preference.PreferenceScreen;
@RunWith(RobolectricTestRunner.class)
public class SubscriptionsPreferenceControllerTest {
private static final String KEY = "preference_group";
@Mock
private PreferenceScreen mScreen;
@Mock
private PreferenceCategory mPreferenceCategory;
@Mock
private SubscriptionManager mSubscriptionManager;
private Context mContext;
private LifecycleOwner mLifecycleOwner;
private Lifecycle mLifecycle;
private SubscriptionsPreferenceController mController;
private int mOnChildUpdatedCount;
private SubscriptionsPreferenceController.UpdateListener mUpdateListener;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = spy(RuntimeEnvironment.application);
mLifecycleOwner = () -> mLifecycle;
mLifecycle = new Lifecycle(mLifecycleOwner);
when(mContext.getSystemService(SubscriptionManager.class)).thenReturn(mSubscriptionManager);
when(mScreen.findPreference(eq(KEY))).thenReturn(mPreferenceCategory);
when(mPreferenceCategory.getContext()).thenReturn(mContext);
mOnChildUpdatedCount = 0;
mUpdateListener = () -> mOnChildUpdatedCount++;
mController = new SubscriptionsPreferenceController(mContext, mLifecycle, mUpdateListener,
KEY, 5);
}
@Test
public void isAvailable_oneSubscription_availableFalse() {
SubscriptionUtil.setAvailableSubscriptionsForTesting(
Arrays.asList(mock(SubscriptionInfo.class)));
assertThat(mController.isAvailable()).isFalse();
}
@Test
public void isAvailable_twoSubscriptions_availableTrue() {
SubscriptionUtil.setAvailableSubscriptionsForTesting(
Arrays.asList(mock(SubscriptionInfo.class), mock(SubscriptionInfo.class)));
assertThat(mController.isAvailable()).isTrue();
}
@Test
public void isAvailable_fiveSubscriptions_availableTrue() {
final ArrayList<SubscriptionInfo> subs = new ArrayList<>();
for (int i = 0; i < 5; i++) {
subs.add(mock(SubscriptionInfo.class));
}
SubscriptionUtil.setAvailableSubscriptionsForTesting(subs);
assertThat(mController.isAvailable()).isTrue();
}
@Test
public void isAvailable_airplaneModeOn_availableFalse() {
SubscriptionUtil.setAvailableSubscriptionsForTesting(
Arrays.asList(mock(SubscriptionInfo.class), mock(SubscriptionInfo.class)));
assertThat(mController.isAvailable()).isTrue();
Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON, 1);
assertThat(mController.isAvailable()).isFalse();
}
@Test
public void onAirplaneModeChanged_airplaneModeTurnedOn_eventFired() {
SubscriptionUtil.setAvailableSubscriptionsForTesting(
Arrays.asList(mock(SubscriptionInfo.class), mock(SubscriptionInfo.class)));
mController.onResume();
mController.displayPreference(mScreen);
assertThat(mController.isAvailable()).isTrue();
final int updateCountBeforeModeChange = mOnChildUpdatedCount;
Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON, 1);
mController.onAirplaneModeChanged(true);
assertThat(mController.isAvailable()).isFalse();
assertThat(mOnChildUpdatedCount).isEqualTo(updateCountBeforeModeChange + 1);
}
@Test
public void onAirplaneModeChanged_airplaneModeTurnedOff_eventFired() {
Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON, 1);
SubscriptionUtil.setAvailableSubscriptionsForTesting(
Arrays.asList(mock(SubscriptionInfo.class), mock(SubscriptionInfo.class)));
mController.onResume();
mController.displayPreference(mScreen);
assertThat(mController.isAvailable()).isFalse();
final int updateCountBeforeModeChange = mOnChildUpdatedCount;
Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON, 0);
mController.onAirplaneModeChanged(false);
assertThat(mController.isAvailable()).isTrue();
assertThat(mOnChildUpdatedCount).isEqualTo(updateCountBeforeModeChange + 1);
}
@Test
public void onSubscriptionsChanged_countBecameTwo_eventFired() {
final SubscriptionInfo sub1 = mock(SubscriptionInfo.class);
final SubscriptionInfo sub2 = mock(SubscriptionInfo.class);
SubscriptionUtil.setAvailableSubscriptionsForTesting(Arrays.asList(sub1));
mController.onResume();
mController.displayPreference(mScreen);
assertThat(mController.isAvailable()).isFalse();
final int updateCountBeforeSubscriptionChange = mOnChildUpdatedCount;
SubscriptionUtil.setAvailableSubscriptionsForTesting(Arrays.asList(sub1, sub2));
mController.onSubscriptionsChanged();
assertThat(mController.isAvailable()).isTrue();
assertThat(mOnChildUpdatedCount).isEqualTo(updateCountBeforeSubscriptionChange + 1);
}
@Test
public void onSubscriptionsChanged_countBecameOne_eventFired() {
final SubscriptionInfo sub1 = mock(SubscriptionInfo.class);
final SubscriptionInfo sub2 = mock(SubscriptionInfo.class);
SubscriptionUtil.setAvailableSubscriptionsForTesting(Arrays.asList(sub1, sub2));
mController.onResume();
mController.displayPreference(mScreen);
assertThat(mController.isAvailable()).isTrue();
final int updateCountBeforeSubscriptionChange = mOnChildUpdatedCount;
SubscriptionUtil.setAvailableSubscriptionsForTesting(Arrays.asList(sub1));
mController.onSubscriptionsChanged();
assertThat(mController.isAvailable()).isFalse();
assertThat(mOnChildUpdatedCount).isEqualTo(updateCountBeforeSubscriptionChange + 1);
}
@Test
public void onSubscriptionsChanged_subscriptionReplaced_preferencesChanged() {
final SubscriptionInfo sub1 = mock(SubscriptionInfo.class);
final SubscriptionInfo sub2 = mock(SubscriptionInfo.class);
final SubscriptionInfo sub3 = mock(SubscriptionInfo.class);
when(sub1.getDisplayName()).thenReturn("sub1");
when(sub2.getDisplayName()).thenReturn("sub2");
when(sub3.getDisplayName()).thenReturn("sub3");
when(sub1.getSubscriptionId()).thenReturn(1);
when(sub2.getSubscriptionId()).thenReturn(2);
when(sub3.getSubscriptionId()).thenReturn(3);
// Start out with only sub1 and sub2.
SubscriptionUtil.setAvailableSubscriptionsForTesting(Arrays.asList(sub1, sub2));
mController.onResume();
mController.displayPreference(mScreen);
final ArgumentCaptor<Preference> captor = ArgumentCaptor.forClass(Preference.class);
verify(mPreferenceCategory, times(2)).addPreference(captor.capture());
assertThat(captor.getAllValues().size()).isEqualTo(2);
assertThat(captor.getAllValues().get(0).getTitle()).isEqualTo("sub1");
assertThat(captor.getAllValues().get(1).getTitle()).isEqualTo("sub2");
// Now replace sub2 with sub3, and make sure the old preference was removed and the new
// preference was added.
final int updateCountBeforeSubscriptionChange = mOnChildUpdatedCount;
SubscriptionUtil.setAvailableSubscriptionsForTesting(Arrays.asList(sub1, sub3));
mController.onSubscriptionsChanged();
assertThat(mController.isAvailable()).isTrue();
assertThat(mOnChildUpdatedCount).isEqualTo(updateCountBeforeSubscriptionChange + 1);
verify(mPreferenceCategory).removePreference(captor.capture());
assertThat(captor.getValue().getTitle()).isEqualTo("sub2");
verify(mPreferenceCategory, times(3)).addPreference(captor.capture());
assertThat(captor.getValue().getTitle()).isEqualTo("sub3");
}
}