Merge "Initial skeleton of new "Supervision" settings screen with top-level entry point." into main

This commit is contained in:
Yvonne Jiang
2025-01-21 11:27:13 -08:00
committed by Android (Google) Code Review
12 changed files with 423 additions and 0 deletions

View File

@@ -0,0 +1,5 @@
# Bug component: 1680938
jparks@google.com
sonnyf@google.com
yvonnejiang@google.com

View File

@@ -0,0 +1,46 @@
/*
* Copyright (C) 2025 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.supervision
import android.app.settings.SettingsEnums
import android.content.Context
import com.android.settings.R
import com.android.settings.dashboard.DashboardFragment
/**
* Fragment to display the Supervision settings landing page (Settings > Supervision).
*
* See [SupervisionDashboardScreen] for details on the page contents.
*
* This class extends [DashboardFragment] in order to support dynamic settings injection.
*/
class SupervisionDashboardFragment : DashboardFragment() {
override fun getPreferenceScreenResId() = R.xml.placeholder_preference_screen
override fun getMetricsCategory() = SettingsEnums.SUPERVISION_DASHBOARD
override fun getLogTag() = TAG
override fun getPreferenceScreenBindingKey(context: Context) = SupervisionDashboardScreen.KEY
// TODO(b/383405598): redirect to Play Store if supervisor client is not
// fully present.
companion object {
private const val TAG = "SupervisionDashboard"
}
}

View File

@@ -0,0 +1,68 @@
/*
* Copyright (C) 2025 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.supervision
import android.app.supervision.flags.Flags
import android.content.Context
import com.android.settings.R
import com.android.settingslib.metadata.ProvidePreferenceScreen
import com.android.settingslib.metadata.preferenceHierarchy
import com.android.settingslib.preference.PreferenceScreenCreator
/**
* Supervision settings landing page (Settings > Supervision).
*
* This screen typically includes three parts:
* 1. Primary switch to toggle supervision on and off.
* 2. List of supervision features. Individual features like website filters or bedtime schedules
* will be listed in a group and link out to their own respective settings pages. Features
* implemented by supervision client apps can also be dynamically injected into this group.
* 3. Entry point to supervision PIN management settings page.
*/
@ProvidePreferenceScreen(SupervisionDashboardScreen.KEY)
class SupervisionDashboardScreen : PreferenceScreenCreator {
override fun isFlagEnabled(context: Context) = Flags.enableSupervisionSettingsScreen()
override val key: String
get() = KEY
override val title: Int
get() = R.string.supervision_settings_title
override val summary: Int
get() = R.string.supervision_settings_summary
override val icon: Int
get() = R.drawable.ic_account_child_invert
override val keywords: Int
get() = R.string.keywords_supervision_settings
override fun fragmentClass() = SupervisionDashboardFragment::class.java
override fun getPreferenceHierarchy(context: Context) =
preferenceHierarchy(context, this) {
+SupervisionMainSwitchPreference()
+TitlelessPreferenceGroup("supervision_features_group_1") += {
// Empty category for dynamic injection targeting.
}
}
companion object {
const val KEY = "top_level_supervision"
}
}

View File

@@ -0,0 +1,71 @@
/*
* Copyright (C) 2025 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.supervision
import android.app.supervision.SupervisionManager
import android.content.Context
import com.android.settings.R
import com.android.settingslib.datastore.KeyValueStore
import com.android.settingslib.datastore.NoOpKeyedObservable
import com.android.settingslib.metadata.MainSwitchPreference
import com.android.settingslib.metadata.PreferenceSummaryProvider
import com.android.settingslib.metadata.ReadWritePermit
import com.android.settingslib.metadata.SensitivityLevel
/** Main toggle to enable or disable device supervision. */
class SupervisionMainSwitchPreference :
MainSwitchPreference(KEY, R.string.device_supervision_switch_title), PreferenceSummaryProvider {
// TODO(b/383568136): Make presence of summary conditional on whether PIN
// has been set up before or not.
override fun getSummary(context: Context): CharSequence? =
context.getString(R.string.device_supervision_switch_no_pin_summary)
override fun storage(context: Context): KeyValueStore = SupervisionMainSwitchStorage(context)
override fun getReadPermit(context: Context, callingPid: Int, callingUid: Int) =
ReadWritePermit.DISALLOW
override fun getWritePermit(
context: Context,
value: Boolean?,
callingPid: Int,
callingUid: Int,
) = ReadWritePermit.DISALLOW
override val sensitivityLevel: Int
get() = SensitivityLevel.HIGH_SENSITIVITY
// TODO(b/390505725): Listen for changes in supervision state.
@Suppress("UNCHECKED_CAST")
private class SupervisionMainSwitchStorage(private val context: Context) :
NoOpKeyedObservable<String>(), KeyValueStore {
override fun contains(key: String) = key == KEY
override fun <T : Any> getValue(key: String, valueType: Class<T>) =
(context.getSystemService(SupervisionManager::class.java)?.isSupervisionEnabled() ==
true)
as T
override fun <T : Any> setValue(key: String, valueType: Class<T>, value: T?) {
// TODO(b/383402852): implement handling of main toggle.
}
}
companion object {
const val KEY = "device_supervision_switch"
}
}

View File

@@ -0,0 +1,35 @@
/*
* Copyright (C) 2025 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.supervision
import androidx.preference.Preference
import com.android.settingslib.metadata.PreferenceCategory
import com.android.settingslib.metadata.PreferenceMetadata
import com.android.settingslib.preference.PreferenceCategoryBinding
import com.android.settingslib.widget.theme.R
/**
* A [PreferenceCategory] that does not have a title, and hides the space reserved for displaying
* the title label above the category.
*/
class TitlelessPreferenceGroup(override val key: String) :
PreferenceCategory(key, title = 0), PreferenceCategoryBinding {
override fun bind(preference: Preference, metadata: PreferenceMetadata) {
preference.layoutResource = R.layout.settingslib_preference_category_no_title
super.bind(preference, metadata)
}
}

View File

@@ -0,0 +1,28 @@
/*
* Copyright (C) 2025 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.supervision
import android.app.supervision.flags.Flags
import android.content.Context
import com.android.settings.core.BasePreferenceController
/** Controller for the top level Supervision settings Preference item. */
class TopLevelSupervisionPreferenceController(context: Context, key: String) :
BasePreferenceController(context, key) {
override fun getAvailabilityStatus(): Int =
if (Flags.enableSupervisionSettingsScreen()) AVAILABLE else UNSUPPORTED_ON_DEVICE
}