fix: 首次提交

This commit is contained in:
2024-12-09 11:25:23 +08:00
parent d0c01071e9
commit 2c2109a5f3
4741 changed files with 290641 additions and 0 deletions

15
SettingsLib/Spa/.gitignore vendored Normal file
View File

@@ -0,0 +1,15 @@
*.iml
.gradle
/local.properties
/.idea/caches
/.idea/libraries
/.idea/modules.xml
/.idea/workspace.xml
/.idea/navEditor.xml
/.idea/assetWizardSettings.xml
.DS_Store
build
/captures
.externalNativeBuild
.cxx
local.properties

View File

@@ -0,0 +1,135 @@
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<JetCodeStyleSettings>
<option name="PACKAGES_IMPORT_LAYOUT">
<value>
<package name="" alias="false" withSubpackages="true" />
<package name="" alias="true" withSubpackages="true" />
</value>
</option>
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
</JetCodeStyleSettings>
<XML>
<option name="XML_KEEP_LINE_BREAKS" value="true" />
</XML>
<codeStyleSettings language="XML">
<option name="FORCE_REARRANGE_MODE" value="1" />
<indentOptions>
<option name="CONTINUATION_INDENT_SIZE" value="4" />
</indentOptions>
<arrangement>
<rules>
<section>
<rule>
<match>
<AND>
<NAME>xmlns:android</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>xmlns:.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:id</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:name</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>name</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>style</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
<order>ANDROID_ATTRIBUTE_ORDER</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>.*</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
</rules>
</arrangement>
</codeStyleSettings>
<codeStyleSettings language="kotlin">
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
<indentOptions>
<option name="CONTINUATION_INDENT_SIZE" value="4" />
</indentOptions>
</codeStyleSettings>
</code_scheme>
</component>

View File

@@ -0,0 +1,5 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
</state>
</component>

View File

@@ -0,0 +1,10 @@
<component name="CopyrightManager">
<copyright>
<option name="notice"
value="Copyright (C) &amp;#36;today.year The Android Open Source Project&#10;&#10;Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);&#10;you may not use this file except in compliance with the License.&#10;You may obtain a copy of the License at&#10;&#10; http://www.apache.org/licenses/LICENSE-2.0&#10;&#10;Unless required by applicable law or agreed to in writing, software&#10;distributed under the License is distributed on an &quot;AS IS&quot; BASIS,&#10;WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.&#10;See the License for the specific language governing permissions and&#10;limitations under the License."/>
<option name="keyword" value="Copyright"/>
<option name="allowReplaceKeyword" value=""/>
<option name="myName" value="Apache 2"/>
<option name="myLocal" value="true"/>
</copyright>
</component>

View File

@@ -0,0 +1,8 @@
<component name="CopyrightManager">
<settings default="Apache 2">
<LanguageOptions name="XML">
<option name="fileTypeOverride" value="3" />
<option name="prefixLines" value="false" />
</LanguageOptions>
</settings>
</component>

15
SettingsLib/Spa/.idea/vcs.xml generated Normal file
View File

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CommitMessageInspectionProfile">
<profile version="1.0">
<inspection_tool class="BodyLimit" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="SubjectBodySeparation" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="SubjectLimit" enabled="true" level="WARNING" enabled_by_default="true">
<option name="RIGHT_MARGIN" value="50" />
</inspection_tool>
</profile>
</component>
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$/../../.." vcs="Git" />
</component>
</project>

9
SettingsLib/Spa/OWNERS Normal file
View File

@@ -0,0 +1,9 @@
include platform/frameworks/base:/packages/SettingsLib/OWNERS
chaohuiw@google.com
hanxu@google.com
kellyz@google.com
pierreqian@google.com
lijun@google.com
songchenxi@google.com
cyl@google.com

View File

@@ -0,0 +1,18 @@
{
"presubmit": [
{
"name": "SpaLibTests"
},
{
"name": "SpaPrivilegedLibTests"
},
{
"name": "SettingsSpaUnitTests"
}
],
"postsubmit": [
{
"name": "SpaScreenshotTests"
}
]
}

View File

@@ -0,0 +1,73 @@
/*
* 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.
*/
import com.android.build.api.dsl.CommonExtension
import com.android.build.gradle.BaseExtension
import com.android.build.gradle.api.AndroidBasePlugin
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
alias(libs.plugins.android.application) apply false
alias(libs.plugins.android.library) apply false
alias(libs.plugins.kotlin.android) apply false
}
val androidTop: String = File(rootDir, "../../../../..").canonicalPath
allprojects {
extra["androidTop"] = androidTop
extra["jetpackComposeVersion"] = "1.7.0-alpha03"
}
subprojects {
layout.buildDirectory.set(file("$androidTop/out/gradle-spa/$name"))
plugins.withType<AndroidBasePlugin> {
configure<BaseExtension> {
compileSdkVersion(34)
defaultConfig {
minSdk = 21
targetSdk = 34
}
}
configure<JavaPluginExtension> {
toolchain {
languageVersion.set(JavaLanguageVersion.of(libs.versions.jvm.get()))
}
}
}
afterEvaluate {
plugins.withType<AndroidBasePlugin> {
the(CommonExtension::class).apply {
if (buildFeatures.compose == true) {
composeOptions {
kotlinCompilerExtensionVersion = libs.versions.compose.compiler.get()
}
}
}
}
}
tasks.withType<KotlinCompile> {
kotlinOptions {
jvmTarget = libs.versions.jvm.get()
freeCompilerArgs = listOf("-Xjvm-default=all")
}
}
}

View File

@@ -0,0 +1,33 @@
//
// Copyright (C) 2022 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 {
default_applicable_licenses: ["frameworks_base_license"],
}
android_app {
name: "SpaLibGallery",
srcs: ["src/**/*.kt"],
use_resource_processor: true,
static_libs: [
"SpaLib",
"androidx.compose.runtime_runtime",
],
kotlincflags: ["-Xjvm-default=all"],
sdk_version: "current",
min_sdk_version: "31",
}

View File

@@ -0,0 +1,82 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2022 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.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.settingslib.spa.gallery">
<uses-sdk android:minSdkVersion="21" />
<application
android:name=".GalleryApplication"
android:enableOnBackInvokedCallback="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_label"
android:supportsRtl="true">
<activity
android:name=".GalleryMainActivity"
android:configChanges="orientation|screenLayout|screenSize|smallestScreenSize"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<provider
android:name="com.android.settingslib.spa.search.SpaSearchProvider"
android:authorities="com.android.spa.gallery.search.provider"
android:exported="true"
android:grantUriPermissions="true"
android:permission="android.permission.READ_SEARCH_INDEXABLES">
<intent-filter>
<action android:name="android.content.action.SPA_SEARCH_PROVIDER" />
</intent-filter>
</provider>
<provider android:name="com.android.settingslib.spa.slice.SpaSliceProvider"
android:authorities="com.android.spa.gallery.slice.provider"
android:exported="true" >
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.app.slice.category.SLICE" />
</intent-filter>
</provider>
<receiver
android:name="com.android.settingslib.spa.slice.SpaSliceBroadcastReceiver"
android:exported="false">
</receiver>
<activity
android:name="com.android.settingslib.spa.debug.BlankActivity"
android:exported="true">
</activity>
<activity
android:name="com.android.settingslib.spa.debug.DebugActivity"
android:exported="true">
</activity>
<provider
android:name="com.android.settingslib.spa.debug.DebugProvider"
android:authorities="com.android.spa.gallery.debug.provider"
android:exported="false">
</provider>
<activity
android:name="com.android.settingslib.spa.gallery.GalleryDialogActivity"
android:exported="true"
android:theme="@style/Theme.SpaLib.Dialog">
</activity>
</application>
</manifest>

View File

@@ -0,0 +1,45 @@
/*
* Copyright 2022 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.
*/
plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.android)
}
android {
namespace = "com.android.settingslib.spa.gallery"
defaultConfig {
applicationId = "com.android.settingslib.spa.gallery"
versionCode = 1
versionName = "1.0"
}
sourceSets {
sourceSets.getByName("main") {
kotlin.setSrcDirs(listOf("src"))
res.setSrcDirs(listOf("res"))
manifest.srcFile("AndroidManifest.xml")
}
}
buildFeatures {
compose = true
}
}
dependencies {
implementation(project(":spa"))
}

View File

@@ -0,0 +1,52 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2021 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.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="412dp"
android:height="300dp"
android:viewportWidth="412"
android:viewportHeight="300">
<path
android:pathData="M383.9,300H28.1C12.6,300 0,287.4 0,271.9V28.1C0,12.6 12.6,0 28.1,0h355.8C399.4,0 412,12.6 412,28.1v243.8C412,287.4 399.4,300 383.9,300z"
android:fillColor="#FFFFFF"/>
<path
android:pathData="M79.2,179.6h53.6v8.5h-53.6z"
android:fillColor="#1A73E8"/>
<path
android:pathData="M142.5,179.6h30.4v8.5h-30.4z"
android:fillColor="#1A73E8"/>
<path
android:pathData="M79.2,195.5h79.2v8.5h-79.2z"
android:fillColor="#1A73E8"/>
<path
android:pathData="M168.1,195.5h34.1v8.5h-34.1z"
android:fillColor="#1A73E8"/>
<path
android:pathData="M211.9,195.5h34.1v8.5h-34.1z"
android:fillColor="#1A73E8"/>
<path
android:pathData="M182.7,179.6h73.1v8.5h-73.1z"
android:fillColor="#1A73E8"/>
<path
android:pathData="M265.5,179.6h26.8v8.5h-26.8z"
android:fillColor="#1A73E8"/>
<path
android:pathData="M302.1,179.6h26.8v8.5h-26.8z"
android:fillColor="#1A73E8"/>
<path
android:pathData="M142.7,67.9h-11.5c-1.6,0 -2.9,1.3 -2.9,2.9H67.8c-7.9,0 -14.4,6.5 -14.4,14.4v132.4c0,7.9 6.5,14.4 14.4,14.4h276.4c7.9,0 14.4,-6.5 14.4,-14.4V85.2c0,-7.9 -6.5,-14.4 -14.4,-14.4H203.1c0,-1.6 -1.3,-2.9 -2.9,-2.9h-28.8c-1.6,0 -2.9,1.3 -2.9,2.9h-23C145.5,69.2 144.3,67.9 142.7,67.9zM344.2,73.7c6.4,0 11.5,5.2 11.5,11.5v132.4c0,6.3 -5.2,11.5 -11.5,11.5H67.8c-6.4,0 -11.5,-5.2 -11.5,-11.5V85.2c0,-6.3 5.2,-11.5 11.5,-11.5H344.2z"
android:fillColor="#DADCE0"/>
</vector>

View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2022 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.
-->
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@mipmap/ic_launcher_background" />
<foreground android:drawable="@mipmap/ic_launcher_foreground" />
</adaptive-icon>

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2022 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.
-->
<resources>
<!-- Gallery App label. [DO NOT TRANSLATE] -->
<string name="app_label" translatable="false">Gallery</string>
<!-- Gallery App name. [DO NOT TRANSLATE] -->
<string name="app_name" translatable="false">SpaLib Gallery</string>
<!-- Title for single line summary preference. [DO NOT TRANSLATE] -->
<string name="single_line_summary_preference_title" translatable="false">Preference (singleLineSummary = true)</string>
<!-- Summary for single line summary preference. [DO NOT TRANSLATE] -->
<string name="single_line_summary_preference_summary" translatable="false">A very long summary to show case a preference which only shows a single line summary.</string>
<!-- Footer text with two links. [DO NOT TRANSLATE] -->
<string name="footer_with_two_links" translatable="false">Annotated string with <a href="https://www.android.com/">link 1</a> and <a href="https://source.android.com/">link 2</a>.</string>
<!-- Sample title -->
<string name="sample_title" translatable="false">Lorem ipsum</string>
<!-- Sample text -->
<string name="sample_text" translatable="false">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent a rhoncus tellus. Nulla facilisi. Pellentesque erat ex, maximus viae turpis</string>
</resources>

View File

@@ -0,0 +1,27 @@
/*
* Copyright (C) 2022 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.settingslib.spa.gallery
import android.app.Application
import com.android.settingslib.spa.framework.common.SpaEnvironmentFactory
class GalleryApplication : Application() {
override fun onCreate() {
super.onCreate()
SpaEnvironmentFactory.reset(GallerySpaEnvironment(this))
}
}

View File

@@ -0,0 +1,45 @@
/*
* Copyright (C) 2024 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.settingslib.spa.gallery
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.style.TextAlign
import com.android.settingslib.spa.SpaBaseDialogActivity
import com.android.settingslib.spa.widget.dialog.AlertDialogButton
import com.android.settingslib.spa.widget.dialog.SettingsAlertDialogWithIcon
class GalleryDialogActivity : SpaBaseDialogActivity() {
@Composable
override fun Content() {
SettingsAlertDialogWithIcon(
onDismissRequest = { finish() },
confirmButton = AlertDialogButton("confirm") { finish() },
dismissButton = AlertDialogButton("dismiss") { finish() },
title = "title",
text = {
Text(
"text",
modifier = Modifier.fillMaxWidth(),
textAlign = TextAlign.Center
)
}
)
}
}

View File

@@ -0,0 +1,21 @@
/*
* Copyright (C) 2022 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.settingslib.spa.gallery
import com.android.settingslib.spa.framework.BrowseActivity
class GalleryMainActivity : BrowseActivity()

View File

@@ -0,0 +1,124 @@
/*
* Copyright (C) 2022 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.settingslib.spa.gallery
import android.content.Context
import com.android.settingslib.spa.debug.DebugLogger
import com.android.settingslib.spa.framework.common.SettingsPageProviderRepository
import com.android.settingslib.spa.framework.common.SpaEnvironment
import com.android.settingslib.spa.framework.common.createSettingsPage
import com.android.settingslib.spa.gallery.button.ActionButtonPageProvider
import com.android.settingslib.spa.gallery.card.CardPageProvider
import com.android.settingslib.spa.gallery.chart.ChartPageProvider
import com.android.settingslib.spa.gallery.dialog.DialogMainPageProvider
import com.android.settingslib.spa.gallery.dialog.NavDialogProvider
import com.android.settingslib.spa.gallery.editor.EditorMainPageProvider
import com.android.settingslib.spa.gallery.editor.SettingsDropdownBoxPageProvider
import com.android.settingslib.spa.gallery.editor.SettingsDropdownCheckBoxProvider
import com.android.settingslib.spa.gallery.home.HomePageProvider
import com.android.settingslib.spa.gallery.itemList.ItemListPageProvider
import com.android.settingslib.spa.gallery.itemList.ItemOperatePageProvider
import com.android.settingslib.spa.gallery.itemList.OperateListPageProvider
import com.android.settingslib.spa.gallery.editor.SettingsOutlinedTextFieldPageProvider
import com.android.settingslib.spa.gallery.editor.SettingsTextFieldPasswordPageProvider
import com.android.settingslib.spa.gallery.page.ArgumentPageProvider
import com.android.settingslib.spa.gallery.page.FooterPageProvider
import com.android.settingslib.spa.gallery.page.IllustrationPageProvider
import com.android.settingslib.spa.gallery.page.LoadingBarPageProvider
import com.android.settingslib.spa.gallery.page.ProgressBarPageProvider
import com.android.settingslib.spa.gallery.page.SettingsPagerPageProvider
import com.android.settingslib.spa.gallery.page.SliderPageProvider
import com.android.settingslib.spa.gallery.preference.ListPreferencePageProvider
import com.android.settingslib.spa.gallery.preference.MainSwitchPreferencePageProvider
import com.android.settingslib.spa.gallery.preference.PreferenceMainPageProvider
import com.android.settingslib.spa.gallery.preference.PreferencePageProvider
import com.android.settingslib.spa.gallery.preference.SwitchPreferencePageProvider
import com.android.settingslib.spa.gallery.preference.TwoTargetSwitchPreferencePageProvider
import com.android.settingslib.spa.gallery.scaffold.SearchScaffoldPageProvider
import com.android.settingslib.spa.gallery.scaffold.SuwScaffoldPageProvider
import com.android.settingslib.spa.gallery.ui.CategoryPageProvider
import com.android.settingslib.spa.gallery.ui.CopyablePageProvider
import com.android.settingslib.spa.gallery.ui.SpinnerPageProvider
import com.android.settingslib.spa.slice.SpaSliceBroadcastReceiver
/**
* Enum to define all SPP name here.
* Since the SPP name would be used in log, DO NOT change it once it is set. One can still change
* the display name for better readability if necessary.
*/
enum class SettingsPageProviderEnum(val displayName: String) {
HOME("home"),
PREFERENCE("preference"),
ARGUMENT("argument"),
ITEM_LIST("itemList"),
ITEM_OP_PAGE("itemOp"),
// Add your SPPs
}
class GallerySpaEnvironment(context: Context) : SpaEnvironment(context) {
override val pageProviderRepository = lazy {
SettingsPageProviderRepository(
allPageProviders = listOf(
HomePageProvider,
PreferenceMainPageProvider,
PreferencePageProvider,
SwitchPreferencePageProvider,
MainSwitchPreferencePageProvider,
ListPreferencePageProvider,
TwoTargetSwitchPreferencePageProvider,
ArgumentPageProvider,
SliderPageProvider,
SpinnerPageProvider,
SettingsPagerPageProvider,
FooterPageProvider,
IllustrationPageProvider,
CategoryPageProvider,
ActionButtonPageProvider,
ProgressBarPageProvider,
LoadingBarPageProvider,
ChartPageProvider,
DialogMainPageProvider,
NavDialogProvider,
ItemListPageProvider,
ItemOperatePageProvider,
OperateListPageProvider,
EditorMainPageProvider,
SettingsOutlinedTextFieldPageProvider,
SettingsDropdownBoxPageProvider,
SettingsDropdownCheckBoxProvider,
SettingsTextFieldPasswordPageProvider,
SearchScaffoldPageProvider,
SuwScaffoldPageProvider,
CardPageProvider,
CopyablePageProvider,
),
rootPages = listOf(
HomePageProvider.createSettingsPage(),
)
)
}
override val logger = DebugLogger()
override val browseActivityClass = GalleryMainActivity::class.java
override val sliceBroadcastReceiverClass = SpaSliceBroadcastReceiver::class.java
// For debugging
override val searchProviderAuthorities = "com.android.spa.gallery.search.provider"
override val sliceProviderAuthorities = "com.android.spa.gallery.slice.provider"
}

