diff --git a/src/com/android/settings/network/MobileNetworkListFragment.java b/src/com/android/settings/network/MobileNetworkListFragment.java deleted file mode 100644 index 3de05afeea7..00000000000 --- a/src/com/android/settings/network/MobileNetworkListFragment.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * 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; - -import android.app.settings.SettingsEnums; -import android.content.Context; -import android.os.UserManager; - -import androidx.recyclerview.widget.RecyclerView; - -import com.android.settings.R; -import com.android.settings.dashboard.DashboardFragment; -import com.android.settings.network.telephony.MobileNetworkUtils; -import com.android.settings.search.BaseSearchIndexProvider; -import com.android.settingslib.search.SearchIndexable; - -@SearchIndexable(forTarget = SearchIndexable.ALL & ~SearchIndexable.ARC) -public class MobileNetworkListFragment extends DashboardFragment { - private static final String LOG_TAG = "NetworkListFragment"; - - private static final String KEY_ADD_SIM = "add_sim"; - - @Override - public void onResume() { - super.onResume(); - // Disable the animation of the preference list - final RecyclerView prefListView = getListView(); - if (prefListView != null) { - prefListView.setItemAnimator(null); - } - - findPreference(KEY_ADD_SIM).setVisible(MobileNetworkUtils.showEuiccSettings(getContext())); - } - - @Override - protected int getPreferenceScreenResId() { - return R.xml.network_provider_sims_list; - } - - @Override - protected String getLogTag() { - return LOG_TAG; - } - - @Override - public int getMetricsCategory() { - return SettingsEnums.MOBILE_NETWORK_LIST; - } - - public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = - new BaseSearchIndexProvider(R.xml.network_provider_sims_list) { - - @Override - protected boolean isPageSearchEnabled(Context context) { - return SubscriptionUtil.isSimHardwareVisible(context) && - context.getSystemService(UserManager.class).isAdminUser(); - } - }; -} diff --git a/src/com/android/settings/network/MobileNetworkListFragment.kt b/src/com/android/settings/network/MobileNetworkListFragment.kt new file mode 100644 index 00000000000..5000afdf0a3 --- /dev/null +++ b/src/com/android/settings/network/MobileNetworkListFragment.kt @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2023 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 android.app.settings.SettingsEnums +import android.content.Context +import android.os.Bundle +import android.provider.Settings +import android.view.View +import androidx.annotation.VisibleForTesting +import androidx.preference.Preference +import com.android.settings.R +import com.android.settings.SettingsPreferenceFragment +import com.android.settings.dashboard.DashboardFragment +import com.android.settings.network.telephony.MobileNetworkUtils +import com.android.settings.search.BaseSearchIndexProvider +import com.android.settings.utils.observeSettingsGlobalBoolean +import com.android.settingslib.search.SearchIndexable +import com.android.settingslib.spaprivileged.framework.common.userManager + +@SearchIndexable(forTarget = SearchIndexable.ALL and SearchIndexable.ARC.inv()) +class MobileNetworkListFragment : DashboardFragment() { + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + observeAirplaneModeAndFinishIfOn() + } + + override fun onResume() { + super.onResume() + // Disable the animation of the preference list + listView.itemAnimator = null + + findPreference(KEY_ADD_SIM)!!.isVisible = + MobileNetworkUtils.showEuiccSettings(context) + } + + override fun getPreferenceScreenResId() = R.xml.network_provider_sims_list + + override fun getLogTag() = LOG_TAG + + override fun getMetricsCategory() = SettingsEnums.MOBILE_NETWORK_LIST + + companion object { + private const val LOG_TAG = "NetworkListFragment" + private const val KEY_ADD_SIM = "add_sim" + + @JvmStatic + fun SettingsPreferenceFragment.observeAirplaneModeAndFinishIfOn() { + requireContext().observeSettingsGlobalBoolean( + name = Settings.Global.AIRPLANE_MODE_ON, + lifecycle = viewLifecycleOwner.lifecycle, + ) { isAirplaneModeOn: Boolean -> + if (isAirplaneModeOn) { + finish() + } + } + } + + @JvmField + val SEARCH_INDEX_DATA_PROVIDER = SearchIndexProvider() + + @VisibleForTesting + class SearchIndexProvider : BaseSearchIndexProvider(R.xml.network_provider_sims_list) { + public override fun isPageSearchEnabled(context: Context): Boolean = + SubscriptionUtil.isSimHardwareVisible(context) && + context.userManager.isAdminUser + } + } +} diff --git a/src/com/android/settings/network/telephony/MobileNetworkSettings.java b/src/com/android/settings/network/telephony/MobileNetworkSettings.java index afc1b7e1c6f..7e290b829ed 100644 --- a/src/com/android/settings/network/telephony/MobileNetworkSettings.java +++ b/src/com/android/settings/network/telephony/MobileNetworkSettings.java @@ -16,6 +16,8 @@ package com.android.settings.network.telephony; +import static com.android.settings.network.MobileNetworkListFragment.observeAirplaneModeAndFinishIfOn; + import android.app.Activity; import android.app.settings.SettingsEnums; import android.content.Context; @@ -31,7 +33,10 @@ import android.util.Log; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; +import android.view.View; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import androidx.preference.Preference; @@ -326,6 +331,12 @@ public class MobileNetworkSettings extends AbstractMobileNetworkSettings impleme onRestoreInstance(icicle); } + @Override + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + observeAirplaneModeAndFinishIfOn(this); + } + @Override public void onResume() { super.onResume(); @@ -361,11 +372,6 @@ public class MobileNetworkSettings extends AbstractMobileNetworkSettings impleme super.onPause(); } - @Override - public void onDestroy() { - super.onDestroy(); - } - @VisibleForTesting void onRestoreInstance(Bundle icicle) { if (icicle != null) { diff --git a/src/com/android/settings/utils/SettingsGlobalBooleanDelegate.kt b/src/com/android/settings/utils/SettingsGlobalBooleanDelegate.kt new file mode 100644 index 00000000000..fdfbdb4ecfa --- /dev/null +++ b/src/com/android/settings/utils/SettingsGlobalBooleanDelegate.kt @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2023 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.utils + +import android.content.ContentResolver +import android.content.Context +import android.database.ContentObserver +import android.os.Handler +import android.provider.Settings +import androidx.lifecycle.DefaultLifecycleObserver +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.LifecycleOwner +import kotlin.properties.ReadWriteProperty +import kotlin.reflect.KProperty + +fun Context.observeSettingsGlobalBoolean( + name: String, + lifecycle: Lifecycle, + onChange: (newValue: Boolean) -> Unit, +) { + val field by settingsGlobalBoolean(name) + val contentObserver = object : ContentObserver(Handler.getMain()) { + override fun onChange(selfChange: Boolean) { + onChange(field) + } + } + val uri = Settings.Global.getUriFor(name) + lifecycle.addObserver(object : DefaultLifecycleObserver { + override fun onStart(owner: LifecycleOwner) { + contentResolver.registerContentObserver(uri, false, contentObserver) + onChange(field) + } + + override fun onStop(owner: LifecycleOwner) { + contentResolver.unregisterContentObserver(contentObserver) + } + }) +} + +fun Context.settingsGlobalBoolean(name: String): ReadWriteProperty = + SettingsGlobalBooleanDelegate(this, name) + +private class SettingsGlobalBooleanDelegate(context: Context, private val name: String) : + ReadWriteProperty { + + private val contentResolver: ContentResolver = context.contentResolver + + override fun getValue(thisRef: Any?, property: KProperty<*>): Boolean = + Settings.Global.getInt(contentResolver, name, 0) != 0 + + override fun setValue(thisRef: Any?, property: KProperty<*>, value: Boolean) { + Settings.Global.putInt(contentResolver, name, if (value) 1 else 0) + } +} diff --git a/tests/robotests/src/com/android/settings/network/MobileNetworkListFragmentTest.java b/tests/robotests/src/com/android/settings/network/MobileNetworkListFragmentTest.java deleted file mode 100644 index 2e04ea751f0..00000000000 --- a/tests/robotests/src/com/android/settings/network/MobileNetworkListFragmentTest.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * 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; - -import static com.google.common.truth.Truth.assertThat; - -import static org.mockito.Mockito.when; - -import android.content.Context; -import android.content.res.Resources; -import android.os.UserManager; - -import com.android.settings.R; -import com.android.settings.search.BaseSearchIndexProvider; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.robolectric.RobolectricTestRunner; -import org.robolectric.util.ReflectionHelpers; - -@RunWith(RobolectricTestRunner.class) -public class MobileNetworkListFragmentTest { - @Mock - private Context mContext; - @Mock - private Resources mResources; - @Mock - private UserManager mUserManager; - - private MobileNetworkListFragment mFragment; - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - mFragment = new MobileNetworkListFragment(); - } - - @Test - public void isPageSearchEnabled_adminUser_shouldReturnTrue() { - when(mContext.getSystemService(UserManager.class)).thenReturn(mUserManager); - when(mUserManager.isAdminUser()).thenReturn(true); - final BaseSearchIndexProvider provider = - (BaseSearchIndexProvider) mFragment.SEARCH_INDEX_DATA_PROVIDER; - - when(mContext.getResources()).thenReturn(mResources); - when(mResources.getBoolean(R.bool.config_show_sim_info)).thenReturn(true); - - final Object obj = ReflectionHelpers.callInstanceMethod(provider, "isPageSearchEnabled", - ReflectionHelpers.ClassParameter.from(Context.class, mContext)); - final boolean isEnabled = (Boolean) obj; - - assertThat(isEnabled).isTrue(); - } - - @Test - public void isPageSearchEnabled_nonAdminUser_shouldReturnFalse() { - when(mContext.getSystemService(UserManager.class)).thenReturn(mUserManager); - when(mUserManager.isAdminUser()).thenReturn(false); - final BaseSearchIndexProvider provider = - (BaseSearchIndexProvider) mFragment.SEARCH_INDEX_DATA_PROVIDER; - - when(mContext.getResources()).thenReturn(mResources); - when(mResources.getBoolean(R.bool.config_show_sim_info)).thenReturn(true); - - final Object obj = ReflectionHelpers.callInstanceMethod(provider, "isPageSearchEnabled", - ReflectionHelpers.ClassParameter.from(Context.class, mContext)); - final boolean isEnabled = (Boolean) obj; - - assertThat(isEnabled).isFalse(); - } -} diff --git a/tests/spa_unit/src/com/android/settings/network/MobileNetworkListFragmentTest.kt b/tests/spa_unit/src/com/android/settings/network/MobileNetworkListFragmentTest.kt new file mode 100644 index 00000000000..3ba4bac39ce --- /dev/null +++ b/tests/spa_unit/src/com/android/settings/network/MobileNetworkListFragmentTest.kt @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2023 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 android.content.Context +import android.content.res.Resources +import android.os.UserManager +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.android.settings.R +import com.android.settingslib.spaprivileged.framework.common.userManager +import com.google.common.truth.Truth.assertThat +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.mock +import org.mockito.kotlin.spy +import org.mockito.kotlin.stub + +@RunWith(AndroidJUnit4::class) +class MobileNetworkListFragmentTest { + private val mockUserManager = mock() + + private val mockResources = mock() + + private val context: Context = spy(ApplicationProvider.getApplicationContext()) { + on { userManager } doReturn mockUserManager + on { resources } doReturn mockResources + } + + @Test + fun isPageSearchEnabled_adminUser_shouldReturnTrue() { + mockUserManager.stub { + on { isAdminUser } doReturn true + } + mockResources.stub { + on { getBoolean(R.bool.config_show_sim_info) } doReturn true + } + + val isEnabled = + MobileNetworkListFragment.SEARCH_INDEX_DATA_PROVIDER.isPageSearchEnabled(context) + + assertThat(isEnabled).isTrue() + } + + @Test + fun isPageSearchEnabled_nonAdminUser_shouldReturnFalse() { + mockUserManager.stub { + on { isAdminUser } doReturn false + } + mockResources.stub { + on { getBoolean(R.bool.config_show_sim_info) } doReturn true + } + + val isEnabled = + MobileNetworkListFragment.SEARCH_INDEX_DATA_PROVIDER.isPageSearchEnabled(context) + + assertThat(isEnabled).isFalse() + } +} diff --git a/tests/spa_unit/src/com/android/settings/utils/SettingsGlobalBooleanDelegateTest.kt b/tests/spa_unit/src/com/android/settings/utils/SettingsGlobalBooleanDelegateTest.kt new file mode 100644 index 00000000000..75c368561af --- /dev/null +++ b/tests/spa_unit/src/com/android/settings/utils/SettingsGlobalBooleanDelegateTest.kt @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2023 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.utils + +import android.content.Context +import android.provider.Settings +import androidx.lifecycle.testing.TestLifecycleOwner +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.google.common.truth.Truth.assertThat +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +class SettingsGlobalBooleanDelegateTest { + + private val context: Context = ApplicationProvider.getApplicationContext() + + @Test + fun getValue_setTrue_returnTrue() { + Settings.Global.putInt(context.contentResolver, TEST_NAME, 1) + + val value by context.settingsGlobalBoolean(TEST_NAME) + + assertThat(value).isTrue() + } + + @Test + fun getValue_setFalse_returnFalse() { + Settings.Global.putInt(context.contentResolver, TEST_NAME, 0) + + val value by context.settingsGlobalBoolean(TEST_NAME) + + assertThat(value).isFalse() + } + + @Test + fun setValue_setTrue_returnTrue() { + var value by context.settingsGlobalBoolean(TEST_NAME) + + value = true + + assertThat(Settings.Global.getInt(context.contentResolver, TEST_NAME, 0)).isEqualTo(1) + } + + @Test + fun setValue_setFalse_returnFalse() { + var value by context.settingsGlobalBoolean(TEST_NAME) + + value = false + + assertThat(Settings.Global.getInt(context.contentResolver, TEST_NAME, 1)).isEqualTo(0) + } + + @Test + fun observeSettingsGlobalBoolean_valueNotChanged() { + var value by context.settingsGlobalBoolean(TEST_NAME) + value = false + var newValue: Boolean? = null + + context.observeSettingsGlobalBoolean(TEST_NAME, TestLifecycleOwner().lifecycle) { + newValue = it + } + + assertThat(newValue).isFalse() + } + + @Test + fun observeSettingsGlobalBoolean_valueChanged() { + var value by context.settingsGlobalBoolean(TEST_NAME) + value = false + var newValue: Boolean? = null + + context.observeSettingsGlobalBoolean(TEST_NAME, TestLifecycleOwner().lifecycle) { + newValue = it + } + value = true + + assertThat(newValue).isFalse() + } + + private companion object { + const val TEST_NAME = "test_boolean_delegate" + } +}