Merge "Merge mainline-prod into master"
diff --git a/res/layout/notif_priority_conversation_preference.xml b/res/layout/notif_priority_conversation_preference.xml
index 5da1238..d07bc23 100644
--- a/res/layout/notif_priority_conversation_preference.xml
+++ b/res/layout/notif_priority_conversation_preference.xml
@@ -31,27 +31,32 @@
android:padding="@dimen/notification_importance_button_padding"
android:clickable="true"
android:focusable="true">
- <ImageView
- android:id="@+id/icon"
- android:src="@drawable/ic_important_outline"
- android:background="@android:color/transparent"
- android:layout_gravity="center"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:clickable="false"
- android:focusable="false"/>
- <TextView
- android:id="@+id/label"
+ <LinearLayout
+ android:id="@+id/icon_label_container"
+ android:gravity="center_vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:ellipsize="end"
- android:maxLines="1"
- android:clickable="false"
- android:focusable="false"
- android:layout_toEndOf="@id/icon"
- android:layout_marginStart="@dimen/notification_importance_drawable_padding"
- android:textAppearance="@style/TextAppearance.NotificationImportanceButton.Unselected"
- android:text="@string/notification_priority_title"/>
+ android:orientation="horizontal">
+ <ImageView
+ android:id="@+id/icon"
+ android:src="@drawable/ic_important_outline"
+ android:background="@android:color/transparent"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:clickable="false"
+ android:focusable="false"/>
+ <TextView
+ android:id="@+id/label"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:ellipsize="end"
+ android:maxLines="1"
+ android:clickable="false"
+ android:focusable="false"
+ android:layout_marginStart="@dimen/notification_importance_drawable_padding"
+ android:textAppearance="@style/TextAppearance.NotificationImportanceButton.Unselected"
+ android:text="@string/notification_priority_title"/>
+ </LinearLayout>
<TextView
android:id="@+id/summary"
android:paddingTop="@dimen/notification_importance_button_padding"
@@ -62,7 +67,6 @@
android:focusable="false"
android:ellipsize="end"
android:maxLines="4"
- android:layout_below="@id/icon"
android:textAppearance="@style/TextAppearance.NotificationImportanceDetail"
android:visibility="gone" />
</com.android.settings.notification.NotificationButtonRelativeLayout>
@@ -75,27 +79,32 @@
android:layout_marginTop="@dimen/notification_importance_button_separation"
android:clickable="true"
android:focusable="true">
- <ImageView
- android:id="@+id/icon"
- android:src="@drawable/ic_notifications_alert"
- android:background="@android:color/transparent"
- android:layout_gravity="center"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:clickable="false"
- android:focusable="false"/>
- <TextView
- android:id="@+id/label"
+ <LinearLayout
+ android:id="@+id/icon_label_container"
+ android:gravity="center_vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:ellipsize="end"
- android:maxLines="1"
- android:clickable="false"
- android:focusable="false"
- android:layout_toEndOf="@id/icon"
- android:layout_marginStart="@dimen/notification_importance_drawable_padding"
- android:textAppearance="@style/TextAppearance.NotificationImportanceButton.Unselected"
- android:text="@string/notification_alert_title"/>
+ android:orientation="horizontal">
+ <ImageView
+ android:id="@+id/icon"
+ android:src="@drawable/ic_notifications_alert"
+ android:background="@android:color/transparent"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:clickable="false"
+ android:focusable="false"/>
+ <TextView
+ android:id="@+id/label"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:ellipsize="end"
+ android:maxLines="1"
+ android:clickable="false"
+ android:focusable="false"
+ android:layout_marginStart="@dimen/notification_importance_drawable_padding"
+ android:textAppearance="@style/TextAppearance.NotificationImportanceButton.Unselected"
+ android:text="@string/notification_alert_title"/>
+ </LinearLayout>
<TextView
android:id="@+id/summary"
android:paddingTop="@dimen/notification_importance_button_padding"
@@ -106,7 +115,7 @@
android:focusable="false"
android:ellipsize="end"
android:maxLines="2"
- android:layout_below="@id/icon"
+ android:layout_below="@id/icon_label_container"
android:textAppearance="@style/TextAppearance.NotificationImportanceDetail"
android:visibility="gone" />
</com.android.settings.notification.NotificationButtonRelativeLayout>
@@ -119,27 +128,32 @@
android:layout_marginTop="@dimen/notification_importance_button_separation"
android:clickable="true"
android:focusable="true">
- <ImageView
- android:id="@+id/icon"
- android:src="@drawable/ic_notifications_off_24dp"
- android:background="@android:color/transparent"
- android:layout_gravity="center"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:clickable="false"
- android:focusable="false"/>
- <TextView
- android:id="@+id/label"
+ <LinearLayout
+ android:id="@+id/icon_label_container"
+ android:gravity="center_vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:ellipsize="end"
- android:maxLines="1"
- android:clickable="false"
- android:focusable="false"
- android:layout_toEndOf="@id/icon"
- android:layout_marginStart="@dimen/notification_importance_drawable_padding"
- android:textAppearance="@style/TextAppearance.NotificationImportanceButton.Unselected"
- android:text="@string/notification_silence_title"/>
+ android:orientation="horizontal">
+ <ImageView
+ android:id="@+id/icon"
+ android:src="@drawable/ic_notifications_off_24dp"
+ android:background="@android:color/transparent"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:clickable="false"
+ android:focusable="false"/>
+ <TextView
+ android:id="@+id/label"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:ellipsize="end"
+ android:maxLines="1"
+ android:clickable="false"
+ android:focusable="false"
+ android:layout_marginStart="@dimen/notification_importance_drawable_padding"
+ android:textAppearance="@style/TextAppearance.NotificationImportanceButton.Unselected"
+ android:text="@string/notification_silence_title"/>
+ </LinearLayout>
<TextView
android:id="@+id/summary"
android:paddingTop="@dimen/notification_importance_button_padding"
@@ -150,7 +164,7 @@
android:focusable="false"
android:ellipsize="end"
android:maxLines="2"
- android:layout_below="@id/icon"
+ android:layout_below="@id/icon_label_container"
android:textAppearance="@style/TextAppearance.NotificationImportanceDetail"
android:visibility="gone" />
</com.android.settings.notification.NotificationButtonRelativeLayout>
diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml
index e50e9de..41053e4 100644
--- a/res/values-cs/strings.xml
+++ b/res/values-cs/strings.xml
@@ -1727,7 +1727,7 @@
<string name="sms_change_default_no_previous_dialog_text" msgid="6215622785087181275">"Chcete pro zprávy SMS používat aplikaci <xliff:g id="NEW_APP">%s</xliff:g>?"</string>
<string name="network_scorer_picker_title" msgid="2022922801936206195">"Poskytovatel hodnocení sítí"</string>
<string name="network_scorer_picker_none_preference" msgid="8894034333043177807">"Žádné"</string>
- <string name="network_scorer_change_active_dialog_title" msgid="7005220310238618141">"Změnit nastavení Google Wi-Fi Assistant?"</string>
+ <string name="network_scorer_change_active_dialog_title" msgid="7005220310238618141">"Změnit asistenta pro Wi-Fi?"</string>
<string name="network_scorer_change_active_dialog_text" msgid="7006057749370850706">"Chcete spravovat síťová připojení pomocí aplikace <xliff:g id="NEW_APP">%1$s</xliff:g> namísto aplikace <xliff:g id="CURRENT_APP">%2$s</xliff:g>?"</string>
<string name="network_scorer_change_active_no_previous_dialog_text" msgid="680685773455072321">"Chcete spravovat síťová připojení pomocí aplikace <xliff:g id="NEW_APP">%s</xliff:g>?"</string>
<string name="mobile_unknown_sim_operator" msgid="6650422533065760963">"Operátor SIM karty není znám"</string>
diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml
index 378ac84..d151027 100644
--- a/res/values-sk/strings.xml
+++ b/res/values-sk/strings.xml
@@ -3587,7 +3587,7 @@
<string name="gentle_notifications_display_summary_shade_status_lock" msgid="2068738866725616212">"Zobrazovať v rozbaľovacom paneli, stavovom riadku a na uzamknutej obrazovke"</string>
<string name="silent_notifications_status_bar" msgid="6113307620588767516">"Skryť tiché upozornenia v stavovom riadku"</string>
<string name="notification_pulse_title" msgid="8013178454646671529">"Blikať"</string>
- <string name="lock_screen_notifications_title" msgid="3063951257121435570">"Upozornenia na uzamkn. obraz."</string>
+ <string name="lock_screen_notifications_title" msgid="3063951257121435570">"Upozornenia na uzamknutej obrazovke"</string>
<string name="lockscreen_bypass_title" msgid="6519964196744088573">"Preskakovať uzamknutú obrazovku"</string>
<string name="lockscreen_bypass_summary" msgid="6688592486830491144">"Po odomk. tvárou priamo prejsť na poslednú použitú obrazovku"</string>
<string name="keywords_lockscreen_bypass" msgid="41035425468915498">"Uzamknutá obrazovka, preskočiť, obísť"</string>
diff --git a/src/com/android/settings/bluetooth/DevicePickerFragment.java b/src/com/android/settings/bluetooth/DevicePickerFragment.java
index 02625bb..ab8eea5 100644
--- a/src/com/android/settings/bluetooth/DevicePickerFragment.java
+++ b/src/com/android/settings/bluetooth/DevicePickerFragment.java
@@ -18,6 +18,7 @@
import static android.os.UserManager.DISALLOW_CONFIG_BLUETOOTH;
+import android.Manifest;
import android.app.settings.SettingsEnums;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
@@ -192,6 +193,6 @@
if (mLaunchPackage != null && mLaunchClass != null) {
intent.setClassName(mLaunchPackage, mLaunchClass);
}
- getActivity().sendBroadcast(intent);
+ getActivity().sendBroadcast(intent, Manifest.permission.BLUETOOTH_ADMIN);
}
}
diff --git a/src/com/android/settings/deviceinfo/OWNERS b/src/com/android/settings/deviceinfo/OWNERS
index bedbe16..e6569f4 100644
--- a/src/com/android/settings/deviceinfo/OWNERS
+++ b/src/com/android/settings/deviceinfo/OWNERS
@@ -1,6 +1,7 @@
# Default reviewers for this and subdirectories.
andychou@google.com
bonianchen@google.com
+goldmanj@google.com
allenwtsu@google.com
# Emergency approvers in case the above are not available
diff --git a/src/com/android/settings/network/OWNERS b/src/com/android/settings/network/OWNERS
index 87e5fcc..991ad43 100644
--- a/src/com/android/settings/network/OWNERS
+++ b/src/com/android/settings/network/OWNERS
@@ -2,6 +2,7 @@
allenwtsu@google.com
andychou@google.com
bonianchen@google.com
+goldmanj@google.com
leechou@google.com
songferngwang@google.com
tomhsu@google.com
diff --git a/src/com/android/settings/sim/OWNERS b/src/com/android/settings/sim/OWNERS
index bedbe16..e6569f4 100644
--- a/src/com/android/settings/sim/OWNERS
+++ b/src/com/android/settings/sim/OWNERS
@@ -1,6 +1,7 @@
# Default reviewers for this and subdirectories.
andychou@google.com
bonianchen@google.com
+goldmanj@google.com
allenwtsu@google.com
# Emergency approvers in case the above are not available
diff --git a/src/com/android/settings/wifi/calling/OWNERS b/src/com/android/settings/wifi/calling/OWNERS
index 87e5fcc..991ad43 100644
--- a/src/com/android/settings/wifi/calling/OWNERS
+++ b/src/com/android/settings/wifi/calling/OWNERS
@@ -2,6 +2,7 @@
allenwtsu@google.com
andychou@google.com
bonianchen@google.com
+goldmanj@google.com
leechou@google.com
songferngwang@google.com
tomhsu@google.com
diff --git a/src/com/android/settings/wifi/details2/WifiDetailPreferenceController2.java b/src/com/android/settings/wifi/details2/WifiDetailPreferenceController2.java
index b862540..9676f58 100644
--- a/src/com/android/settings/wifi/details2/WifiDetailPreferenceController2.java
+++ b/src/com/android/settings/wifi/details2/WifiDetailPreferenceController2.java
@@ -165,6 +165,7 @@
private NetworkInfo mNetworkInfo;
private NetworkCapabilities mNetworkCapabilities;
private int mRssiSignalLevel = -1;
+ @VisibleForTesting boolean mShowX; // Shows the Wi-Fi signal icon of Pie+x when it's true.
private String[] mSignalStr;
private WifiInfo mWifiInfo;
private final WifiManager mWifiManager;
@@ -554,7 +555,7 @@
}
private void refreshRssiViews() {
- int signalLevel = mWifiEntry.getLevel();
+ final int signalLevel = mWifiEntry.getLevel();
// Disappears signal view if not in range. e.g. for saved networks.
if (signalLevel == WifiEntry.WIFI_LEVEL_UNREACHABLE) {
@@ -563,11 +564,14 @@
return;
}
- if (mRssiSignalLevel == signalLevel) {
+ final boolean showX = mWifiEntry.shouldShowXLevelIcon();
+
+ if (mRssiSignalLevel == signalLevel && mShowX == showX) {
return;
}
mRssiSignalLevel = signalLevel;
- Drawable wifiIcon = mIconInjector.getIcon(mRssiSignalLevel);
+ mShowX = showX;
+ Drawable wifiIcon = mIconInjector.getIcon(mShowX, mRssiSignalLevel);
if (mEntityHeaderController != null) {
mEntityHeaderController
@@ -1003,8 +1007,8 @@
mContext = context;
}
- public Drawable getIcon(int level) {
- return mContext.getDrawable(Utils.getWifiIconResource(level)).mutate();
+ public Drawable getIcon(boolean showX, int level) {
+ return mContext.getDrawable(Utils.getWifiIconResource(showX, level)).mutate();
}
}
diff --git a/tests/robotests/src/com/android/settings/wifi/details2/WifiDetailPreferenceController2Test.java b/tests/robotests/src/com/android/settings/wifi/details2/WifiDetailPreferenceController2Test.java
index 9aaaa43..1f4254e 100644
--- a/tests/robotests/src/com/android/settings/wifi/details2/WifiDetailPreferenceController2Test.java
+++ b/tests/robotests/src/com/android/settings/wifi/details2/WifiDetailPreferenceController2Test.java
@@ -299,7 +299,7 @@
.thenReturn(mMockHeaderController);
when(mMockHeaderController.setSecondSummary(nullable(String.class)))
.thenReturn(mMockHeaderController);
- when(mMockIconInjector.getIcon(anyInt())).thenReturn(new ColorDrawable());
+ when(mMockIconInjector.getIcon(anyBoolean(), anyInt())).thenReturn(new ColorDrawable());
setupMockedPreferenceScreen();
}
@@ -500,7 +500,7 @@
public void entityHeader_shouldHaveIconSetForConnectedNetwork() {
setUpForConnectedNetwork();
setUpSpyController();
- Drawable expectedIcon = mMockIconInjector.getIcon(LEVEL);
+ Drawable expectedIcon = mMockIconInjector.getIcon(false /* showX */, LEVEL);
displayAndResume();
@@ -510,7 +510,7 @@
@Test
public void entityHeader_shouldHaveIconSetForDisconnectedNetwork() {
setUpForDisconnectedNetwork();
- Drawable expectedIcon = mMockIconInjector.getIcon(LEVEL);
+ Drawable expectedIcon = mMockIconInjector.getIcon(false /* showX */, LEVEL);
displayAndResume();
@@ -615,6 +615,7 @@
displayAndResume();
+ assertThat(mController.mShowX).isFalse();
verify(mMockSignalStrengthPref).setIcon(any(Drawable.class));
}
@@ -624,6 +625,7 @@
displayAndResume();
+ assertThat(mController.mShowX).isFalse();
verify(mMockSignalStrengthPref).setIcon(any(Drawable.class));
}
@@ -633,6 +635,7 @@
displayAndResume();
+ assertThat(mController.mShowX).isFalse();
verify(mMockSignalStrengthPref, never()).setIcon(any(Drawable.class));
}
@@ -645,6 +648,7 @@
displayAndResume();
+ assertThat(mController.mShowX).isFalse();
verify(mMockSignalStrengthPref).setSummary(expectedStrength);
}
@@ -656,6 +660,7 @@
displayAndResume();
+ assertThat(mController.mShowX).isFalse();
verify(mMockSignalStrengthPref).setSummary(expectedStrength);
}
@@ -665,10 +670,25 @@
displayAndResume();
+ assertThat(mController.mShowX).isFalse();
verify(mMockSignalStrengthPref, never()).setSummary(any(String.class));
}
@Test
+ public void signalStrengthPref_shouldShowXLevelIcon_showXTrue() {
+ setUpForConnectedNetwork();
+ setUpSpyController();
+ final String expectedStrength =
+ mContext.getResources().getStringArray(R.array.wifi_signal)[LEVEL];
+ when(mMockWifiEntry.shouldShowXLevelIcon()).thenReturn(true);
+
+ displayAndResume();
+
+ assertThat(mController.mShowX).isTrue();
+ verify(mMockSignalStrengthPref).setSummary(expectedStrength);
+ }
+
+ @Test
public void linkSpeedPref_shouldNotShowIfNotSet() {
setUpForConnectedNetwork();
setUpSpyController();
@@ -1529,7 +1549,7 @@
ArgumentCaptor<BitmapDrawable> drawableCaptor =
ArgumentCaptor.forClass(BitmapDrawable.class);
Drawable original = mContext.getDrawable(Utils.getWifiIconResource(LEVEL)).mutate();
- when(mMockIconInjector.getIcon(anyInt())).thenReturn(original);
+ when(mMockIconInjector.getIcon(anyBoolean(), anyInt())).thenReturn(original);
displayAndResume();
@@ -1548,7 +1568,7 @@
ArgumentCaptor<BitmapDrawable> drawableCaptor =
ArgumentCaptor.forClass(BitmapDrawable.class);
Drawable original = mContext.getDrawable(Utils.getWifiIconResource(LEVEL)).mutate();
- when(mMockIconInjector.getIcon(anyInt())).thenReturn(original);
+ when(mMockIconInjector.getIcon(anyBoolean(), anyInt())).thenReturn(original);
displayAndResume();
diff --git a/tests/unit/src/com/android/settings/datausage/AppPrefLoaderTest.java b/tests/unit/src/com/android/settings/datausage/AppPrefLoaderTest.java
new file mode 100644
index 0000000..902906c
--- /dev/null
+++ b/tests/unit/src/com/android/settings/datausage/AppPrefLoaderTest.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2020 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.datausage;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.graphics.drawable.Drawable;
+import android.util.ArraySet;
+
+import androidx.preference.Preference;
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidJUnit4.class)
+public class AppPrefLoaderTest {
+
+ @Mock
+ private PackageManager mPackageManager;
+
+ private AppPrefLoader mLoader;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ final ArraySet<String> pkgs = new ArraySet<>(2);
+ pkgs.add("pkg0");
+ pkgs.add("pkg1");
+ mLoader = new AppPrefLoader(
+ ApplicationProvider.getApplicationContext(), pkgs, mPackageManager);
+ }
+
+ @Test
+ public void loadInBackground_packageNotFound_shouldReturnEmptySet()
+ throws NameNotFoundException {
+ when(mPackageManager.getApplicationInfo(anyString(), anyInt()))
+ .thenThrow(new NameNotFoundException());
+
+ assertThat(mLoader.loadInBackground()).isEmpty();
+ }
+
+ @Test
+ public void loadInBackground_shouldReturnPreference() throws NameNotFoundException {
+ ApplicationInfo info = mock(ApplicationInfo.class);
+ when(mPackageManager.getApplicationInfo(anyString(), anyInt())).thenReturn(info);
+ final Drawable drawable = mock(Drawable.class);
+ final String label = "Label1";
+ when(info.loadIcon(mPackageManager)).thenReturn(drawable);
+ when(info.loadLabel(mPackageManager)).thenReturn(label);
+
+ Preference preference = mLoader.loadInBackground().valueAt(0);
+ assertThat(preference.getTitle()).isEqualTo(label);
+ assertThat(preference.getIcon()).isEqualTo(drawable);
+ assertThat(preference.isSelectable()).isFalse();
+ }
+}
diff --git a/tests/unit/src/com/android/settings/network/MultiNetworkHeaderControllerTest.java b/tests/unit/src/com/android/settings/network/MultiNetworkHeaderControllerTest.java
new file mode 100644
index 0000000..cdb82a6
--- /dev/null
+++ b/tests/unit/src/com/android/settings/network/MultiNetworkHeaderControllerTest.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2020 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.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.os.Looper;
+import android.telephony.SubscriptionManager;
+
+import androidx.preference.PreferenceCategory;
+import androidx.preference.PreferenceManager;
+import androidx.preference.PreferenceScreen;
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.android.settings.wifi.WifiConnectionPreferenceController;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+
+@RunWith(AndroidJUnit4.class)
+public class MultiNetworkHeaderControllerTest {
+ private static final String KEY_HEADER = "multi_network_header";
+ private static final int EXPANDED_CHILDREN_COUNT = 5;
+
+ @Mock
+ private PreferenceCategory mPreferenceCategory;
+ @Mock
+ private WifiConnectionPreferenceController mWifiController;
+ @Mock
+ private SubscriptionsPreferenceController mSubscriptionsController;
+ @Mock
+ private SubscriptionManager mSubscriptionManager;
+ @Mock
+ private Lifecycle mLifecycle;
+
+ private Context mContext;
+ private PreferenceManager mPreferenceManager;
+ private PreferenceScreen mPreferenceScreen;
+ private MultiNetworkHeaderController mHeaderController;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mContext = spy(ApplicationProvider.getApplicationContext());
+ when(mContext.getSystemService(SubscriptionManager.class)).thenReturn(mSubscriptionManager);
+
+ mHeaderController = new MultiNetworkHeaderController(mContext, KEY_HEADER) {
+ @Override
+ WifiConnectionPreferenceController createWifiController(Lifecycle lifecycle) {
+ return mWifiController;
+ }
+
+ @Override
+ SubscriptionsPreferenceController createSubscriptionsController(Lifecycle lifecycle) {
+ return mSubscriptionsController;
+ }
+ };
+
+ if (Looper.myLooper() == null) {
+ Looper.prepare();
+ }
+ mPreferenceManager = new PreferenceManager(mContext);
+ mPreferenceScreen = mPreferenceManager.createPreferenceScreen(mContext);
+ mPreferenceScreen.setInitialExpandedChildrenCount(EXPANDED_CHILDREN_COUNT);
+ when(mPreferenceCategory.getKey()).thenReturn(KEY_HEADER);
+ when(mPreferenceCategory.getPreferenceCount()).thenReturn(3);
+ mPreferenceScreen.addPreference(mPreferenceCategory);
+ }
+
+ @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 wifiAvailable, boolean subscriptionsAvailable,
+ boolean setVisibleExpectedValue) {
+ when(mWifiController.isAvailable()).thenReturn(wifiAvailable);
+ when(mSubscriptionsController.isAvailable()).thenReturn(subscriptionsAvailable);
+
+ mHeaderController.init(mLifecycle);
+ mHeaderController.displayPreference(mPreferenceScreen);
+ Assert.assertEquals(mPreferenceCategory.isVisible(), setVisibleExpectedValue);
+ }
+
+ @Test
+ public void displayPreference_bothNotAvailable_categoryIsNotVisible() {
+ displayPreferenceTest(false, false, false);
+ }
+
+ @Test
+ public void displayPreference_wifiAvailableButNotSubscriptions_categoryIsNotVisible() {
+ displayPreferenceTest(true, false, false);
+ }
+
+ @Test
+ public void displayPreference_subscriptionsAvailableButNotWifi_categoryIsVisible() {
+ displayPreferenceTest(false, true, true);
+ }
+
+ @Test
+ public void displayPreference_bothAvailable_categoryIsVisible() {
+ displayPreferenceTest(true, 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();
+
+ Assert.assertTrue(mPreferenceCategory.isVisible());
+ assertThat(mPreferenceScreen.getInitialExpandedChildrenCount()).isEqualTo(
+ EXPANDED_CHILDREN_COUNT + mPreferenceCategory.getPreferenceCount());
+ }
+
+ @Test
+ public void onChildUpdated_subscriptionsBecameUnavailable_categoryIsNotVisible() {
+ when(mSubscriptionsController.isAvailable()).thenReturn(true);
+ mHeaderController.init(mLifecycle);
+ mHeaderController.displayPreference(mPreferenceScreen);
+
+ when(mSubscriptionsController.isAvailable()).thenReturn(false);
+ mHeaderController.onChildrenUpdated();
+
+ Assert.assertFalse(mPreferenceCategory.isVisible());
+ assertThat(mPreferenceScreen.getInitialExpandedChildrenCount()).isEqualTo(
+ EXPANDED_CHILDREN_COUNT);
+ }
+
+ @Test
+ public void onChildUpdated_noExpandedChildCountAndAvailable_doesNotSetExpandedCount() {
+ mPreferenceScreen.setInitialExpandedChildrenCount(Integer.MAX_VALUE);
+
+ when(mSubscriptionsController.isAvailable()).thenReturn(false);
+ mHeaderController.init(mLifecycle);
+ mHeaderController.displayPreference(mPreferenceScreen);
+
+ when(mSubscriptionsController.isAvailable()).thenReturn(true);
+ mHeaderController.onChildrenUpdated();
+
+ // Check that setInitialExpandedChildrenCount was never called.
+ Assert.assertEquals(mPreferenceScreen.getInitialExpandedChildrenCount(), Integer.MAX_VALUE);
+ }
+}
diff --git a/tests/unit/src/com/android/settings/network/telephony/MmsMessagePreferenceControllerTest.java b/tests/unit/src/com/android/settings/network/telephony/MmsMessagePreferenceControllerTest.java
new file mode 100644
index 0000000..6036ec7
--- /dev/null
+++ b/tests/unit/src/com/android/settings/network/telephony/MmsMessagePreferenceControllerTest.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2020 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.android.settings.core.BasePreferenceController.AVAILABLE;
+import static com.android.settings.core.BasePreferenceController.CONDITIONALLY_UNAVAILABLE;
+
+import static com.google.common.truth.Truth.assertThat;
+
+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 android.telephony.TelephonyManager;
+import android.telephony.data.ApnSetting;
+
+import androidx.preference.SwitchPreference;
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidJUnit4.class)
+public class MmsMessagePreferenceControllerTest {
+ private static final int SUB_ID = 2;
+
+ @Mock
+ private TelephonyManager mTelephonyManager;
+ @Mock
+ private SubscriptionManager mSubscriptionManager;
+
+ private MmsMessagePreferenceController mController;
+ private SwitchPreference mPreference;
+ private Context mContext;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ mContext = spy(ApplicationProvider.getApplicationContext());
+ when(mContext.getSystemService(TelephonyManager.class)).thenReturn(mTelephonyManager);
+ when(mContext.getSystemService(Context.TELEPHONY_SERVICE)).thenReturn(mTelephonyManager);
+ when(mContext.getSystemService(SubscriptionManager.class)).thenReturn(mSubscriptionManager);
+ when(mTelephonyManager.createForSubscriptionId(SUB_ID)).thenReturn(mTelephonyManager);
+
+ mPreference = new SwitchPreference(mContext);
+ mController = new MmsMessagePreferenceController(mContext, "mms_message");
+ mController.init(SUB_ID);
+ mPreference.setKey(mController.getPreferenceKey());
+ }
+
+ @Test
+ public void getAvailabilityStatus_invalidSubscription_returnUnavailable() {
+ mController.init(SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE);
+ }
+
+ @Test
+ public void getAvailabilityStatus_mobileDataOn_returnUnavailable() {
+ when(mTelephonyManager.isDataEnabled()).thenReturn(true);
+
+ assertThat(mController.getAvailabilityStatus(SUB_ID)).isEqualTo(CONDITIONALLY_UNAVAILABLE);
+ }
+
+ @Test
+ public void getAvailabilityStatus_meteredOff_returnUnavailable() {
+ when(mTelephonyManager.isApnMetered(ApnSetting.TYPE_MMS)).thenReturn(false);
+
+ assertThat(mController.getAvailabilityStatus(SUB_ID)).isEqualTo(CONDITIONALLY_UNAVAILABLE);
+ }
+
+ @Test
+ public void getAvailabilityStatus_mobileDataOffWithValidSubId_returnAvailable() {
+ mController.init(SUB_ID);
+ when(mTelephonyManager.isDataEnabled()).thenReturn(false);
+ when(mTelephonyManager.isApnMetered(ApnSetting.TYPE_MMS)).thenReturn(true);
+
+ assertThat(mController.getAvailabilityStatus(SUB_ID)).isEqualTo(AVAILABLE);
+ }
+
+ @Test
+ public void isChecked_returnDataFromTelephonyManager() {
+ when(mTelephonyManager.isDataEnabledForApn(ApnSetting.TYPE_MMS)).thenReturn(false);
+ assertThat(mController.isChecked()).isFalse();
+
+ when(mTelephonyManager.isDataEnabledForApn(ApnSetting.TYPE_MMS)).thenReturn(true);
+ assertThat(mController.isChecked()).isTrue();
+ }
+
+ @Test
+ public void setChecked_setDataIntoSubscriptionManager() {
+ mController.setChecked(true);
+ verify(mTelephonyManager).setAlwaysAllowMmsData(true);
+
+ mController.setChecked(false);
+ verify(mTelephonyManager).setAlwaysAllowMmsData(false);
+ }
+}
diff --git a/tests/unit/src/com/android/settings/network/telephony/MobileDataPreferenceControllerTest.java b/tests/unit/src/com/android/settings/network/telephony/MobileDataPreferenceControllerTest.java
new file mode 100644
index 0000000..0feabf2
--- /dev/null
+++ b/tests/unit/src/com/android/settings/network/telephony/MobileDataPreferenceControllerTest.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2020 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.android.settings.core.BasePreferenceController.AVAILABLE_UNSEARCHABLE;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.Instrumentation;
+import android.content.Context;
+import android.content.res.Resources;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+
+import androidx.fragment.app.FragmentManager;
+import androidx.fragment.app.FragmentTransaction;
+import androidx.preference.SwitchPreference;
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+
+@RunWith(AndroidJUnit4.class)
+public class MobileDataPreferenceControllerTest {
+ private static final int SUB_ID = 2;
+ private static final int SUB_ID_OTHER = 3;
+
+ @Mock
+ private FragmentManager mFragmentManager;
+ @Mock
+ private TelephonyManager mTelephonyManager;
+ @Mock
+ private TelephonyManager mInvalidTelephonyManager;
+ @Mock
+ private SubscriptionManager mSubscriptionManager;
+ @Mock
+ private SubscriptionInfo mSubscriptionInfo;
+ @Mock
+ private FragmentTransaction mFragmentTransaction;
+
+ private MobileDataPreferenceController mController;
+ private SwitchPreference mPreference;
+ private Context mContext;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ mContext = spy(ApplicationProvider.getApplicationContext());
+ doReturn(mTelephonyManager).when(mContext).getSystemService(Context.TELEPHONY_SERVICE);
+
+ when(mContext.getSystemService(SubscriptionManager.class)).thenReturn(mSubscriptionManager);
+ doReturn(mTelephonyManager).when(mTelephonyManager).createForSubscriptionId(SUB_ID);
+ doReturn(mInvalidTelephonyManager).when(mTelephonyManager).createForSubscriptionId(
+ SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+ doReturn(mFragmentTransaction).when(mFragmentManager).beginTransaction();
+
+ mPreference = new SwitchPreference(mContext);
+ mController = new MobileDataPreferenceController(mContext, "mobile_data");
+ mController.init(mFragmentManager, SUB_ID);
+ mPreference.setKey(mController.getPreferenceKey());
+ }
+
+ @Test
+ public void getAvailabilityStatus_invalidSubscription_returnAvailableUnsearchable() {
+ mController.init(mFragmentManager, SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE_UNSEARCHABLE);
+ }
+
+ @Test
+ public void isDialogNeeded_disableSingleSim_returnFalse() {
+ doReturn(true).when(mTelephonyManager).isDataEnabled();
+ doReturn(mSubscriptionInfo).when(mSubscriptionManager).getActiveSubscriptionInfo(SUB_ID);
+ doReturn(1).when(mTelephonyManager).getActiveModemCount();
+
+ assertThat(mController.isDialogNeeded()).isFalse();
+ }
+
+ @Test
+ public void isDialogNeeded_enableNonDefaultSimInMultiSimMode_returnTrue() {
+ doReturn(false).when(mTelephonyManager).isDataEnabled();
+ doReturn(mSubscriptionInfo).when(mSubscriptionManager)
+ .getActiveSubscriptionInfo(SUB_ID);
+ // Ideally, it would be better if we could set the default data subscription to
+ // SUB_ID_OTHER, and set that as an active subscription id.
+ when(mSubscriptionManager.isActiveSubscriptionId(anyInt())).thenReturn(true);
+ doReturn(2).when(mTelephonyManager).getActiveModemCount();
+
+ assertThat(mController.isDialogNeeded()).isTrue();
+ assertThat(mController.mDialogType).isEqualTo(
+ MobileDataDialogFragment.TYPE_MULTI_SIM_DIALOG);
+ }
+
+ @Test
+ public void handlePreferenceTreeClick_needDialog_showDialog() {
+ mController.mNeedDialog = true;
+ final Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+
+ instrumentation.runOnMainSync(() -> {
+ mController.handlePreferenceTreeClick(mPreference);
+ });
+ verify(mFragmentManager).beginTransaction();
+ }
+
+ @Test
+ public void onPreferenceChange_singleSim_On_shouldEnableData() {
+ doReturn(true).when(mTelephonyManager).isDataEnabled();
+ doReturn(mSubscriptionInfo).when(mSubscriptionManager).getActiveSubscriptionInfo(SUB_ID);
+ doReturn(1).when(mTelephonyManager).getActiveModemCount();
+
+ mController.onPreferenceChange(mPreference, true);
+
+ verify(mTelephonyManager).setDataEnabled(true);
+ }
+
+ @Test
+ public void onPreferenceChange_multiSim_On_shouldEnableData() {
+ doReturn(true).when(mTelephonyManager).isDataEnabled();
+ doReturn(mSubscriptionInfo).when(mSubscriptionManager).getActiveSubscriptionInfo(SUB_ID);
+ doReturn(2).when(mTelephonyManager).getActiveModemCount();
+
+ mController.onPreferenceChange(mPreference, true);
+
+ verify(mTelephonyManager).setDataEnabled(true);
+ }
+
+ @Test
+ public void isChecked_returnUserDataEnabled() {
+ mController.init(mFragmentManager, SUB_ID);
+ assertThat(mController.isChecked()).isFalse();
+
+ doReturn(true).when(mTelephonyManager).isDataEnabled();
+ assertThat(mController.isChecked()).isTrue();
+ }
+
+ @Test
+ public void updateState_opportunistic_disabled() {
+ doReturn(mSubscriptionInfo).when(mSubscriptionManager).getActiveSubscriptionInfo(SUB_ID);
+ mController.init(mFragmentManager, SUB_ID);
+ doReturn(true).when(mSubscriptionInfo).isOpportunistic();
+ mController.updateState(mPreference);
+
+ assertThat(mPreference.isEnabled()).isFalse();
+ assertThat(mPreference.getSummary())
+ .isEqualTo(resourceString("mobile_data_settings_summary_auto_switch"));
+ }
+
+ @Test
+ public void updateState_notOpportunistic_enabled() {
+ doReturn(mSubscriptionInfo).when(mSubscriptionManager).getActiveSubscriptionInfo(SUB_ID);
+ mController.init(mFragmentManager, SUB_ID);
+ doReturn(false).when(mSubscriptionInfo).isOpportunistic();
+ mController.updateState(mPreference);
+
+ assertThat(mPreference.isEnabled()).isTrue();
+ assertThat(mPreference.getSummary())
+ .isEqualTo(resourceString("mobile_data_settings_summary"));
+ }
+
+ public String resourceString(String name) {
+ final Resources res = mContext.getResources();
+ return res.getString(res.getIdentifier(name, "string", mContext.getPackageName()));
+ }
+}