View File

@@ -0,0 +1,75 @@
/*
* Copyright (C) 2022 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.settingslib.spa.gallery.button
import android.os.Bundle
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.outlined.Launch
import androidx.compose.material.icons.outlined.Delete
import androidx.compose.material.icons.outlined.WarningAmber
import androidx.compose.runtime.Composable
import androidx.compose.ui.tooling.preview.Preview
import com.android.settingslib.spa.framework.common.SettingsEntryBuilder
import com.android.settingslib.spa.framework.common.SettingsPageProvider
import com.android.settingslib.spa.framework.common.createSettingsPage
import com.android.settingslib.spa.framework.compose.navigator
import com.android.settingslib.spa.framework.theme.SettingsTheme
import com.android.settingslib.spa.widget.button.ActionButton
import com.android.settingslib.spa.widget.button.ActionButtons
import com.android.settingslib.spa.widget.preference.Preference
import com.android.settingslib.spa.widget.preference.PreferenceModel
import com.android.settingslib.spa.widget.scaffold.RegularScaffold
private const val TITLE = "Sample ActionButton"
object ActionButtonPageProvider : SettingsPageProvider {
override val name = "ActionButton"
override fun getTitle(arguments: Bundle?): String {
return TITLE
}
@Composable
override fun Page(arguments: Bundle?) {
RegularScaffold(title = TITLE) {
val actionButtons = listOf(
ActionButton(text = "Open", imageVector = Icons.AutoMirrored.Outlined.Launch) {},
ActionButton(text = "Uninstall", imageVector = Icons.Outlined.Delete) {},
ActionButton(text = "Force stop", imageVector = Icons.Outlined.WarningAmber) {},
)
ActionButtons(actionButtons)
}
}
fun buildInjectEntry(): SettingsEntryBuilder {
return SettingsEntryBuilder.createInject(owner = createSettingsPage())
.setUiLayoutFn {
Preference(object : PreferenceModel {
override val title = TITLE
override val onClick = navigator(name)
})
}
}
}
@Preview(showBackground = true)
@Composable
private fun ActionButtonPagePreview() {
SettingsTheme {
ActionButtonPageProvider.Page(null)
}
}

View File

@@ -0,0 +1,182 @@
/*
* 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.settingslib.spa.gallery.card
import android.os.Bundle
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Error
import androidx.compose.material.icons.outlined.PowerOff
import androidx.compose.material.icons.outlined.Shield
import androidx.compose.material.icons.outlined.WarningAmber
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import com.android.settingslib.spa.framework.common.SettingsEntryBuilder
import com.android.settingslib.spa.framework.common.SettingsPageProvider
import com.android.settingslib.spa.framework.common.createSettingsPage
import com.android.settingslib.spa.framework.compose.navigator
import com.android.settingslib.spa.framework.theme.SettingsDimension
import com.android.settingslib.spa.framework.theme.SettingsTheme
import com.android.settingslib.spa.gallery.R
import com.android.settingslib.spa.widget.card.CardButton
import com.android.settingslib.spa.widget.card.CardModel
import com.android.settingslib.spa.widget.card.SettingsCard
import com.android.settingslib.spa.widget.card.SettingsCardContent
import com.android.settingslib.spa.widget.card.SettingsCollapsibleCard
import com.android.settingslib.spa.widget.preference.Preference
import com.android.settingslib.spa.widget.preference.PreferenceModel
import com.android.settingslib.spa.widget.scaffold.RegularScaffold
object CardPageProvider : SettingsPageProvider {
override val name = "Card"
override fun getTitle(arguments: Bundle?) = TITLE
@Composable
override fun Page(arguments: Bundle?) {
RegularScaffold(title = TITLE) {
SettingsCardWithIcon()
SettingsCardWithoutIcon()
SampleSettingsCollapsibleCard()
SampleSettingsCardContent()
}
}
@Composable
private fun SettingsCardWithIcon() {
SettingsCard(
CardModel(
title = stringResource(R.string.sample_title),
text = stringResource(R.string.sample_text),
imageVector = Icons.Outlined.WarningAmber,
buttons = listOf(
CardButton(text = "Action") {},
),
tintColor = MaterialTheme.colorScheme.error,
containerColor = MaterialTheme.colorScheme.errorContainer,
)
)
}
@Composable
private fun SettingsCardWithoutIcon() {
val sampleTitle = stringResource(R.string.sample_title)
var title by remember { mutableStateOf(sampleTitle) }
SettingsCard(
CardModel(
title = title,
text = stringResource(R.string.sample_text),
) { title = "Clicked" }
)
}
@Composable
fun SampleSettingsCollapsibleCard() {
val context = LocalContext.current
var isVisible0 by rememberSaveable { mutableStateOf(true) }
var isVisible1 by rememberSaveable { mutableStateOf(true) }
val cards = remember {
mutableStateListOf(
CardModel(
title = context.getString(R.string.sample_title),
text = context.getString(R.string.sample_text),
imageVector = Icons.Outlined.PowerOff,
isVisible = { isVisible0 },
onDismiss = { isVisible0 = false },
buttons = listOf(
CardButton(text = "Action") {},
),
),
CardModel(
title = context.getString(R.string.sample_title),
text = context.getString(R.string.sample_text),
imageVector = Icons.Outlined.Shield,
isVisible = { isVisible1 },
onDismiss = { isVisible1 = false },
buttons = listOf(
CardButton(text = "Action") {},
),
)
)
}
SettingsCollapsibleCard(
title = "More alerts",
imageVector = Icons.Outlined.Error,
models = cards.toList()
)
}
@Composable
fun SampleSettingsCardContent() {
SettingsCard {
SettingsCardContent {
Box(
Modifier
.fillMaxWidth()
.clickable { }
.padding(SettingsDimension.dialogItemPadding),
) {
Text(text = "Abc")
}
}
SettingsCardContent {
Box(
Modifier
.fillMaxWidth()
.clickable { }
.padding(SettingsDimension.dialogItemPadding),
) {
Text(text = "123")
}
}
}
}
fun buildInjectEntry(): SettingsEntryBuilder {
return SettingsEntryBuilder.createInject(owner = createSettingsPage())
.setUiLayoutFn {
Preference(object : PreferenceModel {
override val title = TITLE
override val onClick = navigator(name)
})
}
}
private const val TITLE = "Sample Card"
}
@Preview
@Composable
private fun CardPagePreview() {
SettingsTheme {
CardPageProvider.Page(null)
}
}

View File

@@ -0,0 +1,55 @@
/*
* 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.settingslib.spa.gallery.chart
import com.android.settingslib.spa.framework.common.SettingsEntryBuilder
import com.android.settingslib.spa.framework.common.SettingsPage
import com.android.settingslib.spa.widget.chart.BarChart
import com.android.settingslib.spa.widget.chart.BarChartData
import com.android.settingslib.spa.widget.chart.BarChartModel
import com.android.settingslib.spa.widget.chart.ColorPalette
import com.android.settingslib.spa.widget.preference.Preference
import com.android.settingslib.spa.widget.preference.PreferenceModel
import com.github.mikephil.charting.formatter.IAxisValueFormatter
fun createBarChartEntry(owner: SettingsPage) = SettingsEntryBuilder.create("Bar Chart", owner)
.setUiLayoutFn {
Preference(object : PreferenceModel {
override val title = "Bar Chart"
})
BarChart(
barChartModel = object : BarChartModel {
override val chartDataList = listOf(
BarChartData(x = 0f, y = listOf(12f, 2f)),
BarChartData(x = 1f, y = listOf(5f, 1f)),
BarChartData(x = 2f, y = listOf(21f, 2f)),
BarChartData(x = 3f, y = listOf(5f, 1f)),
BarChartData(x = 4f, y = listOf(10f, 0f)),
BarChartData(x = 5f, y = listOf(9f, 1f)),
BarChartData(x = 6f, y = listOf(1f, 1f)),
)
override val colors = listOf(ColorPalette.green, ColorPalette.yellow)
override val xValueFormatter = IAxisValueFormatter { value, _ ->
"4/${value.toInt() + 1}"
}
override val yValueFormatter = IAxisValueFormatter { value, _ ->
"${value.toInt()}m"
}
override val yAxisMaxValue = 30f
}
)
}.build()

View File

@@ -0,0 +1,123 @@
/*
* 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.settingslib.spa.gallery.chart
import android.os.Bundle
import androidx.compose.runtime.Composable
import androidx.compose.ui.tooling.preview.Preview
import com.android.settingslib.spa.framework.common.SettingsEntry
import com.android.settingslib.spa.framework.common.SettingsEntryBuilder
import com.android.settingslib.spa.framework.common.SettingsPageProvider
import com.android.settingslib.spa.framework.common.createSettingsPage
import com.android.settingslib.spa.framework.compose.navigator
import com.android.settingslib.spa.framework.theme.SettingsTheme
import com.android.settingslib.spa.widget.chart.LineChart
import com.android.settingslib.spa.widget.chart.LineChartData
import com.android.settingslib.spa.widget.chart.LineChartModel
import com.android.settingslib.spa.widget.chart.PieChart
import com.android.settingslib.spa.widget.chart.PieChartData
import com.android.settingslib.spa.widget.chart.PieChartModel
import com.android.settingslib.spa.widget.preference.Preference
import com.android.settingslib.spa.widget.preference.PreferenceModel
import com.github.mikephil.charting.formatter.IAxisValueFormatter
import java.text.NumberFormat
private enum class WeekDay(val num: Int) {
Sun(0), Mon(1), Tue(2), Wed(3), Thu(4), Fri(5), Sat(6),
}
private const val TITLE = "Sample Chart"
object ChartPageProvider : SettingsPageProvider {
override val name = "Chart"
private val owner = createSettingsPage()
override fun getTitle(arguments: Bundle?): String {
return TITLE
}
override fun buildEntry(arguments: Bundle?): List<SettingsEntry> {
val entryList = mutableListOf<SettingsEntry>()
entryList.add(
SettingsEntryBuilder.create("Line Chart", owner)
.setUiLayoutFn {
Preference(object : PreferenceModel {
override val title = "Line Chart"
})
LineChart(
lineChartModel = object : LineChartModel {
override val chartDataList = listOf(
LineChartData(x = 0f, y = 0f),
LineChartData(x = 1f, y = 0.1f),
LineChartData(x = 2f, y = 0.2f),
LineChartData(x = 3f, y = 0.6f),
LineChartData(x = 4f, y = 0.9f),
LineChartData(x = 5f, y = 1.0f),
LineChartData(x = 6f, y = 0.8f),
)
override val xValueFormatter =
IAxisValueFormatter { value, _ ->
"${WeekDay.values()[value.toInt()]}"
}
override val yValueFormatter =
IAxisValueFormatter { value, _ ->
NumberFormat.getPercentInstance().format(value)
}
}
)
}.build()
)
entryList.add(createBarChartEntry(owner))
entryList.add(
SettingsEntryBuilder.create("Pie Chart", owner)
.setUiLayoutFn {
Preference(object : PreferenceModel {
override val title = "Pie Chart"
})
PieChart(
pieChartModel = object : PieChartModel {
override val chartDataList = listOf(
PieChartData(label = "Settings", value = 20f),
PieChartData(label = "Chrome", value = 5f),
PieChartData(label = "Gmail", value = 3f),
)
override val centerText = "Today"
}
)
}.build()
)
return entryList
}
fun buildInjectEntry(): SettingsEntryBuilder {
return SettingsEntryBuilder.createInject(owner)
.setUiLayoutFn {
Preference(object : PreferenceModel {
override val title = TITLE
override val onClick = navigator(name)
})
}
}
}
@Preview(showBackground = true)
@Composable
private fun ChartPagePreview() {
SettingsTheme {
ChartPageProvider.Page(null)
}
}

View File

@@ -0,0 +1,67 @@
/*
* Copyright (C) 2022 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.settingslib.spa.gallery.dialog
import android.os.Bundle
import androidx.compose.material3.Text
import com.android.settingslib.spa.framework.common.SettingsEntry
import com.android.settingslib.spa.framework.common.SettingsEntryBuilder
import com.android.settingslib.spa.framework.common.SettingsPageProvider
import com.android.settingslib.spa.framework.common.createSettingsPage
import com.android.settingslib.spa.framework.compose.navigator
import com.android.settingslib.spa.widget.dialog.AlertDialogButton
import com.android.settingslib.spa.widget.dialog.rememberAlertDialogPresenter
import com.android.settingslib.spa.widget.preference.Preference
import com.android.settingslib.spa.widget.preference.PreferenceModel
private const val TITLE = "Category: Dialog"
object DialogMainPageProvider : SettingsPageProvider {
override val name = "DialogMain"
private val owner = createSettingsPage()
override fun buildEntry(arguments: Bundle?): List<SettingsEntry> = listOf(
SettingsEntryBuilder.create("AlertDialog", owner).setUiLayoutFn {
val alertDialogPresenter = rememberAlertDialogPresenter(
confirmButton = AlertDialogButton("Ok"),
dismissButton = AlertDialogButton("Cancel"),
title = "Title",
text = { Text("Text") },
)
Preference(object : PreferenceModel {
override val title = "Show AlertDialog"
override val onClick = alertDialogPresenter::open
})
}.build(),
SettingsEntryBuilder.create("NavDialog", owner).setUiLayoutFn {
Preference(object : PreferenceModel {
override val title = "Navigate to Dialog"
override val onClick = navigator(route = NavDialogProvider.name)
})
}.build(),
)
fun buildInjectEntry() = SettingsEntryBuilder.createInject(owner)
.setUiLayoutFn {
Preference(object : PreferenceModel {
override val title = TITLE
override val onClick = navigator(name)
})
}
override fun getTitle(arguments: Bundle?) = TITLE
}

View File

@@ -0,0 +1,32 @@
/*
* Copyright (C) 2024 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.settingslib.spa.gallery.dialog
import android.os.Bundle
import androidx.compose.runtime.Composable
import com.android.settingslib.spa.framework.common.SettingsPageProvider
import com.android.settingslib.spa.widget.dialog.SettingsDialogCard
object NavDialogProvider : SettingsPageProvider {
override val name = "NavDialog"
override val navType = SettingsPageProvider.NavType.Dialog
@Composable
override fun Page(arguments: Bundle?) {
SettingsDialogCard("Example Nav Dialog") {}
}
}

View File

@@ -0,0 +1,60 @@
/*
* 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.settingslib.spa.gallery.editor
import android.os.Bundle
import com.android.settingslib.spa.framework.common.SettingsEntry
import com.android.settingslib.spa.framework.common.SettingsEntryBuilder
import com.android.settingslib.spa.framework.common.SettingsPageProvider
import com.android.settingslib.spa.framework.common.createSettingsPage
import com.android.settingslib.spa.framework.compose.navigator
import com.android.settingslib.spa.widget.preference.Preference
import com.android.settingslib.spa.widget.preference.PreferenceModel
private const val TITLE = "Category: Editor"
object EditorMainPageProvider : SettingsPageProvider {
override val name = "EditorMain"
private val owner = createSettingsPage()
override fun buildEntry(arguments: Bundle?): List<SettingsEntry> {
return listOf(
SettingsOutlinedTextFieldPageProvider.buildInjectEntry().setLink(fromPage = owner)
.build(),
SettingsDropdownBoxPageProvider.buildInjectEntry().setLink(fromPage = owner)
.build(),
SettingsDropdownCheckBoxProvider.buildInjectEntry().setLink(fromPage = owner)
.build(),
SettingsTextFieldPasswordPageProvider.buildInjectEntry().setLink(fromPage = owner)
.build(),
)
}
fun buildInjectEntry(): SettingsEntryBuilder {
return SettingsEntryBuilder.createInject(owner = owner)
.setUiLayoutFn {
Preference(object : PreferenceModel {
override val title = TITLE
override val onClick = navigator(name)
})
}
}
override fun getTitle(arguments: Bundle?): String {
return TITLE
}
}

View File

@@ -0,0 +1,102 @@
/*
* 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.settingslib.spa.gallery.editor
import android.os.Bundle
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.tooling.preview.Preview
import com.android.settingslib.spa.framework.common.SettingsEntryBuilder
import com.android.settingslib.spa.framework.common.SettingsPageProvider
import com.android.settingslib.spa.framework.common.createSettingsPage
import com.android.settingslib.spa.framework.compose.navigator
import com.android.settingslib.spa.framework.theme.SettingsTheme
import com.android.settingslib.spa.widget.editor.SettingsDropdownBox
import com.android.settingslib.spa.widget.preference.Preference
import com.android.settingslib.spa.widget.preference.PreferenceModel
import com.android.settingslib.spa.widget.scaffold.RegularScaffold
private const val TITLE = "Sample SettingsDropdownBox"
object SettingsDropdownBoxPageProvider : SettingsPageProvider {
override val name = "SettingsDropdownBox"
override fun getTitle(arguments: Bundle?): String {
return TITLE
}
@Composable
override fun Page(arguments: Bundle?) {
RegularScaffold(title = TITLE) {
Regular()
NotEnabled()
Empty()
}
}
@Composable
private fun Regular() {
var selectedItem by remember { mutableIntStateOf(-1) }
SettingsDropdownBox(
label = "SettingsDropdownBox",
options = listOf("item1", "item2", "item3"),
selectedOptionIndex = selectedItem,
) { selectedItem = it }
}
@Composable
private fun NotEnabled() {
var selectedItem by remember { mutableIntStateOf(0) }
SettingsDropdownBox(
label = "Not enabled",
options = listOf("item1", "item2", "item3"),
enabled = false,
selectedOptionIndex = selectedItem,
) { selectedItem = it }
}
@Composable
private fun Empty() {
var selectedItem by remember { mutableIntStateOf(-1) }
SettingsDropdownBox(
label = "Empty",
options = emptyList(),
selectedOptionIndex = selectedItem,
) { selectedItem = it }
}
fun buildInjectEntry(): SettingsEntryBuilder {
return SettingsEntryBuilder.createInject(owner = createSettingsPage())
.setUiLayoutFn {
Preference(object : PreferenceModel {
override val title = TITLE
override val onClick = navigator(name)
})
}
}
}
@Preview(showBackground = true)
@Composable
private fun SettingsDropdownBoxPagePreview() {
SettingsTheme {
SettingsDropdownBoxPageProvider.Page(null)
}
}

View File

@@ -0,0 +1,134 @@
/*
* Copyright (C) 2024 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.settingslib.spa.gallery.editor
import android.os.Bundle
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.tooling.preview.Preview
import com.android.settingslib.spa.framework.common.SettingsEntryBuilder
import com.android.settingslib.spa.framework.common.SettingsPageProvider
import com.android.settingslib.spa.framework.common.createSettingsPage
import com.android.settingslib.spa.framework.compose.navigator
import com.android.settingslib.spa.framework.theme.SettingsTheme
import com.android.settingslib.spa.widget.editor.SettingsDropdownCheckBox
import com.android.settingslib.spa.widget.editor.SettingsDropdownCheckOption
import com.android.settingslib.spa.widget.preference.Preference
import com.android.settingslib.spa.widget.preference.PreferenceModel
import com.android.settingslib.spa.widget.scaffold.RegularScaffold
private const val TITLE = "Sample SettingsDropdownCheckBox"
object SettingsDropdownCheckBoxProvider : SettingsPageProvider {
override val name = "SettingsDropdownCheckBox"
override fun getTitle(arguments: Bundle?): String {
return TITLE
}
@Composable
override fun Page(arguments: Bundle?) {
RegularScaffold(title = TITLE) {
SettingsDropdownCheckBox(
label = "SettingsDropdownCheckBox",
options = remember {
listOf(
SettingsDropdownCheckOption("Item 1"),
SettingsDropdownCheckOption("Item 2"),
SettingsDropdownCheckOption("Item 3"),
)
},
)
SettingsDropdownCheckBox(
label = "Empty list",
options = emptyList(),
)
SettingsDropdownCheckBox(
label = "Disabled",
options = remember {
listOf(
SettingsDropdownCheckOption("Item 1", selected = mutableStateOf(true)),
SettingsDropdownCheckOption("Item 2"),
SettingsDropdownCheckOption("Item 3"),
)
},
enabled = false,
)
SettingsDropdownCheckBox(
label = "With disabled item",
options = remember {
listOf(
SettingsDropdownCheckOption("Item 1"),
SettingsDropdownCheckOption("Item 2"),
SettingsDropdownCheckOption(
text = "Disabled item 1",
changeable = false,
selected = mutableStateOf(true),
),
SettingsDropdownCheckOption("Disabled item 2", changeable = false),
)
},
)
SettingsDropdownCheckBox(
label = "With select all",
options = remember {
listOf(
SettingsDropdownCheckOption("All", isSelectAll = true),
SettingsDropdownCheckOption("Item 1"),
SettingsDropdownCheckOption("Item 2"),
SettingsDropdownCheckOption("Item 3"),
)
},
)
SettingsDropdownCheckBox(
label = "With disabled item and select all",
options =
remember {
listOf(
SettingsDropdownCheckOption("All", isSelectAll = true, changeable = false),
SettingsDropdownCheckOption("Item 1"),
SettingsDropdownCheckOption("Item 2"),
SettingsDropdownCheckOption(
text = "Disabled item 1",
changeable = false,
selected = mutableStateOf(true),
),
SettingsDropdownCheckOption("Disabled item 2", changeable = false),
)
},
)
}
}
fun buildInjectEntry(): SettingsEntryBuilder {
return SettingsEntryBuilder.createInject(owner = createSettingsPage()).setUiLayoutFn {
Preference(object : PreferenceModel {
override val title = TITLE
override val onClick = navigator(name)
})
}
}
}
@Preview(showBackground = true)
@Composable
private fun SettingsDropdownCheckBoxPagePreview() {
SettingsTheme {
SettingsDropdownCheckBoxProvider.Page(null)
}
}

View File

@@ -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.settingslib.spa.gallery.editor
import android.os.Bundle
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.tooling.preview.Preview
import com.android.settingslib.spa.framework.common.SettingsEntryBuilder
import com.android.settingslib.spa.framework.common.SettingsPageProvider
import com.android.settingslib.spa.framework.common.createSettingsPage
import com.android.settingslib.spa.framework.compose.navigator
import com.android.settingslib.spa.framework.theme.SettingsTheme
import com.android.settingslib.spa.widget.editor.SettingsOutlinedTextField
import com.android.settingslib.spa.widget.preference.Preference
import com.android.settingslib.spa.widget.preference.PreferenceModel
import com.android.settingslib.spa.widget.scaffold.RegularScaffold
private const val TITLE = "Sample SettingsOutlinedTextField"
object SettingsOutlinedTextFieldPageProvider : SettingsPageProvider {
override val name = "SettingsOutlinedTextField"
override fun getTitle(arguments: Bundle?): String {
return TITLE
}
@Composable
override fun Page(arguments: Bundle?) {
var value by remember { mutableStateOf("Enabled Value") }
RegularScaffold(title = TITLE) {
SettingsOutlinedTextField(
value = value,
label = "OutlinedTextField Enabled",
enabled = true,
onTextChange = {value = it})
}
}
fun buildInjectEntry(): SettingsEntryBuilder {
return SettingsEntryBuilder.createInject(owner = createSettingsPage())
.setUiLayoutFn {
Preference(object : PreferenceModel {
override val title = TITLE
override val onClick = navigator(name)
})
}
}
}
@Preview(showBackground = true)
@Composable
private fun SettingsOutlinedTextFieldPagePreview() {
SettingsTheme {
SettingsOutlinedTextFieldPageProvider.Page(null)
}
}

View File

@@ -0,0 +1,73 @@
/*
* 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.settingslib.spa.gallery.editor
import android.os.Bundle
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.tooling.preview.Preview
import com.android.settingslib.spa.framework.common.SettingsEntryBuilder
import com.android.settingslib.spa.framework.common.SettingsPageProvider
import com.android.settingslib.spa.framework.common.createSettingsPage
import com.android.settingslib.spa.framework.compose.navigator
import com.android.settingslib.spa.framework.theme.SettingsTheme
import com.android.settingslib.spa.widget.editor.SettingsTextFieldPassword
import com.android.settingslib.spa.widget.preference.Preference
import com.android.settingslib.spa.widget.preference.PreferenceModel
import com.android.settingslib.spa.widget.scaffold.RegularScaffold
private const val TITLE = "Sample SettingsTextFieldPassword"
object SettingsTextFieldPasswordPageProvider : SettingsPageProvider {
override val name = "SettingsTextFieldPassword"
override fun getTitle(arguments: Bundle?): String {
return TITLE
}
@Composable
override fun Page(arguments: Bundle?) {
var value by remember { mutableStateOf("value") }
RegularScaffold(title = TITLE) {
SettingsTextFieldPassword(
value = value,
label = "label",
onTextChange = { value = it })
}
}
fun buildInjectEntry(): SettingsEntryBuilder {
return SettingsEntryBuilder.createInject(owner = createSettingsPage())
.setUiLayoutFn {
Preference(object : PreferenceModel {
override val title = TITLE
override val onClick = navigator(name)
})
}
}
}
@Preview(showBackground = true)
@Composable
private fun SettingsTextFieldPasswordPagePreview() {
SettingsTheme {
SettingsTextFieldPasswordPageProvider.Page(null)
}
}

View File

@@ -0,0 +1,108 @@
/*
* Copyright (C) 2022 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.settingslib.spa.gallery.home
import android.os.Bundle
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.tooling.preview.Preview
import com.android.settingslib.spa.framework.common.SettingsEntry
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.theme.SettingsTheme
import com.android.settingslib.spa.gallery.R
import com.android.settingslib.spa.gallery.SettingsPageProviderEnum
import com.android.settingslib.spa.gallery.button.ActionButtonPageProvider
import com.android.settingslib.spa.gallery.card.CardPageProvider
import com.android.settingslib.spa.gallery.chart.ChartPageProvider
import com.android.settingslib.spa.gallery.dialog.DialogMainPageProvider
import com.android.settingslib.spa.gallery.editor.EditorMainPageProvider
import com.android.settingslib.spa.gallery.itemList.OperateListPageProvider
import com.android.settingslib.spa.gallery.page.ArgumentPageModel
import com.android.settingslib.spa.gallery.page.ArgumentPageProvider
import com.android.settingslib.spa.gallery.page.FooterPageProvider
import com.android.settingslib.spa.gallery.page.IllustrationPageProvider
import com.android.settingslib.spa.gallery.page.LoadingBarPageProvider
import com.android.settingslib.spa.gallery.page.ProgressBarPageProvider
import com.android.settingslib.spa.gallery.page.SettingsPagerPageProvider
import com.android.settingslib.spa.gallery.page.SliderPageProvider
import com.android.settingslib.spa.gallery.preference.PreferenceMainPageProvider
import com.android.settingslib.spa.gallery.scaffold.SearchScaffoldPageProvider
import com.android.settingslib.spa.gallery.scaffold.SuwScaffoldPageProvider
import com.android.settingslib.spa.gallery.ui.CategoryPageProvider
import com.android.settingslib.spa.gallery.ui.CopyablePageProvider
import com.android.settingslib.spa.gallery.ui.SpinnerPageProvider
import com.android.settingslib.spa.widget.scaffold.HomeScaffold
object HomePageProvider : SettingsPageProvider {
override val name = SettingsPageProviderEnum.HOME.name
override val displayName = SettingsPageProviderEnum.HOME.displayName
private val owner = createSettingsPage()
override fun buildEntry(arguments: Bundle?): List<SettingsEntry> {
return listOf(
PreferenceMainPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
OperateListPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
ArgumentPageProvider.buildInjectEntry("foo")!!.setLink(fromPage = owner).build(),
SearchScaffoldPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
SuwScaffoldPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
SliderPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
SpinnerPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
SettingsPagerPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
FooterPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
IllustrationPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
CategoryPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
ActionButtonPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
ProgressBarPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
LoadingBarPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
ChartPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
DialogMainPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
EditorMainPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
CardPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
CopyablePageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
)
}
override fun getTitle(arguments: Bundle?): String {
return SpaEnvironmentFactory.instance.appContext.getString(R.string.app_name)
}
@Composable
override fun Page(arguments: Bundle?) {
val title = remember { getTitle(arguments) }
val entries = remember { buildEntry(arguments) }
HomeScaffold(title) {
for (entry in entries) {
if (entry.owner.isCreateBy(SettingsPageProviderEnum.ARGUMENT.name)) {
entry.UiLayout(ArgumentPageModel.buildArgument(intParam = 0))
} else {
entry.UiLayout()
}
}
}
}
}
@Preview(showBackground = true)
@Composable
private fun HomeScreenPreview() {
SpaEnvironmentFactory.resetForPreview()
SettingsTheme {
HomePageProvider.Page(null)
}
}

View File

@@ -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.settingslib.spa.gallery.itemList
import android.os.Bundle
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.core.os.bundleOf
import androidx.navigation.NavType
import androidx.navigation.navArgument
import com.android.settingslib.spa.framework.common.EntrySearchData
import com.android.settingslib.spa.framework.common.SettingsEntry
import com.android.settingslib.spa.framework.common.SettingsEntryBuilder
import com.android.settingslib.spa.framework.common.SettingsPageProvider
import com.android.settingslib.spa.framework.common.createSettingsPage
import com.android.settingslib.spa.framework.compose.navigator
import com.android.settingslib.spa.framework.util.getStringArg
import com.android.settingslib.spa.framework.util.navLink
import com.android.settingslib.spa.gallery.SettingsPageProviderEnum
import com.android.settingslib.spa.widget.preference.Preference
import com.android.settingslib.spa.widget.preference.PreferenceModel
import com.android.settingslib.spa.widget.scaffold.RegularScaffold
private const val OPERATOR_PARAM_NAME = "opParam"
object ItemListPageProvider : SettingsPageProvider {
override val name = SettingsPageProviderEnum.ITEM_LIST.name
override val displayName = SettingsPageProviderEnum.ITEM_LIST.displayName
override val parameter = listOf(
navArgument(OPERATOR_PARAM_NAME) { type = NavType.StringType },
)
override fun getTitle(arguments: Bundle?): String {
val operation = parameter.getStringArg(OPERATOR_PARAM_NAME, arguments) ?: "NULL"
return "Operation: $operation"
}
override fun buildEntry(arguments: Bundle?): List<SettingsEntry> {
if (!ItemOperatePageProvider.isValidArgs(arguments)) return emptyList()
val operation = parameter.getStringArg(OPERATOR_PARAM_NAME, arguments)!!
val owner = createSettingsPage(arguments)
return listOf(
ItemOperatePageProvider.buildInjectEntry(operation)!!.setLink(fromPage = owner).build(),
)
}
fun buildInjectEntry(opParam: String): SettingsEntryBuilder? {
val arguments = bundleOf(OPERATOR_PARAM_NAME to opParam)
if (!ItemOperatePageProvider.isValidArgs(arguments)) return null
return SettingsEntryBuilder.createInject(
owner = createSettingsPage(arguments),
label = "ItemList_$opParam",
).setUiLayoutFn {
Preference(
object : PreferenceModel {
override val title = opParam
override val onClick = navigator(
SettingsPageProviderEnum.ITEM_LIST.name + parameter.navLink(it)
)
}
)
}.setSearchDataFn {
EntrySearchData(title = "Operation: $opParam")
}
}
@Composable
override fun Page(arguments: Bundle?) {
val title = remember { getTitle(arguments) }
val entries = remember { buildEntry(arguments) }
val itemList = remember {
// Add logic to get item List during runtime.
listOf("itemFoo", "itemBar", "itemToy")
}
RegularScaffold(title) {
for (item in itemList) {
val rtArgs = ItemOperatePageProvider.genRuntimeArguments(item)
for (entry in entries) {
entry.UiLayout(rtArgs)
}
}
}
}
}

View File

@@ -0,0 +1,127 @@
/*
* 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.settingslib.spa.gallery.itemList
import android.os.Bundle
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.core.os.bundleOf
import androidx.navigation.NavType
import androidx.navigation.navArgument
import com.android.settingslib.spa.framework.common.SettingsEntry
import com.android.settingslib.spa.framework.common.SettingsEntryBuilder
import com.android.settingslib.spa.framework.common.SettingsPageProvider
import com.android.settingslib.spa.framework.common.createSettingsPage
import com.android.settingslib.spa.framework.compose.navigator
import com.android.settingslib.spa.framework.util.getStringArg
import com.android.settingslib.spa.framework.util.navLink
import com.android.settingslib.spa.gallery.SettingsPageProviderEnum
import com.android.settingslib.spa.widget.preference.Preference
import com.android.settingslib.spa.widget.preference.PreferenceModel
import com.android.settingslib.spa.widget.preference.SwitchPreference
import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel
private const val OPERATOR_PARAM_NAME = "opParam"
private const val ITEM_NAME_PARAM_NAME = "rt_nameParam"
private val ALLOWED_OPERATOR_LIST = listOf("opDnD", "opPiP", "opInstall", "opConnect")
object ItemOperatePageProvider : SettingsPageProvider {
override val name = SettingsPageProviderEnum.ITEM_OP_PAGE.name
override val displayName = SettingsPageProviderEnum.ITEM_OP_PAGE.displayName
override val parameter = listOf(
navArgument(OPERATOR_PARAM_NAME) { type = NavType.StringType },
navArgument(ITEM_NAME_PARAM_NAME) { type = NavType.StringType },
)
override fun getTitle(arguments: Bundle?): String {
// Operation name is not a runtime parameter, which should always available
val operation = parameter.getStringArg(OPERATOR_PARAM_NAME, arguments) ?: "opInValid"
// Item name is a runtime parameter, which could be missing
val itemName = parameter.getStringArg(ITEM_NAME_PARAM_NAME, arguments) ?: "[unset]"
return "$operation on $itemName"
}
override fun buildEntry(arguments: Bundle?): List<SettingsEntry> {
if (!isValidArgs(arguments)) return emptyList()
val owner = createSettingsPage(arguments)
val entryList = mutableListOf<SettingsEntry>()
entryList.add(
SettingsEntryBuilder.create("ItemName", owner)
.setUiLayoutFn {
// Item name is a runtime parameter, which needs to be read inside UiLayoutFn
val itemName = parameter.getStringArg(ITEM_NAME_PARAM_NAME, it) ?: "NULL"
Preference(
object : PreferenceModel {
override val title = "Item $itemName"
}
)
}.build()
)
// Operation name is not a runtime parameter, which can be read outside.
val opName = parameter.getStringArg(OPERATOR_PARAM_NAME, arguments)!!
entryList.add(
SettingsEntryBuilder.create("ItemOp", owner)
.setUiLayoutFn {
var checked by rememberSaveable { mutableStateOf(false) }
SwitchPreference(remember {
object : SwitchPreferenceModel {
override val title = "Item operation: $opName"
override val checked = { checked }
override val onCheckedChange =
{ newChecked: Boolean -> checked = newChecked }
}
})
}.build(),
)
return entryList
}
fun buildInjectEntry(opParam: String): SettingsEntryBuilder? {
val arguments = bundleOf(OPERATOR_PARAM_NAME to opParam)
if (!isValidArgs(arguments)) return null
return SettingsEntryBuilder.createInject(
owner = createSettingsPage(arguments),
label = "ItemOp_$opParam",
).setUiLayoutFn {
// Item name is a runtime parameter, which needs to be read inside UiLayoutFn
val itemName = parameter.getStringArg(ITEM_NAME_PARAM_NAME, it) ?: "NULL"
Preference(
object : PreferenceModel {
override val title = "item: $itemName"
override val onClick = navigator(
SettingsPageProviderEnum.ITEM_OP_PAGE.name + parameter.navLink(it)
)
}
)
}
}
fun isValidArgs(arguments: Bundle?): Boolean {
val opParam = parameter.getStringArg(OPERATOR_PARAM_NAME, arguments)
return (opParam != null && ALLOWED_OPERATOR_LIST.contains(opParam))
}
fun genRuntimeArguments(itemName: String): Bundle {
return bundleOf(ITEM_NAME_PARAM_NAME to itemName)
}
}

View File

@@ -0,0 +1,56 @@
/*
* 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.settingslib.spa.gallery.itemList
import android.os.Bundle
import com.android.settingslib.spa.framework.common.SettingsEntry
import com.android.settingslib.spa.framework.common.SettingsEntryBuilder
import com.android.settingslib.spa.framework.common.SettingsPageProvider
import com.android.settingslib.spa.framework.common.createSettingsPage
import com.android.settingslib.spa.framework.compose.navigator
import com.android.settingslib.spa.widget.preference.Preference
import com.android.settingslib.spa.widget.preference.PreferenceModel
private const val TITLE = "Operate List Main"
object OperateListPageProvider : SettingsPageProvider {
override val name = "OpList"
private val owner = createSettingsPage()
override fun buildEntry(arguments: Bundle?): List<SettingsEntry> {
return listOf(
ItemListPageProvider.buildInjectEntry("opPiP")!!.setLink(fromPage = owner).build(),
ItemListPageProvider.buildInjectEntry("opInstall")!!.setLink(fromPage = owner).build(),
ItemListPageProvider.buildInjectEntry("opDnD")!!.setLink(fromPage = owner).build(),
ItemListPageProvider.buildInjectEntry("opConnect")!!.setLink(fromPage = owner).build(),
)
}
override fun getTitle(arguments: Bundle?): String {
return TITLE
}
fun buildInjectEntry(): SettingsEntryBuilder {
return SettingsEntryBuilder.createInject(owner = owner)
.setUiLayoutFn {
Preference(object : PreferenceModel {
override val title = TITLE
override val onClick = navigator(name)
})
}
}
}

View File

@@ -0,0 +1,129 @@
/*
* Copyright (C) 2022 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.settingslib.spa.gallery.page
import android.os.Bundle
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.tooling.preview.Preview
import com.android.settingslib.spa.framework.common.SettingsEntry
import com.android.settingslib.spa.framework.common.SettingsEntryBuilder
import com.android.settingslib.spa.framework.common.SettingsPage
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.theme.SettingsTheme
import com.android.settingslib.spa.gallery.SettingsPageProviderEnum
import com.android.settingslib.spa.widget.preference.Preference
import com.android.settingslib.spa.widget.scaffold.RegularScaffold
object ArgumentPageProvider : SettingsPageProvider {
// Defines all entry name in this page.
// Note that entry name would be used in log. DO NOT change it once it is set.
// One can still change the display name for better readability if necessary.
private enum class EntryEnum(val displayName: String) {
STRING_PARAM("string_param"),
INT_PARAM("int_param"),
}
private fun createEntry(owner: SettingsPage, entry: EntryEnum): SettingsEntryBuilder {
return SettingsEntryBuilder.create(owner, entry.name, entry.displayName)
}
override val name = SettingsPageProviderEnum.ARGUMENT.name
override val displayName = SettingsPageProviderEnum.ARGUMENT.displayName
override val parameter = ArgumentPageModel.parameter
override fun buildEntry(arguments: Bundle?): List<SettingsEntry> {
if (!ArgumentPageModel.isValidArgument(arguments)) return emptyList()
val owner = createSettingsPage(arguments)
val entryList = mutableListOf<SettingsEntry>()
entryList.add(
createEntry(owner, EntryEnum.STRING_PARAM)
// Set attributes
.setIsSearchDataDynamic(true)
.setSearchDataFn { ArgumentPageModel.genStringParamSearchData() }
.setUiLayoutFn {
// Set ui rendering
Preference(ArgumentPageModel.create(it).genStringParamPreferenceModel())
}.build()
)
entryList.add(
createEntry(owner, EntryEnum.INT_PARAM)
// Set attributes
.setIsSearchDataDynamic(true)
.setSearchDataFn { ArgumentPageModel.genIntParamSearchData() }
.setUiLayoutFn {
// Set ui rendering
Preference(ArgumentPageModel.create(it).genIntParamPreferenceModel())
}.build()
)
entryList.add(buildInjectEntry("foo")!!.setLink(fromPage = owner).build())
entryList.add(buildInjectEntry("bar")!!.setLink(fromPage = owner).build())
return entryList
}
fun buildInjectEntry(stringParam: String): SettingsEntryBuilder? {
val arguments = ArgumentPageModel.buildArgument(stringParam)
if (!ArgumentPageModel.isValidArgument(arguments)) return null
return SettingsEntryBuilder.createInject(
owner = createSettingsPage(arguments),
label = "${name}_$stringParam",
)
.setSearchDataFn { ArgumentPageModel.genInjectSearchData() }
.setUiLayoutFn {
// Set ui rendering
Preference(ArgumentPageModel.create(it).genInjectPreferenceModel())
}
}
override fun getTitle(arguments: Bundle?): String {
return ArgumentPageModel.genPageTitle()
}
@Composable
override fun Page(arguments: Bundle?) {
val title = remember { getTitle(arguments) }
val entries = remember { buildEntry(arguments) }
val rtArgNext = remember { ArgumentPageModel.buildNextArgument(arguments) }
RegularScaffold(title) {
for (entry in entries) {
if (entry.toPage != null) {
entry.UiLayout(rtArgNext)
} else {
entry.UiLayout()
}
}
}
}
}
@Preview(showBackground = true)
@Composable
private fun ArgumentPagePreview() {
SpaEnvironmentFactory.resetForPreview()
SettingsTheme {
ArgumentPageProvider.Page(
ArgumentPageModel.buildArgument(stringParam = "foo", intParam = 0)
)
}
}

View File

@@ -0,0 +1,138 @@
/*
* 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.settingslib.spa.gallery.page
import android.os.Bundle
import androidx.compose.runtime.Composable
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavType
import androidx.navigation.navArgument
import com.android.settingslib.spa.framework.common.EntrySearchData
import com.android.settingslib.spa.framework.common.PageModel
import com.android.settingslib.spa.framework.common.SpaEnvironmentFactory
import com.android.settingslib.spa.framework.compose.navigator
import com.android.settingslib.spa.framework.util.getIntArg
import com.android.settingslib.spa.framework.util.getStringArg
import com.android.settingslib.spa.framework.util.navLink
import com.android.settingslib.spa.gallery.SettingsPageProviderEnum
import com.android.settingslib.spa.widget.preference.PreferenceModel
private const val TAG = "ArgumentPageModel"
// Defines all the resources for this page.
// In real Settings App, resources data is defined in xml, rather than SPP.
private const val PAGE_TITLE = "Sample page with arguments"
private const val STRING_PARAM_TITLE = "String param value"
private const val INT_PARAM_TITLE = "Int param value"
private const val STRING_PARAM_NAME = "stringParam"
private const val INT_PARAM_NAME = "rt_intParam"
private val ARGUMENT_PAGE_KEYWORDS = listOf("argument keyword1", "argument keyword2")
class ArgumentPageModel : PageModel() {
companion object {
val parameter = listOf(
navArgument(STRING_PARAM_NAME) { type = NavType.StringType },
navArgument(INT_PARAM_NAME) { type = NavType.IntType },
)
fun buildArgument(stringParam: String? = null, intParam: Int? = null): Bundle {
val args = Bundle()
if (stringParam != null) args.putString(STRING_PARAM_NAME, stringParam)
if (intParam != null) args.putInt(INT_PARAM_NAME, intParam)
return args
}
fun buildNextArgument(arguments: Bundle? = null): Bundle {
val intParam = parameter.getIntArg(INT_PARAM_NAME, arguments)
val nextIntParam = if (intParam != null) intParam + 1 else null
return buildArgument(intParam = nextIntParam)
}
fun isValidArgument(arguments: Bundle?): Boolean {
val stringParam = parameter.getStringArg(STRING_PARAM_NAME, arguments)
return (stringParam != null && listOf("foo", "bar").contains(stringParam))
}
fun genStringParamSearchData(): EntrySearchData {
return EntrySearchData(title = STRING_PARAM_TITLE)
}
fun genIntParamSearchData(): EntrySearchData {
return EntrySearchData(title = INT_PARAM_TITLE)
}
fun genInjectSearchData(): EntrySearchData {
return EntrySearchData(title = PAGE_TITLE, keyword = ARGUMENT_PAGE_KEYWORDS)
}
fun genPageTitle(): String {
return PAGE_TITLE
}
@Composable
fun create(arguments: Bundle?): ArgumentPageModel {
val pageModel: ArgumentPageModel = viewModel(key = arguments.toString())
pageModel.initOnce(arguments)
return pageModel
}
}
private var arguments: Bundle? = null
private var stringParam: String? = null
private var intParam: Int? = null
override fun initialize(arguments: Bundle?) {
SpaEnvironmentFactory.instance.logger.message(
TAG, "Initialize with args " + arguments.toString()
)
this.arguments = arguments
stringParam = parameter.getStringArg(STRING_PARAM_NAME, arguments)
intParam = parameter.getIntArg(INT_PARAM_NAME, arguments)
}
@Composable
fun genStringParamPreferenceModel(): PreferenceModel {
return object : PreferenceModel {
override val title = STRING_PARAM_TITLE
override val summary = { stringParam!! }
}
}
@Composable
fun genIntParamPreferenceModel(): PreferenceModel {
return object : PreferenceModel {
override val title = INT_PARAM_TITLE
override val summary = { intParam!!.toString() }
}
}
@Composable
fun genInjectPreferenceModel(): PreferenceModel {
val summaryArray = listOf(
"$STRING_PARAM_NAME=" + stringParam!!,
"$INT_PARAM_NAME=" + intParam!!
)
return object : PreferenceModel {
override val title = PAGE_TITLE
override val summary = { summaryArray.joinToString(", ") }
override val onClick = navigator(
SettingsPageProviderEnum.ARGUMENT.name + parameter.navLink(arguments)
)
}
}
}

View File

@@ -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.settingslib.spa.gallery.page
import android.os.Bundle
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.tooling.preview.Preview
import com.android.settingslib.spa.framework.common.EntrySearchData
import com.android.settingslib.spa.framework.common.SettingsEntry
import com.android.settingslib.spa.framework.common.SettingsEntryBuilder
import com.android.settingslib.spa.framework.common.SettingsPageProvider
import com.android.settingslib.spa.framework.common.createSettingsPage
import com.android.settingslib.spa.framework.compose.navigator
import com.android.settingslib.spa.framework.theme.SettingsTheme
import com.android.settingslib.spa.gallery.R
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.AnnotatedText
import com.android.settingslib.spa.widget.ui.Footer
private const val TITLE = "Sample Footer"
object FooterPageProvider : SettingsPageProvider {
override val name = "Footer"
private val owner = createSettingsPage()
override fun buildEntry(arguments: Bundle?): List<SettingsEntry> {
val entryList = mutableListOf<SettingsEntry>()
entryList.add(
SettingsEntryBuilder.create( "Some Preference", owner)
.setSearchDataFn { EntrySearchData(title = "Some Preference") }
.setUiLayoutFn {
Preference(remember {
object : PreferenceModel {
override val title = "Some Preference"
override val summary = { "Some summary" }
}
})
}.build()
)
return entryList
}
fun buildInjectEntry(): SettingsEntryBuilder {
return SettingsEntryBuilder.createInject(owner)
.setUiLayoutFn {
Preference(object : PreferenceModel {
override val title = TITLE
override val onClick = navigator(name)
})
}
}
override fun getTitle(arguments: Bundle?): String {
return TITLE
}
@Composable
override fun Page(arguments: Bundle?) {
RegularScaffold(title = getTitle(arguments)) {
for (entry in buildEntry(arguments)) {
entry.UiLayout()
}
Footer(footerText = "Footer text always at the end of page.")
Footer {
AnnotatedText(R.string.footer_with_two_links)
}
}
}
}
@Preview(showBackground = true)
@Composable
private fun FooterPagePreview() {
SettingsTheme {
FooterPageProvider.Page(null)
}
}

View File

@@ -0,0 +1,94 @@
/*
* Copyright (C) 2022 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.settingslib.spa.gallery.page
import android.os.Bundle
import androidx.compose.runtime.Composable
import androidx.compose.ui.tooling.preview.Preview
import com.android.settingslib.spa.framework.common.SettingsEntry
import com.android.settingslib.spa.framework.common.SettingsEntryBuilder
import com.android.settingslib.spa.framework.common.SettingsPageProvider
import com.android.settingslib.spa.framework.common.createSettingsPage
import com.android.settingslib.spa.framework.compose.navigator
import com.android.settingslib.spa.framework.theme.SettingsTheme
import com.android.settingslib.spa.gallery.R
import com.android.settingslib.spa.widget.illustration.Illustration
import com.android.settingslib.spa.widget.illustration.IllustrationModel
import com.android.settingslib.spa.widget.illustration.ResourceType
import com.android.settingslib.spa.widget.preference.Preference
import com.android.settingslib.spa.widget.preference.PreferenceModel
private const val TITLE = "Sample Illustration"
object IllustrationPageProvider : SettingsPageProvider {
override val name = "Illustration"
private val owner = createSettingsPage()
override fun buildEntry(arguments: Bundle?): List<SettingsEntry> {
val entryList = mutableListOf<SettingsEntry>()
entryList.add(
SettingsEntryBuilder.create( "Lottie Illustration", owner)
.setUiLayoutFn {
Preference(object : PreferenceModel {
override val title = "Lottie Illustration"
})
Illustration(object : IllustrationModel {
override val resId = R.raw.accessibility_shortcut_type_triple_tap
override val resourceType = ResourceType.LOTTIE
})
}.build()
)
entryList.add(
SettingsEntryBuilder.create( "Image Illustration", owner)
.setUiLayoutFn {
Preference(object : PreferenceModel {
override val title = "Image Illustration"
})
Illustration(object : IllustrationModel {
override val resId = R.drawable.accessibility_captioning_banner
override val resourceType = ResourceType.IMAGE
})
}.build()
)
return entryList
}
fun buildInjectEntry(): SettingsEntryBuilder {
return SettingsEntryBuilder.createInject(owner)
.setUiLayoutFn {
Preference(object : PreferenceModel {
override val title = TITLE
override val onClick = navigator(name)
})
}
}
override fun getTitle(arguments: Bundle?): String {
return TITLE
}
}
@Preview(showBackground = true)
@Composable
private fun IllustrationPagePreview() {
SettingsTheme {
IllustrationPageProvider.Page(null)
}
}

View File

@@ -0,0 +1,88 @@
/*
* Copyright (C) 2022 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.settingslib.spa.gallery.page
import android.os.Bundle
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.android.settingslib.spa.framework.common.SettingsEntryBuilder
import com.android.settingslib.spa.framework.common.SettingsPageProvider
import com.android.settingslib.spa.framework.common.createSettingsPage
import com.android.settingslib.spa.framework.compose.navigator
import com.android.settingslib.spa.framework.theme.SettingsTheme
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.CircularLoadingBar
import com.android.settingslib.spa.widget.ui.LinearLoadingBar
private const val TITLE = "Sample LoadingBar"
object LoadingBarPageProvider : SettingsPageProvider {
override val name = "LoadingBar"
fun buildInjectEntry(): SettingsEntryBuilder {
return SettingsEntryBuilder.createInject(owner = createSettingsPage())
.setUiLayoutFn {
Preference(object : PreferenceModel {
override val title = TITLE
override val onClick = navigator(name)
})
}
}
override fun getTitle(arguments: Bundle?): String {
return TITLE
}
@Composable
override fun Page(arguments: Bundle?) {
var loading by remember { mutableStateOf(true) }
RegularScaffold(title = getTitle(arguments)) {
Button(
onClick = { loading = !loading },
modifier = Modifier.padding(start = 20.dp)
) {
if (loading) {
Text(text = "Stop")
} else {
Text(text = "Resume")
}
}
}
LinearLoadingBar(isLoading = loading, yOffset = 104.dp)
CircularLoadingBar(isLoading = loading)
}
}
@Preview(showBackground = true)
@Composable
private fun LoadingBarPagePreview() {
SettingsTheme {
LoadingBarPageProvider.Page(null)
}
}

View File

@@ -0,0 +1,118 @@
/*
* Copyright (C) 2022 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.settingslib.spa.gallery.page
import android.os.Bundle
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Delete
import androidx.compose.material.icons.outlined.SystemUpdate
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.tooling.preview.Preview
import com.android.settingslib.spa.framework.common.SettingsEntryBuilder
import com.android.settingslib.spa.framework.common.SettingsPageProvider
import com.android.settingslib.spa.framework.common.createSettingsPage
import com.android.settingslib.spa.framework.compose.navigator
import com.android.settingslib.spa.framework.theme.SettingsTheme
import com.android.settingslib.spa.widget.preference.Preference
import com.android.settingslib.spa.widget.preference.PreferenceModel
import com.android.settingslib.spa.widget.preference.ProgressBarPreference
import com.android.settingslib.spa.widget.preference.ProgressBarPreferenceModel
import com.android.settingslib.spa.widget.preference.ProgressBarWithDataPreference
import com.android.settingslib.spa.widget.scaffold.RegularScaffold
import com.android.settingslib.spa.widget.ui.CircularProgressBar
import kotlinx.coroutines.delay
private const val TITLE = "Sample ProgressBar"
object ProgressBarPageProvider : SettingsPageProvider {
override val name = "ProgressBar"
fun buildInjectEntry(): SettingsEntryBuilder {
return SettingsEntryBuilder.createInject(owner = createSettingsPage())
.setUiLayoutFn {
Preference(object : PreferenceModel {
override val title = TITLE
override val onClick = navigator(name)
})
}
}
override fun getTitle(arguments: Bundle?): String {
return TITLE
}
@Composable
override fun Page(arguments: Bundle?) {
RegularScaffold(title = getTitle(arguments)) {
// Auto update the progress and finally jump tp 0.4f.
var progress by remember { mutableStateOf(0f) }
LaunchedEffect(Unit) {
while (progress < 1f) {
delay(100)
progress += 0.01f
}
delay(500)
progress = 0.4f
}
LargeProgressBar(progress)
SimpleProgressBar()
ProgressBarWithData()
CircularProgressBar(progress = progress, radius = 160f)
}
}
}
@Composable
private fun LargeProgressBar(progress: Float) {
ProgressBarPreference(object : ProgressBarPreferenceModel {
override val title = "Large Progress Bar"
override val progress = progress
override val height = 20f
})
}
@Composable
private fun SimpleProgressBar() {
ProgressBarPreference(object : ProgressBarPreferenceModel {
override val title = "Simple Progress Bar"
override val progress = 0.2f
override val icon = Icons.Outlined.SystemUpdate
})
}
@Composable
private fun ProgressBarWithData() {
ProgressBarWithDataPreference(model = object : ProgressBarPreferenceModel {
override val title = "Progress Bar with Data"
override val progress = 0.2f
override val icon = Icons.Outlined.Delete
}, data = "25G")
}
@Preview(showBackground = true)
@Composable
private fun ProgressBarPagePreview() {
SettingsTheme {
ProgressBarPageProvider.Page(null)
}
}

View File

@@ -0,0 +1,73 @@
/*
* Copyright (C) 2022 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.settingslib.spa.gallery.page
import android.os.Bundle
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import com.android.settingslib.spa.framework.common.SettingsEntryBuilder
import com.android.settingslib.spa.framework.common.SettingsPageProvider
import com.android.settingslib.spa.framework.common.createSettingsPage
import com.android.settingslib.spa.framework.compose.navigator
import com.android.settingslib.spa.framework.theme.SettingsTheme
import com.android.settingslib.spa.widget.preference.Preference
import com.android.settingslib.spa.widget.preference.PreferenceModel
import com.android.settingslib.spa.widget.scaffold.SettingsPager
import com.android.settingslib.spa.widget.scaffold.SettingsScaffold
import com.android.settingslib.spa.widget.ui.PlaceholderTitle
private const val TITLE = "Sample SettingsPager"
object SettingsPagerPageProvider : SettingsPageProvider {
override val name = "SettingsPager"
fun buildInjectEntry(): SettingsEntryBuilder {
return SettingsEntryBuilder.createInject(owner = createSettingsPage())
.setUiLayoutFn {
Preference(object : PreferenceModel {
override val title = TITLE
override val onClick = navigator(name)
})
}
}
override fun getTitle(arguments: Bundle?): String {
return TITLE
}
@Composable
override fun Page(arguments: Bundle?) {
SettingsScaffold(title = getTitle(arguments)) { paddingValues ->
Box(Modifier.padding(paddingValues)) {
SettingsPager(listOf("Personal", "Work")) {
PlaceholderTitle("Page $it")
}
}
}
}
}
@Preview(showBackground = true)
@Composable
private fun SettingsPagerPagePreview() {
SettingsTheme {
SettingsPagerPageProvider.Page(null)
}
}

View File

@@ -0,0 +1,127 @@
/*
* Copyright (C) 2022 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.settingslib.spa.gallery.page
import android.os.Bundle
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.AccessAlarm
import androidx.compose.material.icons.outlined.MusicNote
import androidx.compose.material.icons.outlined.MusicOff
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.tooling.preview.Preview
import com.android.settingslib.spa.framework.common.SettingsEntry
import com.android.settingslib.spa.framework.common.SettingsEntryBuilder
import com.android.settingslib.spa.framework.common.SettingsPageProvider
import com.android.settingslib.spa.framework.common.createSettingsPage
import com.android.settingslib.spa.framework.compose.navigator
import com.android.settingslib.spa.framework.theme.SettingsTheme
import com.android.settingslib.spa.widget.preference.SliderPreference
import com.android.settingslib.spa.widget.preference.SliderPreferenceModel
import com.android.settingslib.spa.widget.preference.Preference
import com.android.settingslib.spa.widget.preference.PreferenceModel
private const val TITLE = "Sample Slider"
object SliderPageProvider : SettingsPageProvider {
override val name = "Slider"
private val owner = createSettingsPage()
override fun buildEntry(arguments: Bundle?): List<SettingsEntry> {
val entryList = mutableListOf<SettingsEntry>()
entryList.add(
SettingsEntryBuilder.create("Simple Slider", owner)
.setUiLayoutFn {
SliderPreference(object : SliderPreferenceModel {
override val title = "Simple Slider"
override val initValue = 40
})
}.build()
)
entryList.add(
SettingsEntryBuilder.create("Slider with icon", owner)
.setUiLayoutFn {
SliderPreference(object : SliderPreferenceModel {
override val title = "Slider with icon"
override val initValue = 30
override val onValueChangeFinished = {
println("onValueChangeFinished")
}
override val icon = Icons.Outlined.AccessAlarm
})
}.build()
)
entryList.add(
SettingsEntryBuilder.create("Slider with changeable icon", owner)
.setUiLayoutFn {
val initValue = 0
var icon by remember { mutableStateOf(Icons.Outlined.MusicOff) }
var sliderPosition by remember { mutableStateOf(initValue) }
SliderPreference(object : SliderPreferenceModel {
override val title = "Slider with changeable icon"
override val initValue = initValue
override val onValueChange = { it: Int ->
sliderPosition = it
icon = if (it > 0) Icons.Outlined.MusicNote else Icons.Outlined.MusicOff
}
override val onValueChangeFinished = {
println("onValueChangeFinished: the value is $sliderPosition")
}
override val icon = icon
})
}.build()
)
entryList.add(
SettingsEntryBuilder.create("Slider with steps", owner)
.setUiLayoutFn {
SliderPreference(object : SliderPreferenceModel {
override val title = "Slider with steps"
override val initValue = 2
override val valueRange = 1..5
override val showSteps = true
})
}.build()
)
return entryList
}
fun buildInjectEntry(): SettingsEntryBuilder {
return SettingsEntryBuilder.createInject(owner)
.setUiLayoutFn {
Preference(object : PreferenceModel {
override val title = TITLE
override val onClick = navigator(name)
})
}
}
override fun getTitle(arguments: Bundle?): String {
return TITLE
}
}
@Preview(showBackground = true)
@Composable
private fun SliderPagePreview() {
SettingsTheme {
SliderPageProvider.Page(null)
}
}

View File

@@ -0,0 +1,120 @@
/*
* 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.settingslib.spa.gallery.preference
import android.os.Bundle
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.tooling.preview.Preview
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.settingslib.spa.framework.common.SettingsEntryBuilder
import com.android.settingslib.spa.framework.common.SettingsPageProvider
import com.android.settingslib.spa.framework.common.createSettingsPage
import com.android.settingslib.spa.framework.compose.navigator
import com.android.settingslib.spa.framework.theme.SettingsTheme
import com.android.settingslib.spa.widget.preference.ListPreference
import com.android.settingslib.spa.widget.preference.ListPreferenceModel
import com.android.settingslib.spa.widget.preference.ListPreferenceOption
import com.android.settingslib.spa.widget.preference.Preference
import com.android.settingslib.spa.widget.preference.PreferenceModel
import kotlin.time.Duration.Companion.seconds
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.flow
private const val TITLE = "Sample ListPreference"
object ListPreferencePageProvider : SettingsPageProvider {
override val name = "ListPreference"
private val owner = createSettingsPage()
override fun buildEntry(arguments: Bundle?) = listOf(
SettingsEntryBuilder.create("ListPreference", owner)
.setUiLayoutFn {
SampleListPreference()
}.build(),
SettingsEntryBuilder.create("ListPreference not changeable", owner)
.setUiLayoutFn {
SampleNotChangeableListPreference()
}.build(),
)
fun buildInjectEntry(): SettingsEntryBuilder {
return SettingsEntryBuilder.createInject(owner)
.setUiLayoutFn {
Preference(object : PreferenceModel {
override val title = TITLE
override val onClick = navigator(name)
})
}
}
override fun getTitle(arguments: Bundle?) = TITLE
}
@Composable
private fun SampleListPreference() {
val selectedId = rememberSaveable { mutableIntStateOf(1) }
ListPreference(remember {
object : ListPreferenceModel {
override val title = "Preferred network type"
override val options = listOf(
ListPreferenceOption(id = 1, text = "5G (recommended)"),
ListPreferenceOption(id = 2, text = "LTE"),
ListPreferenceOption(id = 3, text = "3G"),
)
override val selectedId = selectedId
override val onIdSelected: (id: Int) -> Unit = { selectedId.intValue = it }
}
})
}
@Composable
private fun SampleNotChangeableListPreference() {
val selectedId = rememberSaveable { mutableIntStateOf(1) }
val enableFlow = flow {
var enabled = true
while (true) {
delay(3.seconds)
enabled = !enabled
emit(enabled)
}
}
val enabled = enableFlow.collectAsStateWithLifecycle(initialValue = true)
ListPreference(remember {
object : ListPreferenceModel {
override val title = "Preferred network type"
override val enabled = { enabled.value }
override val options = listOf(
ListPreferenceOption(id = 1, text = "5G (recommended)"),
ListPreferenceOption(id = 2, text = "LTE"),
ListPreferenceOption(id = 3, text = "3G"),
)
override val selectedId = selectedId
override val onIdSelected: (id: Int) -> Unit = { selectedId.intValue = it }
}
})
}
@Preview
@Composable
private fun ListPreferencePagePreview() {
SettingsTheme {
ListPreferencePageProvider.Page(null)
}
}

View File

@@ -0,0 +1,108 @@
/*
* 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.settingslib.spa.gallery.preference
import android.os.Bundle
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.tooling.preview.Preview
import com.android.settingslib.spa.framework.common.SettingsEntry
import com.android.settingslib.spa.framework.common.SettingsEntryBuilder
import com.android.settingslib.spa.framework.common.SettingsPageProvider
import com.android.settingslib.spa.framework.common.createSettingsPage
import com.android.settingslib.spa.framework.compose.navigator
import com.android.settingslib.spa.framework.theme.SettingsTheme
import com.android.settingslib.spa.widget.preference.MainSwitchPreference
import com.android.settingslib.spa.widget.preference.Preference
import com.android.settingslib.spa.widget.preference.PreferenceModel
import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel
private const val TITLE = "Sample MainSwitchPreference"
object MainSwitchPreferencePageProvider : SettingsPageProvider {
override val name = "MainSwitchPreference"
private val owner = createSettingsPage()
override fun buildEntry(arguments: Bundle?): List<SettingsEntry> {
val entryList = mutableListOf<SettingsEntry>()
entryList.add(
SettingsEntryBuilder.create("MainSwitchPreference", owner)
.setUiLayoutFn {
SampleMainSwitchPreference()
}.build()
)
entryList.add(
SettingsEntryBuilder.create("MainSwitchPreference not changeable", owner)
.setUiLayoutFn {
SampleNotChangeableMainSwitchPreference()
}.build()
)
return entryList
}
fun buildInjectEntry(): SettingsEntryBuilder {
return SettingsEntryBuilder.createInject(owner)
.setUiLayoutFn {
Preference(object : PreferenceModel {
override val title = TITLE
override val onClick = navigator(name)
})
}
}
override fun getTitle(arguments: Bundle?): String {
return TITLE
}
}
@Composable
private fun SampleMainSwitchPreference() {
var checked by rememberSaveable { mutableStateOf(false) }
MainSwitchPreference(remember {
object : SwitchPreferenceModel {
override val title = "MainSwitchPreference"
override val checked = { checked }
override val onCheckedChange = { newChecked: Boolean -> checked = newChecked }
}
})
}
@Composable
private fun SampleNotChangeableMainSwitchPreference() {
var checked by rememberSaveable { mutableStateOf(true) }
MainSwitchPreference(remember {
object : SwitchPreferenceModel {
override val title = "Not changeable"
override val changeable = { false }
override val checked = { checked }
override val onCheckedChange = { newChecked: Boolean -> checked = newChecked }
}
})
}
@Preview(showBackground = true)
@Composable
private fun MainSwitchPreferencePagePreview() {
SettingsTheme {
MainSwitchPreferencePageProvider.Page(null)
}
}

View File

@@ -0,0 +1,58 @@
/*
* Copyright (C) 2022 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.settingslib.spa.gallery.preference
import android.os.Bundle
import com.android.settingslib.spa.framework.common.SettingsEntry
import com.android.settingslib.spa.framework.common.SettingsEntryBuilder
import com.android.settingslib.spa.framework.common.SettingsPageProvider
import com.android.settingslib.spa.framework.common.createSettingsPage
import com.android.settingslib.spa.framework.compose.navigator
import com.android.settingslib.spa.widget.preference.Preference
import com.android.settingslib.spa.widget.preference.PreferenceModel
private const val TITLE = "Category: Preference"
object PreferenceMainPageProvider : SettingsPageProvider {
override val name = "PreferenceMain"
private val owner = createSettingsPage()
override fun buildEntry(arguments: Bundle?): List<SettingsEntry> {
return listOf(
PreferencePageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
SwitchPreferencePageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
MainSwitchPreferencePageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
ListPreferencePageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
TwoTargetSwitchPreferencePageProvider.buildInjectEntry()
.setLink(fromPage = owner).build(),
)
}
fun buildInjectEntry(): SettingsEntryBuilder {
return SettingsEntryBuilder.createInject(owner = owner)
.setUiLayoutFn {
Preference(object : PreferenceModel {
override val title = TITLE
override val onClick = navigator(name)
})
}
}
override fun getTitle(arguments: Bundle?): String {
return TITLE
}
}

View File

@@ -0,0 +1,112 @@
/*
* Copyright (C) 2022 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.settingslib.spa.gallery.preference
import android.os.Bundle
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
import androidx.lifecycle.viewmodel.compose.viewModel
import com.android.settingslib.spa.framework.common.PageModel
import com.android.settingslib.spa.framework.common.SpaEnvironmentFactory
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
private const val TAG = "PreferencePageModel"
class PreferencePageModel : PageModel() {
companion object {
// Defines all the resources for this page.
// In real Settings App, resources data is defined in xml, rather than SPP.
const val PAGE_TITLE = "Sample Preference"
const val SIMPLE_PREFERENCE_TITLE = "Preference"
const val SIMPLE_PREFERENCE_SUMMARY = "Simple summary"
const val DISABLE_PREFERENCE_TITLE = "Disabled"
const val DISABLE_PREFERENCE_SUMMARY = "Disabled summary"
const val ASYNC_PREFERENCE_TITLE = "Async Preference"
const val ASYNC_PREFERENCE_SUMMARY = "Async summary"
const val MANUAL_UPDATE_PREFERENCE_TITLE = "Manual Updater"
const val AUTO_UPDATE_PREFERENCE_TITLE = "Auto Updater"
val SIMPLE_PREFERENCE_KEYWORDS = listOf("simple keyword1", "simple keyword2")
@Composable
fun create(): PreferencePageModel {
val pageModel: PreferencePageModel = viewModel()
pageModel.initOnce()
return pageModel
}
}
private val spaLogger = SpaEnvironmentFactory.instance.logger
val asyncSummary = mutableStateOf("(loading)")
val asyncEnable = mutableStateOf(false)
private val manualUpdater = mutableStateOf(0)
private val autoUpdater = object : MutableLiveData<String>(" ") {
private var tick = 0
private var updateJob: Job? = null
override fun onActive() {
spaLogger.message(TAG, "autoUpdater.active")
updateJob = viewModelScope.launch(Dispatchers.IO) {
while (true) {
delay(1000L)
tick++
spaLogger.message(TAG, "autoUpdater.value $tick")
postValue(tick.toString())
}
}
}
override fun onInactive() {
spaLogger.message(TAG, "autoUpdater.inactive")
updateJob?.cancel()
}
}
override fun initialize(arguments: Bundle?) {
spaLogger.message(TAG, "initialize with args " + arguments.toString())
viewModelScope.launch(Dispatchers.IO) {
// Loading your data here.
delay(2000L)
asyncSummary.value = ASYNC_PREFERENCE_SUMMARY
asyncEnable.value = true
}
}
fun getManualUpdaterSummary(): State<String> {
spaLogger.message(TAG, "getManualUpdaterSummary")
return derivedStateOf { manualUpdater.value.toString() }
}
fun manualUpdaterOnClick() {
spaLogger.message(TAG, "manualUpdaterOnClick")
manualUpdater.value = manualUpdater.value + 1
}
fun getAutoUpdaterSummary(): LiveData<String> {
spaLogger.message(TAG, "getAutoUpdaterSummary")
return autoUpdater
}
}

View File

@@ -0,0 +1,305 @@
/*
* 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.settingslib.spa.gallery.preference
import android.os.Bundle
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Autorenew
import androidx.compose.material.icons.outlined.DisabledByDefault
import androidx.compose.material.icons.outlined.TouchApp
import androidx.compose.runtime.Composable
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.remember
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import com.android.settingslib.spa.framework.common.EntrySearchData
import com.android.settingslib.spa.framework.common.EntrySliceData
import com.android.settingslib.spa.framework.common.EntryStatusData
import com.android.settingslib.spa.framework.common.SettingsEntry
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.theme.SettingsTheme
import com.android.settingslib.spa.framework.util.createIntent
import com.android.settingslib.spa.gallery.R
import com.android.settingslib.spa.gallery.SettingsPageProviderEnum
import com.android.settingslib.spa.gallery.preference.PreferencePageModel.Companion.ASYNC_PREFERENCE_SUMMARY
import com.android.settingslib.spa.gallery.preference.PreferencePageModel.Companion.ASYNC_PREFERENCE_TITLE
import com.android.settingslib.spa.gallery.preference.PreferencePageModel.Companion.AUTO_UPDATE_PREFERENCE_TITLE
import com.android.settingslib.spa.gallery.preference.PreferencePageModel.Companion.DISABLE_PREFERENCE_SUMMARY
import com.android.settingslib.spa.gallery.preference.PreferencePageModel.Companion.DISABLE_PREFERENCE_TITLE
import com.android.settingslib.spa.gallery.preference.PreferencePageModel.Companion.MANUAL_UPDATE_PREFERENCE_TITLE
import com.android.settingslib.spa.gallery.preference.PreferencePageModel.Companion.PAGE_TITLE
import com.android.settingslib.spa.gallery.preference.PreferencePageModel.Companion.SIMPLE_PREFERENCE_KEYWORDS
import com.android.settingslib.spa.gallery.preference.PreferencePageModel.Companion.SIMPLE_PREFERENCE_SUMMARY
import com.android.settingslib.spa.gallery.preference.PreferencePageModel.Companion.SIMPLE_PREFERENCE_TITLE
import com.android.settingslib.spa.slice.createBrowsePendingIntent
import com.android.settingslib.spa.slice.provider.createDemoActionSlice
import com.android.settingslib.spa.slice.provider.createDemoBrowseSlice
import com.android.settingslib.spa.slice.provider.createDemoSlice
import com.android.settingslib.spa.widget.preference.Preference
import com.android.settingslib.spa.widget.preference.PreferenceModel
import com.android.settingslib.spa.widget.preference.SimplePreferenceMacro
import com.android.settingslib.spa.widget.ui.SettingsIcon
import kotlinx.coroutines.delay
private const val TAG = "PreferencePage"
object PreferencePageProvider : SettingsPageProvider {
// Defines all entry name in this page.
// Note that entry name would be used in log. DO NOT change it once it is set.
// One can still change the display name for better readability if necessary.
enum class EntryEnum(val displayName: String) {
SIMPLE_PREFERENCE("preference"),
SUMMARY_PREFERENCE("preference_with_summary"),
SINGLE_LINE_SUMMARY_PREFERENCE("preference_with_single_line_summary"),
DISABLED_PREFERENCE("preference_disable"),
ASYNC_SUMMARY_PREFERENCE("preference_with_async_summary"),
MANUAL_UPDATE_PREFERENCE("preference_actionable"),
AUTO_UPDATE_PREFERENCE("preference_auto_update"),
}
override val name = SettingsPageProviderEnum.PREFERENCE.name
override val displayName = SettingsPageProviderEnum.PREFERENCE.displayName
private val spaLogger = SpaEnvironmentFactory.instance.logger
private val owner = createSettingsPage()
private fun createEntry(entry: EntryEnum): SettingsEntryBuilder {
return SettingsEntryBuilder.create(owner, entry.name, entry.displayName)
}
override fun buildEntry(arguments: Bundle?): List<SettingsEntry> {
val entryList = mutableListOf<SettingsEntry>()
entryList.add(
createEntry(EntryEnum.SIMPLE_PREFERENCE)
.setMacro {
spaLogger.message(TAG, "create macro for ${EntryEnum.SIMPLE_PREFERENCE}")
SimplePreferenceMacro(title = SIMPLE_PREFERENCE_TITLE)
}
.setStatusDataFn { EntryStatusData(isDisabled = false) }
.build()
)
entryList.add(
createEntry(EntryEnum.SUMMARY_PREFERENCE)
.setMacro {
spaLogger.message(TAG, "create macro for ${EntryEnum.SUMMARY_PREFERENCE}")
SimplePreferenceMacro(
title = SIMPLE_PREFERENCE_TITLE,
summary = SIMPLE_PREFERENCE_SUMMARY,
searchKeywords = SIMPLE_PREFERENCE_KEYWORDS,
)
}
.setStatusDataFn { EntryStatusData(isDisabled = true) }
.build()
)
entryList.add(singleLineSummaryEntry())
entryList.add(
createEntry(EntryEnum.DISABLED_PREFERENCE)
.setHasMutableStatus(true)
.setMacro {
spaLogger.message(TAG, "create macro for ${EntryEnum.DISABLED_PREFERENCE}")
SimplePreferenceMacro(
title = DISABLE_PREFERENCE_TITLE,
summary = DISABLE_PREFERENCE_SUMMARY,
disabled = true,
icon = Icons.Outlined.DisabledByDefault,
)
}
.setStatusDataFn { EntryStatusData(isDisabled = true) }
.build()
)
entryList.add(
createEntry(EntryEnum.ASYNC_SUMMARY_PREFERENCE)
.setHasMutableStatus(true)
.setSearchDataFn {
EntrySearchData(title = ASYNC_PREFERENCE_TITLE)
}
.setStatusDataFn { EntryStatusData(isDisabled = false) }
.setUiLayoutFn {
val model = PreferencePageModel.create()
Preference(
object : PreferenceModel {
override val title = ASYNC_PREFERENCE_TITLE
override val summary = { model.asyncSummary.value }
override val enabled = { model.asyncEnable.value }
}
)
}
.setSliceDataFn { sliceUri, _ ->
val createSliceImpl = { s: String ->
createDemoBrowseSlice(
sliceUri = sliceUri,
title = ASYNC_PREFERENCE_TITLE,
summary = s,
)
}
return@setSliceDataFn object : EntrySliceData() {
init {
postValue(createSliceImpl("(loading)"))
}
override suspend fun asyncRunner() {
spaLogger.message(TAG, "Async entry loading")
delay(2000L)
postValue(createSliceImpl(ASYNC_PREFERENCE_SUMMARY))
}
}
}.build()
)
entryList.add(
createEntry(EntryEnum.MANUAL_UPDATE_PREFERENCE)
.setUiLayoutFn {
val model = PreferencePageModel.create()
val manualUpdaterSummary = remember { model.getManualUpdaterSummary() }
Preference(
object : PreferenceModel {
override val title = MANUAL_UPDATE_PREFERENCE_TITLE
override val summary = { manualUpdaterSummary.value }
override val onClick = { model.manualUpdaterOnClick() }
override val icon = @Composable {
SettingsIcon(imageVector = Icons.Outlined.TouchApp)
}
}
)
}
.setSliceDataFn { sliceUri, args ->
val createSliceImpl = { v: Int ->
createDemoActionSlice(
sliceUri = sliceUri,
title = MANUAL_UPDATE_PREFERENCE_TITLE,
summary = "manual update value $v",
)
}
return@setSliceDataFn object : EntrySliceData() {
private var tick = args?.getString("init")?.toInt() ?: 0
init {
postValue(createSliceImpl(tick))
}
override suspend fun asyncAction() {
tick++
postValue(createSliceImpl(tick))
}
}
}.build()
)
entryList.add(
createEntry(EntryEnum.AUTO_UPDATE_PREFERENCE)
.setUiLayoutFn {
val model = PreferencePageModel.create()
val autoUpdaterSummary = remember {
model.getAutoUpdaterSummary()
}.observeAsState(" ")
Preference(
object : PreferenceModel {
override val title = AUTO_UPDATE_PREFERENCE_TITLE
override val summary = { autoUpdaterSummary.value }
override val icon = @Composable {
SettingsIcon(imageVector = Icons.Outlined.Autorenew)
}
}
)
}
.setSliceDataFn { sliceUri, args ->
val createSliceImpl = { v: Int ->
createDemoBrowseSlice(
sliceUri = sliceUri,
title = AUTO_UPDATE_PREFERENCE_TITLE,
summary = "auto update value $v",
)
}
return@setSliceDataFn object : EntrySliceData() {
private var tick = args?.getString("init")?.toInt() ?: 0
init {
postValue(createSliceImpl(tick))
}
override suspend fun asyncRunner() {
spaLogger.message(TAG, "autoUpdater.active")
while (true) {
delay(1000L)
tick++
spaLogger.message(TAG, "autoUpdater.value $tick")
postValue(createSliceImpl(tick))
}
}
}
}.build()
)
return entryList
}
private fun singleLineSummaryEntry() = createEntry(EntryEnum.SINGLE_LINE_SUMMARY_PREFERENCE)
.setUiLayoutFn {
val summary = stringResource(R.string.single_line_summary_preference_summary)
Preference(
model = object : PreferenceModel {
override val title: String =
stringResource(R.string.single_line_summary_preference_title)
override val summary = { summary }
},
singleLineSummary = true,
)
}
.build()
fun buildInjectEntry(): SettingsEntryBuilder {
return SettingsEntryBuilder.createInject(owner = owner)
.setMacro {
spaLogger.message(TAG, "create macro for INJECT entry")
SimplePreferenceMacro(
title = PAGE_TITLE,
clickRoute = SettingsPageProviderEnum.PREFERENCE.name
)
}
.setSliceDataFn { sliceUri, _ ->
val intent = owner.createIntent()?.createBrowsePendingIntent()
?: return@setSliceDataFn null
return@setSliceDataFn object : EntrySliceData() {
init {
postValue(
createDemoSlice(
sliceUri = sliceUri,
title = PAGE_TITLE,
summary = "Injected Entry",
intent = intent,
)
)
}
}
}
}
override fun getTitle(arguments: Bundle?): String {
return PAGE_TITLE
}
}
@Preview(showBackground = true)
@Composable
private fun PreferencePagePreview() {
SpaEnvironmentFactory.resetForPreview()
SettingsTheme {
PreferencePageProvider.Page(null)
}
}

View File

@@ -0,0 +1,177 @@
/*
* 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.settingslib.spa.gallery.preference
import android.os.Bundle
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.AirplanemodeActive
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.produceState
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.tooling.preview.Preview
import com.android.settingslib.spa.framework.common.SettingsEntry
import com.android.settingslib.spa.framework.common.SettingsEntryBuilder
import com.android.settingslib.spa.framework.common.SettingsPageProvider
import com.android.settingslib.spa.framework.common.createSettingsPage
import com.android.settingslib.spa.framework.compose.navigator
import com.android.settingslib.spa.framework.theme.SettingsTheme
import com.android.settingslib.spa.widget.preference.Preference
import com.android.settingslib.spa.widget.preference.PreferenceModel
import com.android.settingslib.spa.widget.preference.SwitchPreference
import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel
import com.android.settingslib.spa.widget.ui.SettingsIcon
import kotlinx.coroutines.delay
private const val TITLE = "Sample SwitchPreference"
object SwitchPreferencePageProvider : SettingsPageProvider {
override val name = "SwitchPreference"
private val owner = createSettingsPage()
override fun buildEntry(arguments: Bundle?): List<SettingsEntry> {
val entryList = mutableListOf<SettingsEntry>()
entryList.add(
SettingsEntryBuilder.create( "SwitchPreference", owner)
.setUiLayoutFn {
SampleSwitchPreference()
}.build()
)
entryList.add(
SettingsEntryBuilder.create( "SwitchPreference with summary", owner)
.setUiLayoutFn {
SampleSwitchPreferenceWithSummary()
}.build()
)
entryList.add(
SettingsEntryBuilder.create( "SwitchPreference with async summary", owner)
.setUiLayoutFn {
SampleSwitchPreferenceWithAsyncSummary()
}.build()
)
entryList.add(
SettingsEntryBuilder.create( "SwitchPreference not changeable", owner)
.setUiLayoutFn {
SampleNotChangeableSwitchPreference()
}.build()
)
entryList.add(
SettingsEntryBuilder.create( "SwitchPreference with icon", owner)
.setUiLayoutFn {
SampleSwitchPreferenceWithIcon()
}.build()
)
return entryList
}
fun buildInjectEntry(): SettingsEntryBuilder {
return SettingsEntryBuilder.createInject(owner)
.setUiLayoutFn {
Preference(object : PreferenceModel {
override val title = TITLE
override val onClick = navigator(name)
})
}
}
override fun getTitle(arguments: Bundle?): String {
return TITLE
}
}
@Composable
private fun SampleSwitchPreference() {
var checked by rememberSaveable { mutableStateOf(false) }
SwitchPreference(remember {
object : SwitchPreferenceModel {
override val title = "SwitchPreference"
override val checked = { checked }
override val onCheckedChange = { newChecked: Boolean -> checked = newChecked }
}
})
}
@Composable
private fun SampleSwitchPreferenceWithSummary() {
var checked by rememberSaveable { mutableStateOf(true) }
SwitchPreference(remember {
object : SwitchPreferenceModel {
override val title = "SwitchPreference"
override val summary = { "With summary" }
override val checked = { checked }
override val onCheckedChange = { newChecked: Boolean -> checked = newChecked }
}
})
}
@Composable
private fun SampleSwitchPreferenceWithAsyncSummary() {
var checked by rememberSaveable { mutableStateOf(true) }
val summary = produceState(initialValue = " ") {
delay(1000L)
value = "Async summary"
}
SwitchPreference(remember {
object : SwitchPreferenceModel {
override val title = "SwitchPreference"
override val summary = { summary.value }
override val checked = { checked }
override val onCheckedChange = { newChecked: Boolean -> checked = newChecked }
}
})
}
@Composable
private fun SampleNotChangeableSwitchPreference() {
var checked by rememberSaveable { mutableStateOf(true) }
SwitchPreference(remember {
object : SwitchPreferenceModel {
override val title = "SwitchPreference"
override val summary = { "Not changeable" }
override val changeable = { false }
override val checked = { checked }
override val onCheckedChange = { newChecked: Boolean -> checked = newChecked }
}
})
}
@Composable
private fun SampleSwitchPreferenceWithIcon() {
var checked by rememberSaveable { mutableStateOf(true) }
SwitchPreference(remember {
object : SwitchPreferenceModel {
override val title = "SwitchPreference"
override val checked = { checked }
override val onCheckedChange = { newChecked: Boolean -> checked = newChecked }
override val icon = @Composable {
SettingsIcon(imageVector = Icons.Outlined.AirplanemodeActive)
}
}
})
}
@Preview(showBackground = true)
@Composable
private fun SwitchPreferencePagePreview() {
SettingsTheme {
SwitchPreferencePageProvider.Page(null)
}
}

View File

@@ -0,0 +1,153 @@
/*
* 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.settingslib.spa.gallery.preference
import android.os.Bundle
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.produceState
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.tooling.preview.Preview
import com.android.settingslib.spa.framework.common.SettingsEntry
import com.android.settingslib.spa.framework.common.SettingsEntryBuilder
import com.android.settingslib.spa.framework.common.SettingsPageProvider
import com.android.settingslib.spa.framework.common.createSettingsPage
import com.android.settingslib.spa.framework.compose.navigator
import com.android.settingslib.spa.framework.theme.SettingsTheme
import com.android.settingslib.spa.widget.preference.Preference
import com.android.settingslib.spa.widget.preference.PreferenceModel
import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel
import com.android.settingslib.spa.widget.preference.TwoTargetSwitchPreference
import kotlinx.coroutines.delay
private const val TITLE = "Sample TwoTargetSwitchPreference"
object TwoTargetSwitchPreferencePageProvider : SettingsPageProvider {
override val name = "TwoTargetSwitchPreference"
private val owner = createSettingsPage()
override fun buildEntry(arguments: Bundle?): List<SettingsEntry> {
val entryList = mutableListOf<SettingsEntry>()
entryList.add(
SettingsEntryBuilder.create( "TwoTargetSwitchPreference", owner)
.setUiLayoutFn {
SampleTwoTargetSwitchPreference()
}.build()
)
entryList.add(
SettingsEntryBuilder.create( "TwoTargetSwitchPreference with summary", owner)
.setUiLayoutFn {
SampleTwoTargetSwitchPreferenceWithSummary()
}.build()
)
entryList.add(
SettingsEntryBuilder.create( "TwoTargetSwitchPreference with async summary", owner)
.setUiLayoutFn {
SampleTwoTargetSwitchPreferenceWithAsyncSummary()
}.build()
)
entryList.add(
SettingsEntryBuilder.create( "TwoTargetSwitchPreference not changeable", owner)
.setUiLayoutFn {
SampleNotChangeableTwoTargetSwitchPreference()
}.build()
)
return entryList
}
fun buildInjectEntry(): SettingsEntryBuilder {
return SettingsEntryBuilder.createInject(owner)
.setUiLayoutFn {
Preference(object : PreferenceModel {
override val title = TITLE
override val onClick = navigator(name)
})
}
}
override fun getTitle(arguments: Bundle?): String {
return TITLE
}
}
@Composable
private fun SampleTwoTargetSwitchPreference() {
var checked by rememberSaveable { mutableStateOf(false) }
TwoTargetSwitchPreference(remember {
object : SwitchPreferenceModel {
override val title = "TwoTargetSwitchPreference"
override val checked = { checked }
override val onCheckedChange = { newChecked: Boolean -> checked = newChecked }
}
}) {}
}
@Composable
private fun SampleTwoTargetSwitchPreferenceWithSummary() {
var checked by rememberSaveable { mutableStateOf(true) }
TwoTargetSwitchPreference(remember {
object : SwitchPreferenceModel {
override val title = "TwoTargetSwitchPreference"
override val summary = { "With summary" }
override val checked = { checked }
override val onCheckedChange = { newChecked: Boolean -> checked = newChecked }
}
}) {}
}
@Composable
private fun SampleTwoTargetSwitchPreferenceWithAsyncSummary() {
var checked by rememberSaveable { mutableStateOf(true) }
val summary = produceState(initialValue = " ") {
delay(1000L)
value = "Async summary"
}
TwoTargetSwitchPreference(remember {
object : SwitchPreferenceModel {
override val title = "TwoTargetSwitchPreference"
override val summary = { summary.value }
override val checked = { checked }
override val onCheckedChange = { newChecked: Boolean -> checked = newChecked }
}
}) {}
}
@Composable
private fun SampleNotChangeableTwoTargetSwitchPreference() {
var checked by rememberSaveable { mutableStateOf(true) }
TwoTargetSwitchPreference(remember {
object : SwitchPreferenceModel {
override val title = "TwoTargetSwitchPreference"
override val summary = { "Not changeable" }
override val changeable = { false }
override val checked = { checked }
override val onCheckedChange = { newChecked: Boolean -> checked = newChecked }
}
}) {}
}
@Preview(showBackground = true)
@Composable
private fun TwoTargetSwitchPreferencePagePreview() {
SettingsTheme {
TwoTargetSwitchPreferencePageProvider.Page(null)
}
}

View File

@@ -0,0 +1,56 @@
/*
* 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.settingslib.spa.gallery.scaffold
import android.os.Bundle
import androidx.compose.runtime.Composable
import com.android.settingslib.spa.framework.common.SettingsEntryBuilder
import com.android.settingslib.spa.framework.common.SettingsPageProvider
import com.android.settingslib.spa.framework.common.createSettingsPage
import com.android.settingslib.spa.framework.compose.navigator
import com.android.settingslib.spa.widget.preference.Preference
import com.android.settingslib.spa.widget.preference.PreferenceModel
import com.android.settingslib.spa.widget.scaffold.SearchScaffold
import com.android.settingslib.spa.widget.ui.PlaceholderTitle
private const val TITLE = "Sample SearchScaffold"
object SearchScaffoldPageProvider : SettingsPageProvider {
override val name = "SearchScaffold"
private val owner = createSettingsPage()
fun buildInjectEntry() = SettingsEntryBuilder.createInject(owner = owner)
.setUiLayoutFn {
Preference(object : PreferenceModel {
override val title = TITLE
override val onClick = navigator(name)
})
}
@Composable
override fun Page(arguments: Bundle?) {
Page()
}
}
@Composable
private fun Page() {
SearchScaffold(title = TITLE) { _, searchQuery ->
PlaceholderTitle("Search query: ${searchQuery()}")
}
}

View File

@@ -0,0 +1,85 @@
/*
* 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.settingslib.spa.gallery.scaffold
import android.os.Bundle
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.SignalCellularAlt
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import com.android.settingslib.spa.framework.common.SettingsEntryBuilder
import com.android.settingslib.spa.framework.common.SettingsPageProvider
import com.android.settingslib.spa.framework.common.createSettingsPage
import com.android.settingslib.spa.framework.compose.navigator
import com.android.settingslib.spa.framework.theme.SettingsDimension
import com.android.settingslib.spa.gallery.R
import com.android.settingslib.spa.widget.illustration.Illustration
import com.android.settingslib.spa.widget.illustration.IllustrationModel
import com.android.settingslib.spa.widget.illustration.ResourceType
import com.android.settingslib.spa.widget.preference.Preference
import com.android.settingslib.spa.widget.preference.PreferenceModel
import com.android.settingslib.spa.widget.scaffold.BottomAppBarButton
import com.android.settingslib.spa.widget.scaffold.SuwScaffold
import com.android.settingslib.spa.widget.ui.SettingsBody
private const val TITLE = "Sample SuwScaffold"
object SuwScaffoldPageProvider : SettingsPageProvider {
override val name = "SuwScaffold"
private val owner = createSettingsPage()
fun buildInjectEntry() = SettingsEntryBuilder.createInject(owner = owner)
.setUiLayoutFn {
Preference(object : PreferenceModel {
override val title = TITLE
override val onClick = navigator(name)
})
}
@Composable
override fun Page(arguments: Bundle?) {
Page()
}
}
@Composable
private fun Page() {
SuwScaffold(
imageVector = Icons.Outlined.SignalCellularAlt,
title = "Connect to mobile network",
actionButton = BottomAppBarButton("Next") {},
dismissButton = BottomAppBarButton("Cancel") {},
) {
Column(Modifier.padding(SettingsDimension.itemPadding)) {
SettingsBody("To add another SIM, download a new eSIM.")
}
Illustration(object : IllustrationModel {
override val resId = R.drawable.accessibility_captioning_banner
override val resourceType = ResourceType.IMAGE
})
Column(Modifier.padding(SettingsDimension.itemPadding)) {
SettingsBody("To add another SIM, download a new eSIM.")
}
Illustration(object : IllustrationModel {
override val resId = R.drawable.accessibility_captioning_banner
override val resourceType = ResourceType.IMAGE
})
}
}

View File

@@ -0,0 +1,105 @@
/*
* Copyright (C) 2022 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.settingslib.spa.gallery.ui
import android.os.Bundle
import androidx.compose.runtime.Composable
import androidx.compose.ui.tooling.preview.Preview
import com.android.settingslib.spa.framework.common.EntrySearchData
import com.android.settingslib.spa.framework.common.SettingsEntry
import com.android.settingslib.spa.framework.common.SettingsEntryBuilder
import com.android.settingslib.spa.framework.common.SettingsPageProvider
import com.android.settingslib.spa.framework.common.createSettingsPage
import com.android.settingslib.spa.framework.compose.navigator
import com.android.settingslib.spa.framework.theme.SettingsTheme
import com.android.settingslib.spa.widget.preference.Preference
import com.android.settingslib.spa.widget.preference.PreferenceModel
import com.android.settingslib.spa.widget.preference.SimplePreferenceMacro
import com.android.settingslib.spa.widget.scaffold.RegularScaffold
import com.android.settingslib.spa.widget.ui.Category
import com.android.settingslib.spa.widget.ui.CategoryTitle
private const val TITLE = "Sample Category"
object CategoryPageProvider : SettingsPageProvider {
override val name = "Category"
private val owner = createSettingsPage()
fun buildInjectEntry(): SettingsEntryBuilder {
return SettingsEntryBuilder.createInject(owner)
.setUiLayoutFn {
Preference(object : PreferenceModel {
override val title = TITLE
override val onClick = navigator(name)
})
}
.setSearchDataFn { EntrySearchData(title = TITLE) }
}
override fun getTitle(arguments: Bundle?): String {
return TITLE
}
override fun buildEntry(arguments: Bundle?): List<SettingsEntry> {
val entryList = mutableListOf<SettingsEntry>()
entryList.add(
SettingsEntryBuilder.create("Preference 1", owner)
.setMacro { SimplePreferenceMacro(title = "Preference 1", summary = "Summary 1") }
.build()
)
entryList.add(
SettingsEntryBuilder.create("Preference 2", owner)
.setMacro { SimplePreferenceMacro(title = "Preference 2", summary = "Summary 2") }
.build()
)
entryList.add(
SettingsEntryBuilder.create("Preference 3", owner)
.setMacro { SimplePreferenceMacro(title = "Preference 2", summary = "Summary 3") }
.build()
)
entryList.add(
SettingsEntryBuilder.create("Preference 4", owner)
.setMacro { SimplePreferenceMacro(title = "Preference 4", summary = "Summary 4") }
.build()
)
return entryList
}
@Composable
override fun Page(arguments: Bundle?) {
val entries = buildEntry(arguments)
RegularScaffold(title = getTitle(arguments)) {
CategoryTitle("Category A")
entries[0].UiLayout()
entries[1].UiLayout()
Category("Category B") {
entries[2].UiLayout()
entries[3].UiLayout()
}
}
}
}
@Preview(showBackground = true)
@Composable
private fun SpinnerPagePreview() {
SettingsTheme {
SpinnerPageProvider.Page(null)
}
}

View File

@@ -0,0 +1,61 @@
/*
* 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.settingslib.spa.gallery.ui
import android.os.Bundle
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import com.android.settingslib.spa.framework.common.EntrySearchData
import com.android.settingslib.spa.framework.common.SettingsEntryBuilder
import com.android.settingslib.spa.framework.common.SettingsPageProvider
import com.android.settingslib.spa.framework.common.createSettingsPage
import com.android.settingslib.spa.framework.compose.navigator
import com.android.settingslib.spa.framework.theme.SettingsDimension
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.CopyableBody
private const val TITLE = "Sample Copyable"
object CopyablePageProvider : SettingsPageProvider {
override val name = "Copyable"
private val owner = createSettingsPage()
fun buildInjectEntry(): SettingsEntryBuilder {
return SettingsEntryBuilder.createInject(owner)
.setUiLayoutFn {
Preference(object : PreferenceModel {
override val title = TITLE
override val onClick = navigator(name)
})
}
.setSearchDataFn { EntrySearchData(title = TITLE) }
}
@Composable
override fun Page(arguments: Bundle?) {
RegularScaffold(title = TITLE) {
Box(modifier = Modifier.padding(SettingsDimension.itemPadding)) {
CopyableBody(body = "Copyable body")
}
}
}
}

View File

@@ -0,0 +1,79 @@
/*
* 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.settingslib.spa.gallery.ui
import android.os.Bundle
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.tooling.preview.Preview
import com.android.settingslib.spa.framework.common.SettingsEntryBuilder
import com.android.settingslib.spa.framework.common.SettingsPageProvider
import com.android.settingslib.spa.framework.common.createSettingsPage
import com.android.settingslib.spa.framework.compose.navigator
import com.android.settingslib.spa.framework.theme.SettingsTheme
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.Spinner
import com.android.settingslib.spa.widget.ui.SpinnerOption
private const val TITLE = "Sample Spinner"
object SpinnerPageProvider : SettingsPageProvider {
override val name = "Spinner"
fun buildInjectEntry(): SettingsEntryBuilder {
return SettingsEntryBuilder.createInject(owner = createSettingsPage())
.setUiLayoutFn {
Preference(object : PreferenceModel {
override val title = TITLE
override val onClick = navigator(name)
})
}
}
override fun getTitle(arguments: Bundle?): String {
return TITLE
}
@Composable
override fun Page(arguments: Bundle?) {
RegularScaffold(title = getTitle(arguments)) {
var selectedId by rememberSaveable { mutableIntStateOf(1) }
Spinner(
options = (1..3).map { SpinnerOption(id = it, text = "Option $it") },
selectedId = selectedId,
setId = { selectedId = it },
)
Preference(object : PreferenceModel {
override val title = "Selected id"
override val summary = { selectedId.toString() }
})
}
}
}
@Preview(showBackground = true)
@Composable
private fun SpinnerPagePreview() {
SettingsTheme {
SpinnerPageProvider.Page(null)
}
}

View File

@@ -0,0 +1,39 @@
#
# Copyright (C) 2022 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.
#
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
# AndroidX package structure to make it clearer which packages are bundled with the
# Android operating system, and which are packaged with your app's APK
# https://developer.android.com/topic/libraries/support-library/androidx-rn
android.useAndroidX=true
# Kotlin code style for this project: "official" or "obsolete":
kotlin.code.style=official
# Enables namespacing of each library's R class so that its R class includes only the
# resources declared in the library itself and none from the library's dependencies,
# thereby reducing the size of the R class for that library
android.nonTransitiveRClass=true

View File

@@ -0,0 +1,32 @@
#
# 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.
#
[versions]
agp = "8.2.2"
compose-compiler = "1.5.10"
dexmaker-mockito = "2.28.3"
jvm = "17"
kotlin = "1.9.22"
truth = "1.1.5"
[libraries]
dexmaker-mockito = { module = "com.linkedin.dexmaker:dexmaker-mockito", version.ref = "dexmaker-mockito" }
truth = { module = "com.google.truth:truth", version.ref = "truth" }
[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }
android-library = { id = "com.android.library", version.ref = "agp" }
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }

Binary file not shown.

View File

@@ -0,0 +1,23 @@
#
# Copyright (C) 2022 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.
#
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

249
SettingsLib/Spa/gradlew vendored Normal file
View File

@@ -0,0 +1,249 @@
#!/bin/sh
#
# Copyright © 2015-2021 the original authors.
#
# 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
#
# https://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.
#
##############################################################################
#
# Gradle start up script for POSIX generated by Gradle.
#
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
app_path=$0
# Need this for daisy-chained symlinks.
while
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
[ -h "$app_path" ]
do
ls=$( ls -ld "$app_path" )
link=${ls#*' -> '}
case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done
# This is normally unused
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
warn () {
echo "$*"
} >&2
die () {
echo
echo "$*"
echo
exit 1
} >&2
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "$( uname )" in #(
CYGWIN* ) cygwin=true ;; #(
Darwin* ) darwin=true ;; #(
MSYS* | MINGW* ) msys=true ;; #(
NONSTOP* ) nonstop=true ;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD=$JAVA_HOME/jre/sh/java
else
JAVACMD=$JAVA_HOME/bin/java
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD=java
if ! command -v java >/dev/null 2>&1
then
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
fi
# Increase the maximum file descriptors if we can.
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
fi
# Collect all arguments for the java command, stacking in reverse order:
# * args from the command line
# * the main class name
# * -classpath
# * -D...appname settings
# * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
# Now convert the arguments - kludge to limit ourselves to /bin/sh
for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
fi
# Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
done
fi
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Collect all arguments for the java command:
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# and any embedded shellness will be escaped.
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
# treated as '${Hostname}' itself on the command line.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Stop when "xargs" is not available.
if ! command -v xargs >/dev/null 2>&1
then
die "xargs is not available"
fi
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
exec "$JAVACMD" "$@"

View File

@@ -0,0 +1,51 @@
//
// Copyright (C) 2022 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 {
default_applicable_licenses: ["frameworks_base_license"],
}
filegroup {
name: "SpaScreenshotTestRNGFiles",
srcs: [
"src/**/*.java",
"src/**/*.kt",
],
}
android_test {
name: "SpaScreenshotTests",
use_resource_processor: true,
test_suites: ["device-tests"],
asset_dirs: ["assets"],
srcs: ["src/**/*.kt"],
certificate: "platform",
static_libs: [
"SpaLib",
"SpaLibTestUtils",
"androidx.compose.runtime_runtime",
"androidx.test.ext.junit",
"androidx.test.runner",
"mockito-target-minus-junit4",
"platform-parametric-runner-lib",
"platform-screenshot-diff-core",
],
kotlincflags: ["-Xjvm-default=all"],
}

