diff --git a/src/com/android/settings/spa/SettingsSpaEnvironment.kt b/src/com/android/settings/spa/SettingsSpaEnvironment.kt index 15aaa74a607..55c0f835719 100644 --- a/src/com/android/settings/spa/SettingsSpaEnvironment.kt +++ b/src/com/android/settings/spa/SettingsSpaEnvironment.kt @@ -34,6 +34,7 @@ import com.android.settings.spa.app.specialaccess.WifiControlAppListProvider import com.android.settings.spa.app.specialaccess.UseFullScreenIntentAppListProvider import com.android.settings.spa.development.UsageStatsPageProvider import com.android.settings.spa.home.HomePageProvider +import com.android.settings.spa.network.NetworkAndInternetPageProvider import com.android.settings.spa.notification.AppListNotificationsPageProvider import com.android.settings.spa.notification.NotificationMainPageProvider import com.android.settings.spa.system.AppLanguagesPageProvider @@ -79,7 +80,8 @@ open class SettingsSpaEnvironment(context: Context) : SpaEnvironment(context) { UsageStatsPageProvider, BackgroundInstalledAppsPageProvider, CloneAppInfoSettingsProvider, - ) + togglePermissionAppListTemplate.createPageProviders(), + NetworkAndInternetPageProvider, + ) + togglePermissionAppListTemplate.createPageProviders(), rootPages = listOf( SettingsPage.create(HomePageProvider.name), ), diff --git a/src/com/android/settings/spa/app/network/AirplaneModePreference.kt b/src/com/android/settings/spa/app/network/AirplaneModePreference.kt new file mode 100644 index 00000000000..9cdf76dd8b5 --- /dev/null +++ b/src/com/android/settings/spa/app/network/AirplaneModePreference.kt @@ -0,0 +1,81 @@ +/** + * 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.spa.app.network + +import android.content.Context +import android.content.pm.PackageManager +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.AirplanemodeActive +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.runtime.livedata.observeAsState +import androidx.compose.ui.platform.LocalContext +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import com.android.settings.AirplaneModeEnabler +import com.android.settings.AirplaneModeEnabler.OnAirplaneModeChangedListener +import com.android.settings.R +import com.android.settingslib.spa.widget.preference.SwitchPreference +import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel +import com.android.settingslib.spa.widget.ui.SettingsIcon + +@Composable +fun AirplaneModePreference() { + val context = LocalContext.current + val controller = remember { AirplaneModeController(context) } + if (!controller.isAvailable()) return + SwitchPreference(object : SwitchPreferenceModel { + override val title = context.getString(R.string.airplane_mode) + override val checked = controller.airplaneModeState.observeAsState( + initial = controller.isAirplaneModeOn() + ) + override val onCheckedChange = { newChecked: Boolean -> + controller.setChecked(newChecked) + } + override val icon = @Composable { + SettingsIcon(imageVector = Icons.Outlined.AirplanemodeActive) + } + }) +} + +private class AirplaneModeController(private val context: Context) : OnAirplaneModeChangedListener { + private var airplaneModeEnabler = AirplaneModeEnabler(context, this)!! + private val _airplaneModeState = MutableLiveData() + val airplaneModeState: LiveData + get() = _airplaneModeState + + override fun onAirplaneModeChanged(isAirplaneModeOn: Boolean) { + _airplaneModeState.postValue(isAirplaneModeOn) + } + + fun isAvailable(): Boolean { + return context.resources.getBoolean(R.bool.config_show_toggle_airplane) + && !context.packageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK) + } + + + fun isAirplaneModeOn(): Boolean { + return airplaneModeEnabler.isAirplaneModeOn() + } + + fun setChecked(newChecked: Boolean) { + if (isAirplaneModeOn() == newChecked) { + return + } + airplaneModeEnabler.setAirplaneMode(newChecked) + } + +} diff --git a/src/com/android/settings/spa/app/network/NetworkAndInternet.kt b/src/com/android/settings/spa/app/network/NetworkAndInternet.kt new file mode 100644 index 00000000000..307a2f7b935 --- /dev/null +++ b/src/com/android/settings/spa/app/network/NetworkAndInternet.kt @@ -0,0 +1,95 @@ +/** + * 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.spa.network + +import android.content.Context +import android.os.Bundle +import android.os.UserHandle.myUserId +import android.os.UserManager +import android.os.UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.Wifi +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.res.stringResource +import com.android.settings.R +import com.android.settings.spa.app.network.AirplaneModePreference +import com.android.settingslib.RestrictedLockUtilsInternal +import com.android.settingslib.Utils +import com.android.settingslib.spa.framework.common.SettingsEntryBuilder +import com.android.settingslib.spa.framework.common.SettingsPageProvider +import com.android.settingslib.spa.framework.common.SpaEnvironmentFactory +import com.android.settingslib.spa.framework.common.createSettingsPage +import com.android.settingslib.spa.framework.compose.navigator +import com.android.settingslib.spa.framework.compose.toState +import com.android.settingslib.spa.widget.preference.Preference +import com.android.settingslib.spa.widget.preference.PreferenceModel +import com.android.settingslib.spa.widget.scaffold.RegularScaffold +import com.android.settingslib.spa.widget.ui.SettingsIcon + +object NetworkAndInternetPageProvider : SettingsPageProvider { + override val name = "NetworkAndInternet" + private val owner = createSettingsPage() + + @Composable + override fun Page(arguments: Bundle?) { + RegularScaffold(title = getTitle(arguments)) { + AirplaneModePreference() + } + } + + override fun getTitle(arguments: Bundle?): String { + return SpaEnvironmentFactory.instance.appContext.getString(R.string.network_dashboard_title) + } + + fun buildInjectEntry(): SettingsEntryBuilder { + return SettingsEntryBuilder.createInject(owner = owner) + .setUiLayoutFn { + Preference(object : PreferenceModel { + override val title = stringResource(R.string.network_dashboard_title) + override val summary = stringResource(getSummaryResId()).toState() + override val onClick = navigator(name) + override val icon = @Composable { + SettingsIcon(imageVector = Icons.Outlined.Wifi) + } + }) + } + } + + @Composable + private fun getSummaryResId(): Int { + val isMobileAvailable = remember { isMobileAvailable() } + var summary = if (isMobileAvailable) { + R.string.network_dashboard_summary_mobile + } else { + R.string.network_dashboard_summary_no_mobile + } + return summary + } + + private fun isMobileAvailable(): Boolean { + val context = SpaEnvironmentFactory.instance.appContext + return !isUserRestricted(context) && !Utils.isWifiOnly(context) + } + + private fun isUserRestricted(context: Context): Boolean { + val userManager: UserManager = context.getSystemService(UserManager::class.java)!! + return !userManager.isAdminUser || RestrictedLockUtilsInternal.hasBaseUserRestriction( + context, DISALLOW_CONFIG_MOBILE_NETWORKS, myUserId() + ) + } +} diff --git a/src/com/android/settings/spa/home/HomePage.kt b/src/com/android/settings/spa/home/HomePage.kt index e33fedcf4df..d2416f44a0d 100644 --- a/src/com/android/settings/spa/home/HomePage.kt +++ b/src/com/android/settings/spa/home/HomePage.kt @@ -19,6 +19,7 @@ package com.android.settings.spa.home import android.os.Bundle import com.android.settings.R import com.android.settings.spa.app.AppsMainPageProvider +import com.android.settings.spa.network.NetworkAndInternetPageProvider import com.android.settings.spa.notification.NotificationMainPageProvider import com.android.settings.spa.system.SystemMainPageProvider import com.android.settingslib.spa.framework.common.SettingsEntry @@ -32,6 +33,8 @@ object HomePageProvider : SettingsPageProvider { override fun buildEntry(arguments: Bundle?): List { return listOf( + + NetworkAndInternetPageProvider.buildInjectEntry().setLink(fromPage = owner).build(), AppsMainPageProvider.buildInjectEntry().setLink(fromPage = owner).build(), NotificationMainPageProvider.buildInjectEntry().setLink(fromPage = owner).build(), SystemMainPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),