View File

@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="utf-8"?><!--
Copyright (C) 2022 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.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.settingslib.spa.screenshot">
<uses-sdk android:minSdkVersion="21"/>
<application>
<uses-library android:name="android.test.runner" />
<activity android:name=".DebugActivity" android:exported="true" />
</application>
<instrumentation
android:name="androidx.test.runner.AndroidJUnitRunner"
android:label="Screenshot tests for SpaLib"
android:targetPackage="com.android.settingslib.spa.screenshot">
</instrumentation>
</manifest>

View File

@@ -0,0 +1,36 @@
<!--
Copyright (C) 2022 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.
-->
<configuration description="Runs screendiff tests.">
<option name="test-suite-tag" value="apct-instrumentation" />
<option name="test-suite-tag" value="apct" />
<target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
<option name="optimized-property-setting" value="true" />
</target_preparer>
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
<option name="test-file-name" value="SpaScreenshotTests.apk" />
</target_preparer>
<metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
<option name="directory-keys"
value="/data/user/0/com.android.settingslib.spa.screenshot/files/settings_screenshots" />
<option name="collect-on-run-ended-only" value="true" />
</metrics_collector>
<test class="com.android.tradefed.testtype.AndroidJUnitTest">
<option name="package" value="com.android.settingslib.spa.screenshot" />
<option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
</test>
</configuration>

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 93 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Some files were not shown because too many files have changed in this diff Show More