diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 1fed3139..487ebf2f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -42,21 +42,24 @@ jobs: - name: Build debug run: ./gradlew assembleDebug - - name: Run local tests - run: ./gradlew testDebug + # TODO: Temporarily disable unit tests until the infra is fixed + # - name: Run local tests + # run: ./gradlew testDebug - name: Upload build outputs (APKs) - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: build-outputs path: app/build/outputs + overwrite: true - name: Upload build reports if: always() - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: build-reports path: app/build/reports + overwrite: true androidTest: needs: build @@ -112,7 +115,8 @@ jobs: - name: Upload test reports if: always() - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: test-reports-${{ matrix.api-level }} path: '*/build/reports/androidTests' + overwrite: true diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml index 8d81632f..c22b6fa9 100644 --- a/.idea/kotlinc.xml +++ b/.idea/kotlinc.xml @@ -1,6 +1,6 @@ - \ No newline at end of file diff --git a/README.md b/README.md index 3ba6ad3f..833359aa 100644 --- a/README.md +++ b/README.md @@ -5,8 +5,7 @@ This repository contains a collection of samples that demonstrate the use of different Android OS platform APIs. The samples are organized into folders by topic, and each folder contains a README file that provides more information about the samples in that folder. > **Note:** These samples are intended to showcase specific functionality in isolation, and they may use -> simplified code. They are not intended to be used as production-ready code. The project uses the -> [casa-android](https://github.com/google/casa-android) (intended only for demo projects). +> simplified code. They are not intended to be used as production-ready code. > For best practices follow our documentation and check > [Now In Android](https://github.com/android/nowinandroid) @@ -24,8 +23,6 @@ Browse the samples inside each topic samples folder: We are constantly adding new samples to this repository. You can find a list of all the available samples [here](https://github.com/android/platform-samples/tree/main/samples/README.md). -> 🚧 **Work-in-Progress:** we are working on bringing more existing and new samples into this format. - ## How to run 1. Clone the repository diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 39f68564..0848349e 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -13,36 +13,26 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -@Suppress("DSL_SCOPE_VIOLATION") + plugins { alias(libs.plugins.android.application) alias(libs.plugins.kotlin.android) - alias(libs.plugins.ksp) - alias(libs.plugins.hilt) - id("com.example.platform.convention") -} - -java { - toolchain { - languageVersion.set(JavaLanguageVersion.of(17)) - } + alias(libs.plugins.kotlin.compose) + kotlin("plugin.serialization") version "2.1.10" } android { - namespace = "com.example.platform.app" + namespace = "com.example.platform" compileSdk = 35 defaultConfig { - applicationId = "com.example.platform.app" + applicationId = "com.example.platform" minSdk = 21 targetSdk = 35 versionCode = 1 versionName = "1.0" - testInstrumentationRunner = "com.example.platform.app.AppTestRunner" - vectorDrawables { - useSupportLibrary = true - } + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" } buildTypes { @@ -50,66 +40,71 @@ android { isMinifyEnabled = false proguardFiles( getDefaultProguardFile("proguard-android-optimize.txt"), - "proguard-rules.pro" + "proguard-rules.pro", ) } } - compileOptions { - sourceCompatibility = JavaVersion.VERSION_17 - targetCompatibility = JavaVersion.VERSION_17 + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 + } + kotlinOptions { + jvmTarget = "11" } - buildFeatures { compose = true } - composeOptions { - kotlinCompilerExtensionVersion = libs.versions.composeCompiler.get() - } - packagingOptions { - resources { - excludes += "/META-INF/{AL2.0,LGPL2.1}" - } - } } dependencies { - implementation(platform(libs.compose.bom)) - implementation(libs.casa.ui) - implementation(libs.androidx.appcompat) - - implementation(libs.hilt.android) - ksp(libs.hilt.compiler) - implementation(libs.coil.base) - implementation(libs.coil.video) + implementation(libs.androidx.core.ktx) + implementation(libs.androidx.lifecycle.runtime.ktx) + implementation(libs.androidx.activity.compose) + implementation(platform(libs.androidx.compose.bom)) + implementation(libs.androidx.ui) + implementation(libs.androidx.ui.graphics) + implementation(libs.androidx.ui.tooling.preview) + implementation(libs.androidx.material3) + implementation(libs.androidx.navigation.compose) - // include all available samples. - val samples: List by project.extra - samples.forEach { - implementation(project(it)) - } + implementation(libs.kotlinx.serialization.json) + implementation(libs.androidx.fragment.compose) - // Testing - kspAndroidTest(libs.hilt.compiler) - androidTestImplementation(platform(libs.compose.bom)) - androidTestImplementation(libs.androidx.navigation.testing) - androidTestImplementation(libs.compose.ui.test.manifest) - debugImplementation(libs.compose.ui.test.manifest) - androidTestImplementation(libs.compose.ui.test.junit4) - androidTestImplementation(libs.androidx.test.core) - androidTestImplementation(libs.androidx.test.espresso.core) - androidTestImplementation(libs.androidx.test.rules) - androidTestImplementation(libs.androidx.test.runner) - androidTestImplementation(libs.hilt.testing) - androidTestImplementation(libs.junit4) -} + implementation(project(":shared")) + implementation(project(":samples:accessibility")) + implementation(project(":samples:camera:camera2")) + implementation(project(":samples:connectivity:audio")) + implementation(project(":samples:connectivity:bluetooth:ble")) + implementation(project(":samples:connectivity:bluetooth:companion")) + implementation(project(":samples:connectivity:callnotification")) + implementation(project(":samples:connectivity:telecom")) + implementation(project(":samples:graphics:pdf")) + implementation(project(":samples:graphics:ultrahdr")) + implementation(project(":samples:location")) + implementation(project(":samples:media:ultrahdr")) + implementation(project(":samples:media:video")) + implementation(project(":samples:privacy:data")) + implementation(project(":samples:privacy:permissions")) + implementation(project(":samples:privacy:transparency")) + implementation(project(":samples:storage")) + implementation(project(":samples:user-interface:appwidgets")) + implementation(project(":samples:user-interface:constraintlayout")) + implementation(project(":samples:user-interface:draganddrop")) + implementation(project(":samples:user-interface:haptics")) + implementation(project(":samples:user-interface:picture-in-picture")) + implementation(project(":samples:user-interface:predictiveback")) + implementation(project(":samples:user-interface:quicksettings")) + implementation(project(":samples:user-interface:share")) + implementation(project(":samples:user-interface:text")) + implementation(project(":samples:user-interface:window-insets")) + implementation(project(":samples:user-interface:windowmanager")) -tasks.register("syncSamplesInfo", com.example.platform.plugin.SyncSamplesInfo::class.java) { - projectDir.set(project.rootDir) -} -// Link the assemble task to the sync task. -// TODO: move syncSamplesInfo task here, as this can break isolated projects -afterEvaluate { - tasks.findByName("assembleDebug")?.finalizedBy(tasks.findByName("syncSamplesInfo")) -} + testImplementation(libs.junit) + androidTestImplementation(libs.androidx.junit) + androidTestImplementation(libs.androidx.espresso.core) + androidTestImplementation(platform(libs.androidx.compose.bom)) + androidTestImplementation(libs.androidx.ui.test.junit4) + debugImplementation(libs.androidx.ui.tooling) + debugImplementation(libs.androidx.ui.test.manifest) +} \ No newline at end of file diff --git a/app/src/androidTest/java/com/example/platform/app/AppTestRunner.kt b/app/src/androidTest/java/com/example/platform/app/AppTestRunner.kt deleted file mode 100644 index 4b5ad9c8..00000000 --- a/app/src/androidTest/java/com/example/platform/app/AppTestRunner.kt +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 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 - * - * 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. - */ - -package com.example.platform.app - -import android.app.Application -import android.content.Context -import androidx.test.runner.AndroidJUnitRunner -import dagger.hilt.android.testing.HiltTestApplication - -/** - * A custom runner to set up the instrumented application class for tests. - */ -class AppTestRunner : AndroidJUnitRunner() { - override fun newApplication(cl: ClassLoader?, name: String?, context: Context?): Application { - return super.newApplication(cl, HiltTestApplication::class.java.name, context) - } -} diff --git a/app/src/androidTest/java/com/example/platform/app/NavigationTest.kt b/app/src/androidTest/java/com/example/platform/app/NavigationTest.kt deleted file mode 100644 index 8c816c46..00000000 --- a/app/src/androidTest/java/com/example/platform/app/NavigationTest.kt +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright 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 - * - * 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. - */ - -package com.example.platform.app - -import android.Manifest -import android.os.Build -import androidx.compose.ui.test.assertIsDisplayed -import androidx.compose.ui.test.hasScrollAction -import androidx.compose.ui.test.hasScrollToIndexAction -import androidx.compose.ui.test.hasText -import androidx.compose.ui.test.junit4.createAndroidComposeRule -import androidx.compose.ui.test.onFirst -import androidx.compose.ui.test.onNodeWithText -import androidx.compose.ui.test.performClick -import androidx.compose.ui.test.performScrollToKey -import androidx.test.espresso.Espresso -import androidx.test.rule.GrantPermissionRule -import dagger.hilt.android.testing.HiltAndroidRule -import dagger.hilt.android.testing.HiltAndroidTest -import org.junit.Rule -import org.junit.Test - -/** - * Tests all the samples are present and open. - * - * Note: consider changing the test to use the TestNavController to control navigation instead - */ -@HiltAndroidTest -class NavigationTest { - - /** - * Manages the components' state and is used to perform injection on your test - */ - @get:Rule(order = 0) - val hiltRule = HiltAndroidRule(this) - - /** - * Use the primary activity to initialize the app normally. - */ - @get:Rule(order = 2) - val composeTestRule = createAndroidComposeRule() - - /** - * Avoids showing permission dialog when running certain samples - */ - @get:Rule(order = 3) - val grantPermissionRule: GrantPermissionRule = GrantPermissionRule.grant( - Manifest.permission.ACCESS_FINE_LOCATION, - Manifest.permission.CAMERA, - ) - - @Test - fun testSamplesOpen() { - composeTestRule.apply { - val platformLabel = onNodeWithText(activity.getString(R.string.app_name)) - val scrollNode = onAllNodes(hasScrollAction() and hasScrollToIndexAction()).onFirst() - - // For each sample find it in the list, open it and go back - activity.catalogSamples.forEach { - // Skip disabled samples - if (Build.VERSION.SDK_INT >= it.minSDK) { - try { - scrollNode.performScrollToKey(it.route) - onNode(hasText(it.name) and hasText(it.description)).performClick() - - // Go back - Espresso.pressBack() - platformLabel.assertIsDisplayed() - } catch (e: Exception) { - throw Exception("Test failed in sample ${it.name}", e) - } - } - } - } - } -} diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 1de0a976..d2df3359 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -17,17 +17,15 @@ + android:roundIcon="@mipmap/ic_launcher" + android:supportsRtl="true"> + android:theme="@style/Theme.PlatformSamples"> @@ -36,10 +34,12 @@ - - + + diff --git a/app/src/main/java/com/example/platform/app/ApiSurface.kt b/app/src/main/java/com/example/platform/app/ApiSurface.kt new file mode 100644 index 00000000..a98278fe --- /dev/null +++ b/app/src/main/java/com/example/platform/app/ApiSurface.kt @@ -0,0 +1,214 @@ +/* + * Copyright 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 + * + * 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. + */ + +package com.example.platform.app + +import kotlinx.serialization.Serializable + +@Serializable +data class ApiSurface(override val id: String, override val name: String, override val description: String? = null) : CatalogItem + +val AccessiblityApiSurface = ApiSurface( + "accessiblity", + "Accessibility", + null, +) + +val CameraCamera2ApiSurface = ApiSurface( + "camera-camera2", + "Camera2", + null, +) + +val ConnectivityAudioApiSurface = ApiSurface( + "connectivity-audio", + "Connectivity Audio", + null, +) + +val ConnectivityBluetoothBleApiSurface = ApiSurface( + "connectivity-bluetooth-ble", + "Connectivity Bluetooth BLE", + null, +) + +val ConnectivityBluetoothCompanionApiSurface = ApiSurface( + "connectivity-bluetooth-companion", + "Connectivity Bluetooth Companion", + null, +) + +val ConnectivityCallNotificationApiSurface = ApiSurface( + "connectivity-call-notification", + "Connectivity Call Notification", + null, +) + +val ConnectivityTelecomApiSurface = ApiSurface( + "connectivity-telecom", + "Connectivity Telecom", + null, +) + +val GraphicsPdfApiSurface = ApiSurface( + "graphics-pdf", + "Graphics PDF", + null, +) + +val GraphicsUltraHdrApiSurface = ApiSurface( + "graphics-ultrahdr", + "Graphics UltraHDR", + null, +) + +val LocationApiSurface = ApiSurface( + "location", + "Location", + null, +) + +val MediaUltraHdrApiSurface = ApiSurface( + "media-ultrahdr", + "Media UltraHDR", + null, +) + +val MediaVideoApiSurface = ApiSurface( + "media-video", + "Media Video", + null, +) + +val PrivacyDataApiSurface = ApiSurface( + "privacy-data", + "Privacy Data", + null, +) + +val PrivacyPermissionsApiSurface = ApiSurface( + "privacy-permissions", + "Privacy Permissions", + null, +) + +val PrivacyTransparencyApiSurface = ApiSurface( + "privacy-transparency", + "Privacy Transparency", + null, +) + +val StorageApiSurface = ApiSurface( + "storage", + "Storage", + "Android photo library access capabilities.\nPhoto Picker for unified device and cloud photo access, and MediaStore for detailed local media querying", +) + +val UserInterfaceAppWidgetsApiSurface = ApiSurface( + "user-interface-app-widgets", + "User Interface - App Widgets", + null, +) + +val UserInterfaceConstraintLayoutApiSurface = ApiSurface( + "user-interface-constraint-layout", + "User Interface - Constraint Layout", + null, +) + +val UserInterfaceDragAndDropApiSurface = ApiSurface( + "user-interface-draganddrop", + "User Interface - Drag and Drop", + null, +) + +val UserInterfaceHapticsApiSurface = ApiSurface( + "user-interface-haptics", + "User Interface - Haptics", + null, +) + +val UserInterfacePictureInPictureApiSurface = ApiSurface( + "user-interface-picture-in-picture", + "User Interface - Picture In Picture", + null, +) + +val UserInterfacePredictiveBackApiSurface = ApiSurface( + "user-interface-predictive-back", + "User Interface - Predictive Back", + null, +) + +val UserInterfaceQuickSettingsApiSurface = ApiSurface( + "user-interface-quick-settings", + "User Interface - Quick Settings", + null, +) + +val UserInterfaceShareApiSurface = ApiSurface( + "user-interface-share", + "User Interface - Share", + null, +) + +val UserInterfaceTextApiSurface = ApiSurface( + "user-interface-text", + "User Interface - Text", + null, +) + +val UserInterfaceWindowInsetsApiSurface = ApiSurface( + "user-interface-window-insets", + "User Interface - Window Insets", + null, +) + +val UserInterfaceWindowManagerApiSurface = ApiSurface( + "user-interface-window-manager", + "User Interface - Window Manager", + null, +) + +val API_SURFACES = listOf( + AccessiblityApiSurface, + CameraCamera2ApiSurface, + ConnectivityAudioApiSurface, + ConnectivityBluetoothBleApiSurface, + ConnectivityBluetoothCompanionApiSurface, + ConnectivityCallNotificationApiSurface, + ConnectivityTelecomApiSurface, + GraphicsPdfApiSurface, + GraphicsUltraHdrApiSurface, + LocationApiSurface, + MediaUltraHdrApiSurface, + MediaVideoApiSurface, + PrivacyDataApiSurface, + PrivacyPermissionsApiSurface, + PrivacyTransparencyApiSurface, + StorageApiSurface, + UserInterfaceAppWidgetsApiSurface, + UserInterfaceConstraintLayoutApiSurface, + UserInterfaceDragAndDropApiSurface, + UserInterfaceHapticsApiSurface, + UserInterfacePictureInPictureApiSurface, + UserInterfacePredictiveBackApiSurface, + UserInterfaceQuickSettingsApiSurface, + UserInterfaceShareApiSurface, + UserInterfaceTextApiSurface, + UserInterfaceWindowInsetsApiSurface, + UserInterfaceWindowManagerApiSurface +).associateBy { it.id } \ No newline at end of file diff --git a/app/src/main/java/com/example/platform/app/CatalogScreen.kt b/app/src/main/java/com/example/platform/app/CatalogScreen.kt new file mode 100644 index 00000000..1a6e57af --- /dev/null +++ b/app/src/main/java/com/example/platform/app/CatalogScreen.kt @@ -0,0 +1,122 @@ +/* + * Copyright 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 + * + * 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. + */ + +package com.example.platform.app + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.filled.KeyboardArrowRight +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.OutlinedCard +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp + + +/** + * Represents an API surface or sample. + */ +interface CatalogItem { + val id: String + val name: String + val description: String? +} + +/** + * Screen rendering list of items with their descriptions (API surfaces or samples) + */ +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun CatalogScreen( + item: CatalogItem, + subItems: Map, + onNavigateToSubItem: (CatalogItem) -> Unit = {}, +) { + val list = remember { subItems.values.toList() } + + Scaffold( + topBar = { + TopAppBar( + title = { Text(item.name) }, + ) + }, + ) { paddingValues -> + LazyColumn( + modifier = Modifier + .padding(paddingValues) + .padding(8.dp), + ) { + item { + Text( + text = "Samples", + style = MaterialTheme.typography.bodyLarge, + ) + Spacer(modifier = Modifier.height(16.dp)) + } + items(list) { subItem -> + CatalogScreenItem(item = subItem, onClick = onNavigateToSubItem) + Spacer(modifier = Modifier.height(16.dp)) + } + } + } +} + +@Composable +fun CatalogScreenItem( + item: CatalogItem, + onClick: (CatalogItem) -> Unit, +) { + OutlinedCard(onClick = { onClick(item) }, modifier = Modifier.fillMaxWidth()) { + Row( + modifier = Modifier.padding(16.dp), + horizontalArrangement = Arrangement.spacedBy(16.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + Column( + modifier = Modifier.weight(1f), + verticalArrangement = Arrangement.spacedBy(8.dp), + ) { + Text(text = item.name, style = MaterialTheme.typography.titleSmall) + if (item.description != null) { + Text( + text = item.description!!, + style = MaterialTheme.typography.bodySmall, + ) + } + } + Icon( + imageVector = Icons.AutoMirrored.Default.KeyboardArrowRight, + contentDescription = null, + modifier = Modifier.align(Alignment.CenterVertically), + ) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/platform/app/MainActivity.kt b/app/src/main/java/com/example/platform/app/MainActivity.kt index a84b56d1..b1d066ae 100644 --- a/app/src/main/java/com/example/platform/app/MainActivity.kt +++ b/app/src/main/java/com/example/platform/app/MainActivity.kt @@ -16,34 +16,21 @@ package com.example.platform.app -import android.app.Application -import coil.ImageLoader -import coil.ImageLoaderFactory -import coil.decode.VideoFrameDecoder -import com.google.android.catalog.framework.ui.CatalogActivity -import dagger.hilt.android.AndroidEntryPoint -import dagger.hilt.android.HiltAndroidApp +import android.os.Bundle +import androidx.activity.ComponentActivity +import androidx.activity.compose.setContent +import androidx.activity.enableEdgeToEdge +import androidx.fragment.app.FragmentActivity +import com.example.platform.shared.theme.CatalogTheme -/** - * Main app for the platform samples catalog necessary for injecting the list of available samples - * - * Check [casa-android](https://github.com/google/casa-android#create-catalog-app) setup - */ -@HiltAndroidApp -class MainApp : Application(), ImageLoaderFactory { - - override fun newImageLoader(): ImageLoader { - return ImageLoader.Builder(this) - .components { - add(VideoFrameDecoder.Factory()) +class MainActivity : FragmentActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + enableEdgeToEdge() + setContent { + CatalogTheme { + NavGraph() } - .crossfade(true) - .build() + } } -} - -/** - * Entry point for the platform samples catalog using the [CatalogActivity]. - */ -@AndroidEntryPoint -class MainActivity : CatalogActivity() +} \ No newline at end of file diff --git a/samples/user-interface/appwidgets/src/main/java/com/example/platform/ui/appwidgets/glance/image/ImageFileProvider.kt b/app/src/main/java/com/example/platform/app/MainApp.kt similarity index 81% rename from samples/user-interface/appwidgets/src/main/java/com/example/platform/ui/appwidgets/glance/image/ImageFileProvider.kt rename to app/src/main/java/com/example/platform/app/MainApp.kt index a0151c87..751b61c8 100644 --- a/samples/user-interface/appwidgets/src/main/java/com/example/platform/ui/appwidgets/glance/image/ImageFileProvider.kt +++ b/app/src/main/java/com/example/platform/app/MainApp.kt @@ -1,3 +1,5 @@ +import android.app.Application + /* * Copyright 2023 The Android Open Source Project * @@ -14,8 +16,4 @@ * limitations under the License. */ -package com.example.platform.ui.appwidgets.glance.image - -import androidx.core.content.FileProvider - -class ImageFileProvider : FileProvider() \ No newline at end of file +class MainApp : Application() \ No newline at end of file diff --git a/app/src/main/java/com/example/platform/app/NavGraph.kt b/app/src/main/java/com/example/platform/app/NavGraph.kt new file mode 100644 index 00000000..c5ba5fc9 --- /dev/null +++ b/app/src/main/java/com/example/platform/app/NavGraph.kt @@ -0,0 +1,104 @@ +/* + * Copyright 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 + * + * 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. + */ + +package com.example.platform.app + +import android.content.Intent +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.unit.dp +import androidx.navigation.NavType +import androidx.navigation.compose.NavHost +import androidx.navigation.compose.composable +import androidx.navigation.compose.rememberNavController +import androidx.navigation.navArgument +import androidx.navigation.toRoute +import kotlinx.serialization.Serializable + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun NavGraph() { + val context = LocalContext.current + val navController = rememberNavController() + + NavHost(navController = navController, startDestination = MainScreen) { + composable { + CatalogScreen( + item = MainScreen, + subItems = API_SURFACES, + onNavigateToSubItem = { apiSurface -> navController.navigate(apiSurface as ApiSurface) }, + ) + } + composable { backStackEntry -> + val apiSurface = backStackEntry.toRoute() + val subItems = remember { SAMPLE_DEMOS.filter { it.value.apiSurface == apiSurface } } + + CatalogScreen( + item = apiSurface, + subItems = subItems, + onNavigateToSubItem = { sampleDemo -> + if (sampleDemo is ActivitySampleDemo) { + context.startActivity(Intent(context, sampleDemo.content)) + } else { + navController.navigate("$SAMPLE_DEMO_ROUTE/${sampleDemo.id}") + } + }, + ) + } + + composable( + "$SAMPLE_DEMO_ROUTE/{$SAMPLE_DEMO_ID}", + listOf(navArgument(SAMPLE_DEMO_ID) { type = NavType.StringType }), + ) { backStackEntry -> + val arguments = requireNotNull(backStackEntry.arguments) + val sampleDemoId = requireNotNull(arguments.getString(SAMPLE_DEMO_ID)) + val sampleDemo = SAMPLE_DEMOS.getValue(sampleDemoId) as ComposableSampleDemo + + Scaffold( + topBar = { TopAppBar(title = { Text(sampleDemo.name) }) }, + ) { paddingValues -> + Box( + modifier = Modifier + .padding(paddingValues) + .padding(8.dp) + .fillMaxSize(), + ) { + sampleDemo.content() + } + } + } + } +} + + +@Serializable +data object MainScreen : CatalogItem { + override val id = "main" + override val name = "Platform Samples" + override val description = null +} + +const val SAMPLE_DEMO_ROUTE = "sampledemo" +const val SAMPLE_DEMO_ID = "id" \ No newline at end of file diff --git a/app/src/main/java/com/example/platform/app/SampleDemo.kt b/app/src/main/java/com/example/platform/app/SampleDemo.kt new file mode 100644 index 00000000..1c87b11a --- /dev/null +++ b/app/src/main/java/com/example/platform/app/SampleDemo.kt @@ -0,0 +1,1128 @@ +/* + * Copyright 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 + * + * 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. + */ + +package com.example.platform.app + +import android.os.Build +import androidx.compose.runtime.Composable +import androidx.fragment.compose.AndroidFragment +import com.example.android.pip.PiPMovieActivity +import com.example.android.pip.PiPSampleActivity +import com.example.platform.accessibility.ColorContrast +import com.example.platform.accessibility.LiveRegionView +import com.example.platform.accessibility.SpeakableText +import com.example.platform.camera.imagecapture.Camera2ImageCapture +import com.example.platform.camera.imagecapture.Camera2UltraHDRCapture +import com.example.platform.camera.preview.Camera2Preview +import com.example.platform.connectivity.audio.AudioCommsSample +import com.example.platform.connectivity.bluetooth.ble.BLEScanIntentSample +import com.example.platform.connectivity.bluetooth.ble.ConnectGATTSample +import com.example.platform.connectivity.bluetooth.ble.FindBLEDevicesSample +import com.example.platform.connectivity.bluetooth.ble.server.GATTServerSample +import com.example.platform.connectivity.bluetooth.cdm.CompanionDeviceManagerSample +import com.example.platform.connectivity.callnotification.CallNotificationSample +import com.example.platform.connectivity.telecom.TelecomCallSample +import com.example.platform.graphics.pdf.PdfRendererScreen +import com.example.platform.graphics.ultrahdr.display.CompressingUltraHDRImages +import com.example.platform.graphics.ultrahdr.display.DisplayUltraHDRScreen +import com.example.platform.graphics.ultrahdr.display.DisplayingUltraHDR +import com.example.platform.graphics.ultrahdr.display.DisplayingUltraHDRUsing3PLibrary +import com.example.platform.graphics.ultrahdr.display.VisualizingAnUltraHDRGainmap +import com.example.platform.graphics.ultrahdr.edit.EditingUltraHDR +import com.example.platform.graphics.ultrahdr.opengl.UltraHDRWithOpenGL +import com.example.platform.location.bglocationaccess.BgLocationAccessScreen +import com.example.platform.location.currentLocation.CurrentLocationScreen +import com.example.platform.location.geofencing.GeofencingScreen +import com.example.platform.location.locationupdates.LocationUpdatesScreen +import com.example.platform.location.permission.LocationPermissionScreen +import com.example.platform.location.useractivityrecog.UserActivityRecognitionScreen +import com.example.platform.media.ultrahdr.video.UltraHDRToHDRVideo +import com.example.platform.media.video.TransformerTFLite +import com.example.platform.media.video.TransformerVideoComposition +import com.example.platform.privacy.data.PackageVisibility +import com.example.platform.privacy.permissions.ComposePermissions +import com.example.platform.privacy.permissions.MultiplePermissions +import com.example.platform.privacy.permissions.Permissionless +import com.example.platform.privacy.permissions.SinglePermission +import com.example.platform.privacy.transparency.DataAccessSample +import com.example.platform.privacy.transparency.ScreenshotDetectionSample +import com.example.platform.shared.MinSdkBox +import com.example.platform.storage.mediastore.MediaStoreQuerySample +import com.example.platform.storage.mediastore.SelectedPhotosAccessSample +import com.example.platform.storage.photopicker.PhotoPickerSample +import com.example.platform.ui.appwidgets.AppWidgets +import com.example.platform.ui.constraintlayout.AdvancedArrangementFragment +import com.example.platform.ui.constraintlayout.AdvancedChainsFragment +import com.example.platform.ui.constraintlayout.AspectRatioFragment +import com.example.platform.ui.constraintlayout.BasicArrangementFragment +import com.example.platform.ui.constraintlayout.BasicChainFragment +import com.example.platform.ui.constraintlayout.CenteringViewsFragment +import com.example.platform.ui.constraintlayout.ComplexMotion1Fragment +import com.example.platform.ui.constraintlayout.ComplexMotion2Fragment +import com.example.platform.ui.constraintlayout.ComplexMotion3Fragment +import com.example.platform.ui.constraintlayout.ComplexMotion4Fragment +import com.example.platform.ui.constraintlayout.ConstraintSetFragment +import com.example.platform.ui.constraintlayout.Coordinator1Fragment +import com.example.platform.ui.constraintlayout.Coordinator2Fragment +import com.example.platform.ui.constraintlayout.Coordinator3Fragment +import com.example.platform.ui.constraintlayout.CustomAttributeFragment +import com.example.platform.ui.constraintlayout.Drawer1Fragment +import com.example.platform.ui.constraintlayout.Drawer2Fragment +import com.example.platform.ui.constraintlayout.FragmentTransition2Fragment +import com.example.platform.ui.constraintlayout.FragmentTransitionFragment +import com.example.platform.ui.constraintlayout.GuidelinesFragment +import com.example.platform.ui.constraintlayout.ImageFilter1Fragment +import com.example.platform.ui.constraintlayout.ImageFilter2Fragment +import com.example.platform.ui.constraintlayout.KeyTriggerFragment +import com.example.platform.ui.constraintlayout.KeyframeCycleFragment +import com.example.platform.ui.constraintlayout.KeyframeInterpolationFragment +import com.example.platform.ui.constraintlayout.KeyframePositionFragment +import com.example.platform.ui.constraintlayout.LottieFragment +import com.example.platform.ui.constraintlayout.MotionBasic01Fragment +import com.example.platform.ui.constraintlayout.MotionBasic02Fragment +import com.example.platform.ui.constraintlayout.MotionBasic02NoAutoCompleteFragment +import com.example.platform.ui.constraintlayout.MultiStateFragment +import com.example.platform.ui.constraintlayout.ParallaxFragment +import com.example.platform.ui.constraintlayout.SidePanelFragment +import com.example.platform.ui.constraintlayout.ViewPagerFragment +import com.example.platform.ui.constraintlayout.YoutubeFragment +import com.example.platform.ui.draganddrop.DragAndDropCompose +import com.example.platform.ui.draganddrop.DragAndDropMultiWindow +import com.example.platform.ui.draganddrop.DragAndDropRichContentReceiverFragment +import com.example.platform.ui.draganddrop.DragAndDropWithHelper +import com.example.platform.ui.draganddrop.DragAndDropWithViews +import com.example.platform.ui.haptics.Bounce +import com.example.platform.ui.haptics.Expand +import com.example.platform.ui.haptics.HapticsBasic +import com.example.platform.ui.haptics.Resist +import com.example.platform.ui.haptics.Wobble +import com.example.platform.ui.insets.ImmersiveMode +import com.example.platform.ui.insets.WindowInsetsAnimationActivity +import com.example.platform.ui.predictiveback.PBHostingActivity +import com.example.platform.ui.quicksettings.QuickSettings +import com.example.platform.ui.share.receiver.ShareReceiverActivity +import com.example.platform.ui.share.sender.ShareSender +import com.example.platform.ui.text.ConversionSuggestions +import com.example.platform.ui.text.DownloadableFontsFragment +import com.example.platform.ui.text.Hyphenation +import com.example.platform.ui.text.LineBreak +import com.example.platform.ui.text.Linkify +import com.example.platform.ui.text.TextSpanFragment +import com.example.platform.ui.windowmanager.demos.WindowDemosActivity + +interface SampleDemo : CatalogItem { + override val id: String + override val name: String + override val description: String? + val documentation: String? + val minSdk: Int + val tags: List + val apiSurface: ApiSurface + val content: Any +} + +data class ComposableSampleDemo( + override val id: String, + override val name: String, + override val description: String? = null, + override val documentation: String? = null, + override val minSdk: Int = Build.VERSION_CODES.LOLLIPOP, + override val apiSurface: ApiSurface, + override val tags: List = emptyList(), + override val content: @Composable () -> Unit, +) : SampleDemo + +data class ActivitySampleDemo( + override val id: String, + override val name: String, + override val description: String? = null, + override val documentation: String? = null, + override val minSdk: Int = Build.VERSION_CODES.LOLLIPOP, + override val apiSurface: ApiSurface, + override val tags: List = emptyList(), + override val content: Class<*>, +) : SampleDemo + +val SAMPLE_DEMOS by lazy { + listOf( + ComposableSampleDemo( + id = "color-contrast", + name = "Color Contrast", + description = "This sample demonstrates the importance of proper color contrast and how to " + + "audit your app to ensure proper color contrast.", + documentation = "https://support.google.com/accessibility/android/answer/7158390", + apiSurface = AccessiblityApiSurface, + content = { ColorContrast() }, + ), + ComposableSampleDemo( + id = "live-region-view", + name = "Live Region (View)", + description = "Utilize LiveRegion to automatically notify users of accessibility services" + + " about changes to a view", + documentation = "https://developer.android.com/reference/android/view/View#attr_android:accessibilityLiveRegion", + apiSurface = AccessiblityApiSurface, + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "speakable-text", + name = "Speakable Text", + description = "The sample demonstrates the importance of having proper labels for" + + " interactive elements and how to audit your app for content label related " + + "improvements.", + documentation = "https://developer.android.com/guide/topics/ui/accessibility/apps#describe-ui-element", + apiSurface = AccessiblityApiSurface, + content = { SpeakableText() }, + ), + ComposableSampleDemo( + id = "image-capture", + name = "Image Capture", + description = "This sample demonstrates how to capture an image using Camera2 and encode it " + + "into a JPEG container.", + documentation = "https://developer.android.com/training/camera2/capture-sessions-requests", + apiSurface = CameraCamera2ApiSurface, + tags = listOf("Camera2"), + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "ultrahdr-image-capture", + name = "UltraHDR Image Capture", + description = "This sample demonstrates how to capture a 10-bit compressed still image and " + + "store it using the new UltraHDR image format using Camera2.", + documentation = "https://developer.android.com/guide/topics/media/hdr-image-format", + apiSurface = CameraCamera2ApiSurface, + tags = listOf("UltraHDR", "Camera2"), + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "ultrahdr-image-capture", + name = "Camera2 Preview", + description = "Demonstrates displaying processed pixel data directly from the camera sensor " + + "to the screen using Camera2.", + documentation = "https://developer.android.com/training/camera2", + apiSurface = CameraCamera2ApiSurface, + tags = listOf("Camera2"), + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "communication-audio-manager", + name = "Communication Audio Manager", + description = "This sample shows how to use audio manager to for Communication application that self-manage the call.", + documentation = "https://developer.android.com/guide/topics/connectivity/ble-audio/audio-manager", + minSdk = Build.VERSION_CODES.S, + apiSurface = ConnectivityAudioApiSurface, + tags = listOf("Audio"), + content = { + MinSdkBox(minSdk = Build.VERSION_CODES.S) { + //noinspection NewApi + AudioCommsSample() + } + }, + ), + ComposableSampleDemo( + id = "create-gatt-server", + name = "Create a GATT server", + description = "Shows how to create a GATT server and communicate with the GATT client", + documentation = "https://developer.android.com/reference/android/bluetooth/BluetoothGattServer", + apiSurface = ConnectivityBluetoothBleApiSurface, + tags = listOf("Bluetooth"), + content = { + MinSdkBox(minSdk = Build.VERSION_CODES.M) { + //noinspection NewApi + GATTServerSample() + } + }, + ), + ComposableSampleDemo( + id = "scan-with-ble-intent", + name = "Scan with BLE Intent", + description = "This samples shows how to use the BLE intent to scan for devices", + documentation = "https://developer.android.com/reference/android/bluetooth/le/BluetoothLeScanner#startScan(java.util.List%3Candroid.bluetooth.le.ScanFilter%3E,%20android.bluetooth.le.ScanSettings,%20android.app.PendingIntent)", + apiSurface = ConnectivityBluetoothBleApiSurface, + tags = listOf("Bluetooth"), + content = { + MinSdkBox(minSdk = Build.VERSION_CODES.O) { + //noinspection NewApi + BLEScanIntentSample() + } + }, + ), + ComposableSampleDemo( + id = "connect-gatt-server", + name = "Connect to a GATT server", + description = "Shows how to connect to a GATT server hosted by the BLE device and perform simple operations", + documentation = "https://developer.android.com/guide/topics/connectivity/bluetooth/connect-gatt-server", + apiSurface = ConnectivityBluetoothBleApiSurface, + tags = listOf("Bluetooth"), + content = { + MinSdkBox(minSdk = Build.VERSION_CODES.M) { + //noinspection NewApi + ConnectGATTSample() + } + }, + ), + ComposableSampleDemo( + id = "find-devices", + name = "Find devices", + description = "This example will demonstrate how to scanning for Low Energy Devices", + documentation = "https://developer.android.com/guide/topics/connectivity/bluetooth", + apiSurface = ConnectivityBluetoothBleApiSurface, + tags = listOf("Bluetooth"), + content = { + MinSdkBox(minSdk = Build.VERSION_CODES.M) { + //noinspection NewApi + FindBLEDevicesSample() + } + }, + ), + ComposableSampleDemo( + id = "companion-device-manager", + name = "Companion Device Manager", + description = "This samples shows how to use the CDM to pair and connect with BLE devices", + documentation = "https://developer.android.com/guide/topics/connectivity/companion-device-pairing", + apiSurface = ConnectivityBluetoothCompanionApiSurface, + tags = listOf("Bluetooth"), + content = { + MinSdkBox(minSdk = Build.VERSION_CODES.O) { + //noinspection NewApi + CompanionDeviceManagerSample() + } + }, + ), + ActivitySampleDemo( + id = "call-notification", + name = "Call Notification", + description = "Sample demonstrating how to make incoming call notifications and in call notifications", + documentation = "https://developer.android.com/reference/android/app/Notification.CallStyle", + apiSurface = ConnectivityCallNotificationApiSurface, + content = CallNotificationSample::class.java, + ), + ComposableSampleDemo( + id = "telecom-call", + name = "Telecom Call", + description = "A sample showcasing how to handle calls with the Jetpack Telecom API", + documentation = "https://developer.android.com/guide/topics/connectivity/telecom", + apiSurface = ConnectivityTelecomApiSurface, + content = { + MinSdkBox(minSdk = Build.VERSION_CODES.O) { + //noinspection NewApi + TelecomCallSample() + } + }, + ), + ComposableSampleDemo( + id = "pdf-renderer", + name = "PDF Renderer", + description = "Demonstrates how to use PdfRenderer to display PDF documents on the screen.", + documentation = null, + apiSurface = GraphicsPdfApiSurface, + content = { PdfRendererScreen() }, + ), + ComposableSampleDemo( + id = "compressing-ultrahdr-images", + name = "Compressing UltraHDR Images", + description = "This sample demonstrates displaying an UltraHDR image in a Compose View and an Android View", + documentation = "https://developer.android.com/guide/topics/media/hdr-image-format", + apiSurface = GraphicsUltraHdrApiSurface, + tags = listOf("UltraHDR"), + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "displaying-ultrahdr", + name = "Displaying UltraHDR", + description = "This sample demonstrates displaying an UltraHDR image.", + documentation = "https://developer.android.com/guide/topics/media/hdr-image-format", + apiSurface = GraphicsUltraHdrApiSurface, + tags = listOf("UltraHDR"), + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "displaying-ultrahdr", + name = "Displaying UltraHDR (3P Libraries)", + description = "This sample demonstrates using the various popular image loading library to" + + " detect the presence of a gainmap to enable HDR mode when displaying an UltraHDR image", + documentation = "https://github.com/bumptech/glide", + apiSurface = GraphicsUltraHdrApiSurface, + tags = listOf("UltraHDR"), + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "displaying-ultrahdr-compose", + name = "Displaying UltraHDR (Compose)", + description = "This sample demonstrates displaying an UltraHDR image in a Compose View and an Android View", + documentation = "https://developer.android.com/guide/topics/media/hdr-image-format", + apiSurface = GraphicsUltraHdrApiSurface, + tags = listOf("UltraHDR", "Compose"), + content = { + MinSdkBox(minSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { + //noinspection NewApi + DisplayUltraHDRScreen() + } + }, + ), + ComposableSampleDemo( + id = "visualizing-ultrahdr-gainmap", + name = "Visualizing an UltraHDR Gainmap", + description = "This sample demonstrates visualizing the underlying gainmap of an UltraHDR " + + "image, which reveals which parts of the image are enhanced by the gainmap.", + documentation = "https://developer.android.com/guide/topics/media/hdr-image-format", + apiSurface = GraphicsUltraHdrApiSurface, + tags = listOf("UltraHDR"), + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "editing-ultrahdr", + name = "Editing UltraHDR", + description = "This sample demonstrates editing an UltraHDR image and the resulting gainmap as well. Spatial edit operations like crop, rotate, scale are supported", + documentation = "https://developer.android.com/guide/topics/media/hdr-image-format", + apiSurface = GraphicsUltraHdrApiSurface, + tags = listOf("UltraHDR"), + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "ultrahdr-opengles-surfaceview", + name = "UltraHDR x OpenGLES SurfaceView", + description = "This sample demonstrates displaying an UltraHDR image via and OpenGL Pipeline " + + "and control the SurfaceView's rendering brightness.", + documentation = "https://developer.android.com/guide/topics/media/hdr-image-format", + apiSurface = GraphicsUltraHdrApiSurface, + tags = listOf("UltraHDR"), + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "location-background-location-updates", + name = "Location - Background Location updates", + description = "This Sample demonstrate how to access location and get location updates when app is in background", + documentation = "https://developer.android.com/training/location/background", + apiSurface = LocationApiSurface, + content = { BgLocationAccessScreen() }, + ), + ComposableSampleDemo( + id = "location-getting-current-location", + name = "Location - Getting Current Location", + description = "This Sample demonstrate how to request of current location", + documentation = "https://developer.android.com/training/location/retrieve-current", + apiSurface = LocationApiSurface, + content = { CurrentLocationScreen() }, + ), + ComposableSampleDemo( + id = "location-create-monitor-geofence", + name = "Location - Create and monitor Geofence", + description = "This Sample demonstrate best practices for Creating and monitoring geofence", + documentation = "https://developer.android.com/training/location/geofencing", + apiSurface = LocationApiSurface, + content = { GeofencingScreen() }, + ), + ComposableSampleDemo( + id = "location-updates", + name = "Location - Updates", + description = "This Sample demonstrate how to get location updates", + documentation = "https://developer.android.com/training/location/request-updates", + apiSurface = LocationApiSurface, + content = { LocationUpdatesScreen() }, + ), + ComposableSampleDemo( + id = "location-permissions", + name = "Location - Permissions", + description = "This Sample demonstrate best practices for Location Permission", + documentation = "https://developer.android.com/training/location/permissions", + apiSurface = LocationApiSurface, + tags = listOf("permissions"), + content = { + MinSdkBox(minSdk = Build.VERSION_CODES.Q) { + //noinspection NewApi + LocationPermissionScreen() + } + }, + ), + ComposableSampleDemo( + id = "location-user-activity-recognition", + name = "Location - User Activity Recognition", + description = "This Sample demonstrate detection of user activity like walking, driving, etc.", + documentation = "https://developer.android.com/training/location/transitions", + apiSurface = LocationApiSurface, + content = { UserActivityRecognitionScreen() }, + ), + ComposableSampleDemo( + id = "ultrahdr-to-hdr-video", + name = "UltraHDR to HDR Video", + description = "This sample demonstrates converting a series of UltraHDR images into a HDR " + + "video." + "The sample leverages GPU hardware acceleration to render and encode the " + + "images.", + documentation = "https://developer.android.com/guide/topics/media/hdr-image-format", + apiSurface = MediaUltraHdrApiSurface, + tags = listOf("UltraHDR"), + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "transformer-tflite", + name = "Transformer and TFLite", + description = "This sample demonstrates using Transformer with TFLite/RTLite by applying a selected art style to a video.", + documentation = "https://developer.android.com/guide/topics/media/transformer", + apiSurface = MediaVideoApiSurface, + tags = listOf("Transformer"), + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "video-composition-using-media3-transformer", + name = "Video Composition using Media3 Transformer", + description = "This sample demonstrates concatenation of two video assets and an image using Media3 Transformer library.", + documentation = "https://developer.android.com/guide/topics/media/transformer", + apiSurface = MediaVideoApiSurface, + tags = listOf("Transformer"), + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "package-visibility", + name = "Package Visibility", + description = "A sample that showcase how the package visibility queries affects the available packages", + documentation = "https://developer.android.com/training/package-visibility", + apiSurface = PrivacyDataApiSurface, + content = { PackageVisibility() }, + ), + ComposableSampleDemo( + id = "permissions-compose", + name = "Permissions using Compose", + description = "This sample showcases how to request permission using Accompanist in Compose", + documentation = "https://google.github.io/accompanist/permissions/", + apiSurface = PrivacyPermissionsApiSurface, + tags = listOf("Permissions"), + content = { ComposePermissions() }, + ), + ComposableSampleDemo( + id = "permissions-compose", + name = "Multiple Permissions", + description = "Shows the recommended flow to request multiple RELATED runtime permissions", + documentation = "https://developer.android.com/training/permissions/requesting", + apiSurface = PrivacyPermissionsApiSurface, + tags = listOf("Permissions"), + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "permissionless", + name = "Permissionless", + description = "This sample demonstrate how you can avoid requesting permission for certain actions by leveraging System APIs", + documentation = "https://developer.android.com/training/permissions/evaluating", + apiSurface = PrivacyPermissionsApiSurface, + tags = listOf("Permissions"), + content = { Permissionless() }, + ), + ComposableSampleDemo( + id = "single-permission", + name = "Single Permission", + description = "Shows the recommended flow to request single runtime permissions", + documentation = "https://developer.android.com/training/permissions/requesting", + apiSurface = PrivacyPermissionsApiSurface, + tags = listOf("Permissions"), + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "data-access", + name = "Data Access", + description = "Demonstrates how to implement data access auditing for your app to identify " + + "unexpected data access, even from third-party SDKs and libraries.", + documentation = "https://developer.android.com/guide/topics/data/audit-access", + apiSurface = PrivacyTransparencyApiSurface, + content = { + MinSdkBox(minSdk = Build.VERSION_CODES.R) { + //noinspection NewApi + DataAccessSample() + } + }, + ), + ComposableSampleDemo( + id = "screenshot-detection", + name = "Screenshot Detection", + description = "This sample shows how to detect that the user capture the screen in Android 14 onwards", + documentation = null, + apiSurface = PrivacyTransparencyApiSurface, + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "photo-picker", + name = "PhotoPicker", + description = "Select images/videos in a privacy-friendly way using the photo picker", + documentation = "https://developer.android.com/training/data-storage/shared/photopicker", + apiSurface = StorageApiSurface, + content = { PhotoPickerSample() }, + ), + ComposableSampleDemo( + id = "mediastore-query", + name = "MediaStore - Query", + description = "Query files indexed by MediaStore", + documentation = "https://developer.android.com/training/data-storage/shared/media#media_store", + apiSurface = StorageApiSurface, + content = { MediaStoreQuerySample() }, + ), + ComposableSampleDemo( + id = "selected-photos-access", + name = "Selected Photos Access", + description = "Check and request storage permissions", + documentation = "https://developer.android.com/about/versions/14/changes/partial-photo-video-access", + apiSurface = StorageApiSurface, + content = { SelectedPhotosAccessSample() }, + ), + ComposableSampleDemo( + id = "app-widgets", + name = "App Widgets", + description = "Showcases how to pin widgets within the app and provides a catalog of well-designed canonical widget layouts for inspiration.", + documentation = "https://developer.android.com/develop/ui/views/appwidgets/overview", + apiSurface = UserInterfaceAppWidgetsApiSurface, + tags = listOf("App Widgets"), + content = { + MinSdkBox(minSdk = Build.VERSION_CODES.O) { + //noinspection NewApi + AppWidgets() + } + }, + ), + ComposableSampleDemo( + id = "constraintlayout-centering-views", + name = "ConstraintLayout - 1. Centering Views", + description = "Center child views horizontally or vertically.", + documentation = "https://developer.android.com/develop/ui/views/layout/constraint-layout", + apiSurface = UserInterfaceConstraintLayoutApiSurface, + tags = listOf("ConstraintLayout"), + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "constraintlayout-basic-arrangement", + name = "ConstraintLayout - 2. Basic arrangement", + description = "Arrange positions of child views relative to other views.", + documentation = "https://developer.android.com/develop/ui/views/layout/constraint-layout", + apiSurface = UserInterfaceConstraintLayoutApiSurface, + tags = listOf("ConstraintLayout"), + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "constraintlayout-advanced-arrangement", + name = "ConstraintLayout - 3. Advanced arrangement", + description = "More arrangement options.", + documentation = "https://developer.android.com/develop/ui/views/layout/constraint-layout", + apiSurface = UserInterfaceConstraintLayoutApiSurface, + tags = listOf("ConstraintLayout"), + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "constraintlayout-aspect-ratio", + name = "ConstraintLayout - 4. Aspect ratio", + description = "Specify aspect ratio for the dimensions of the child views.", + documentation = "https://developer.android.com/develop/ui/views/layout/constraint-layout", + apiSurface = UserInterfaceConstraintLayoutApiSurface, + tags = listOf("ConstraintLayout"), + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "constraintlayout-basic-chains", + name = "ConstraintLayout - 5. Basic chains", + description = "Use chains to arrange multiple child views horizontally or vertically.", + documentation = "https://developer.android.com/develop/ui/views/layout/constraint-layout", + apiSurface = UserInterfaceConstraintLayoutApiSurface, + tags = listOf("ConstraintLayout"), + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "constraintlayout-advanced-chains", + name = "ConstraintLayout - 5. Advanced chains", + description = "Use chains to arrange multiple child views horizontally or vertically.", + documentation = "https://developer.android.com/develop/ui/views/layout/constraint-layout", + apiSurface = UserInterfaceConstraintLayoutApiSurface, + tags = listOf("ConstraintLayout"), + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "constraintlayout-constraintset", + name = "ConstraintLayout - 7. ConstraintSet", + description = "Use ConstraintSet to specify multiple constraints to all the child views.", + documentation = "https://developer.android.com/develop/ui/views/layout/constraint-layout", + apiSurface = UserInterfaceConstraintLayoutApiSurface, + tags = listOf("ConstraintLayout"), + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "constraintlayout-guidelines", + name = "ConstraintLayout - 8. Guidelines", + description = "Use a horizontal or vertical guideline to apply constraints to child views.", + documentation = "https://developer.android.com/develop/ui/views/layout/constraint-layout", + apiSurface = UserInterfaceConstraintLayoutApiSurface, + tags = listOf("ConstraintLayout"), + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "motionlayout-01-basic", + name = "MotionLayout - 01. Basic", + description = "Basic motion example using referenced ConstraintLayout files", + documentation = "https://developer.android.com/develop/ui/views/animations/motionlayout", + apiSurface = UserInterfaceConstraintLayoutApiSurface, + tags = listOf("MotionLayout"), + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "motionlayout-02-basic", + name = "MotionLayout - 02. Basic", + description = "Basic motion example using ConstraintSets defined in the MotionScene file", + documentation = "https://developer.android.com/develop/ui/views/animations/motionlayout", + apiSurface = UserInterfaceConstraintLayoutApiSurface, + tags = listOf("MotionLayout"), + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "motionlayout-02-basic-no-auto-complete", + name = "MotionLayout - 02. Basic, no auto complete", + description = "Basic motion example same as 2, but autoComplete is set to false in onSwipe", + documentation = "https://developer.android.com/develop/ui/views/animations/motionlayout", + apiSurface = UserInterfaceConstraintLayoutApiSurface, + tags = listOf("MotionLayout"), + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "motionlayout-03-custom-attribute", + name = "MotionLayout - 03. Custom attribute", + description = "Show color interpolation (custom attribute)", + documentation = "https://developer.android.com/develop/ui/views/animations/motionlayout", + apiSurface = UserInterfaceConstraintLayoutApiSurface, + tags = listOf("MotionLayout"), + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "motionlayout-04-imagefilterview-1", + name = "MotionLayout - 04. ImageFilterView 1", + description = "Show image cross-fade (using ML's ImageFilterView + custom attribute)", + documentation = "https://developer.android.com/develop/ui/views/animations/motionlayout", + apiSurface = UserInterfaceConstraintLayoutApiSurface, + tags = listOf("MotionLayout"), + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "motionlayout-05-imagefilterview-2", + name = "MotionLayout - 05. ImageFilterView 2", + description = "Show image saturation transition (using ML's ImageFilterView + custom attribute)", + documentation = "https://developer.android.com/develop/ui/views/animations/motionlayout", + apiSurface = UserInterfaceConstraintLayoutApiSurface, + tags = listOf("MotionLayout"), + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "motionlayout-06-keyframe-position", + name = "MotionLayout - 06. Keyframe position", + description = "Use a simple keyframe to change the interpolated motion", + documentation = "https://developer.android.com/develop/ui/views/animations/motionlayout", + apiSurface = UserInterfaceConstraintLayoutApiSurface, + tags = listOf("MotionLayout"), + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "motionlayout-07-keyframe-interpolation", + name = "MotionLayout - 07. Keyframe interpolation", + description = "More complex keyframe, adding rotation interpolation", + documentation = "https://developer.android.com/develop/ui/views/animations/motionlayout", + apiSurface = UserInterfaceConstraintLayoutApiSurface, + tags = listOf("MotionLayout"), + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "motionlayout-08-keyframe-cycle", + name = "MotionLayout - 08. Keyframe cycle", + description = "Basic example of using a keyframe cycle", + documentation = "https://developer.android.com/develop/ui/views/animations/motionlayout", + apiSurface = UserInterfaceConstraintLayoutApiSurface, + tags = listOf("MotionLayout"), + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "motionlayout-09-coordinatorlayout-1", + name = "MotionLayout - 09. CoordinatorLayout 1", + description = "Basic example of using MotionLayout instead of AppBarLayout", + documentation = "https://developer.android.com/develop/ui/views/animations/motionlayout", + apiSurface = UserInterfaceConstraintLayoutApiSurface, + tags = listOf("MotionLayout"), + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "motionlayout-10-coordinatorlayout-2", + name = "MotionLayout - 10. CoordinatorLayout 2", + description = "Slightly more complex example of MotionLayout replacing AppBarLayout, with multiple elements and parallax background", + documentation = "https://developer.android.com/develop/ui/views/animations/motionlayout", + apiSurface = UserInterfaceConstraintLayoutApiSurface, + tags = listOf("MotionLayout"), + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "motionlayout-11-coordinatorlayout-3", + name = "MotionLayout - 11. CoordinatorLayout 3", + description = "Another AppBarLayout replacement example", + documentation = "https://developer.android.com/develop/ui/views/animations/motionlayout", + apiSurface = UserInterfaceConstraintLayoutApiSurface, + tags = listOf("MotionLayout"), + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "motionlayout-12-drawerlayout-1", + name = "MotionLayout - 12. DrawerLayout 1", + description = "Basic DrawerLayout with motionlayout", + documentation = "https://developer.android.com/develop/ui/views/animations/motionlayout", + apiSurface = UserInterfaceConstraintLayoutApiSurface, + tags = listOf("MotionLayout"), + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "motionlayout-13-drawerlayout-2", + name = "MotionLayout - 13. DrawerLayout 2", + description = "Advanced DrawerLayout with motionlayout", + documentation = "https://developer.android.com/develop/ui/views/animations/motionlayout", + apiSurface = UserInterfaceConstraintLayoutApiSurface, + tags = listOf("MotionLayout"), + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "motionlayout-14-sidepanel", + name = "MotionLayout - 14. SidePanel", + description = "Side Panel, implemented with MotionLayout only", + documentation = "https://developer.android.com/develop/ui/views/animations/motionlayout", + apiSurface = UserInterfaceConstraintLayoutApiSurface, + tags = listOf("MotionLayout"), + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "motionlayout-15-parallax", + name = "MotionLayout - 15. Parallax", + description = "Parallax background. Drag the car.", + documentation = "https://developer.android.com/develop/ui/views/animations/motionlayout", + apiSurface = UserInterfaceConstraintLayoutApiSurface, + tags = listOf("MotionLayout"), + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "motionlayout-16-viewpager", + name = "MotionLayout - 16. ViewPager", + description = "Using MotionLayout with ViewPager", + documentation = "https://developer.android.com/develop/ui/views/animations/motionlayout", + apiSurface = UserInterfaceConstraintLayoutApiSurface, + tags = listOf("MotionLayout"), + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "motionlayout-17-complex-motion-1", + name = "MotionLayout - 17. Complex Motion 1", + description = "Basic CoordinatorLayout-like behavior. Implemented with MotionLayout only, using a moving guideline. Note the view isn't resized.", + documentation = "https://developer.android.com/develop/ui/views/animations/motionlayout", + apiSurface = UserInterfaceConstraintLayoutApiSurface, + tags = listOf("MotionLayout"), + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "motionlayout-18-complex-motion-2", + name = "MotionLayout - 18. Complex Motion 2", + description = "Advanced CoordinatorLayout-like behavior (adding a FAB). Implemented with MotionLayout only, using a moving guideline. Note the view isn't resized.", + documentation = "https://developer.android.com/develop/ui/views/animations/motionlayout", + apiSurface = UserInterfaceConstraintLayoutApiSurface, + tags = listOf("MotionLayout"), + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "motionlayout-19-complex-motion-3", + name = "MotionLayout - 19. Complex Motion 3", + description = "Advanced CoordinatorLayout-like behavior (adding a FAB). Implemented with MotionLayout only, using direct resizing of the view.", + documentation = "https://developer.android.com/develop/ui/views/animations/motionlayout", + apiSurface = UserInterfaceConstraintLayoutApiSurface, + tags = listOf("MotionLayout"), + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "motionlayout-20-complex-motion-4", + name = "MotionLayout - 20. Complex Motion 4", + description = "Advanced Synchronized reveal motion + helper (bounce). Implemented with MotionLayout only.", + documentation = "https://developer.android.com/develop/ui/views/animations/motionlayout", + apiSurface = UserInterfaceConstraintLayoutApiSurface, + tags = listOf("MotionLayout"), + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "motionlayout-21-fragment-transition-1", + name = "MotionLayout - 21. Fragment transition 1", + description = "Using MotionLayout with ViewPager", + documentation = "https://developer.android.com/develop/ui/views/animations/motionlayout", + apiSurface = UserInterfaceConstraintLayoutApiSurface, + tags = listOf("MotionLayout"), + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "motionlayout-22-fragment-transition-2", + name = "MotionLayout - 22. Fragment transition 2", + description = "Using MotionLayout with ViewPager", + documentation = "https://developer.android.com/develop/ui/views/animations/motionlayout", + apiSurface = UserInterfaceConstraintLayoutApiSurface, + tags = listOf("MotionLayout"), + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "motionlayout-23-lottie", + name = "MotionLayout - 23. Lottie", + description = "Using MotionLayout and Lottie with ViewPager", + documentation = "https://developer.android.com/develop/ui/views/animations/motionlayout", + apiSurface = UserInterfaceConstraintLayoutApiSurface, + tags = listOf("MotionLayout"), + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "motionlayout-24-youtube-like-motion", + name = "MotionLayout - 24. YouTube-like motion", + description = "Example showing a transition like YouTube", + documentation = "https://developer.android.com/develop/ui/views/animations/motionlayout", + apiSurface = UserInterfaceConstraintLayoutApiSurface, + tags = listOf("MotionLayout"), + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "motionlayout-25-keytrigger", + name = "MotionLayout - 25. KeyTrigger", + description = "Example that calls a method using KeyTrigger", + documentation = "https://developer.android.com/develop/ui/views/animations/motionlayout", + apiSurface = UserInterfaceConstraintLayoutApiSurface, + tags = listOf("MotionLayout"), + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "motionlayout-26-multi-state", + name = "MotionLayout - 26. Multi-state", + description = "Example that transitions between multiple states", + documentation = "https://developer.android.com/develop/ui/views/animations/motionlayout", + apiSurface = UserInterfaceConstraintLayoutApiSurface, + tags = listOf("MotionLayout"), + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "drag-and-drop-in-multiwindow-mode", + name = "Drag and Drop in MultiWindow mode", + description = "Drag and drop to another app visible in multiwindow mode", + documentation = "https://developer.android.com/develop/ui/views/touch-and-input/drag-drop/multi-window", + apiSurface = UserInterfaceDragAndDropApiSurface, + tags = listOf("MotionLayout"), + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "drag-and-drop-richcontentreceiver", + name = "Drag and Drop using the RichContentReceiver", + description = "Using RichContentReceiverInterface for implementing Drop for rich data types", + documentation = "https://developer.android.com/develop/ui/views/receive-rich-content", + apiSurface = UserInterfaceDragAndDropApiSurface, + tags = listOf("MotionLayout"), + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "drag-and-drop-compose", + name = "Drag and Drop in Compose", + description = "Drag and drop in Compose", + documentation = null, + apiSurface = UserInterfaceDragAndDropApiSurface, + tags = listOf("MotionLayout"), + content = { DragAndDropCompose() }, + ), + ComposableSampleDemo( + id = "drag-and-drop-helper", + name = "Drag and Drop - Helper", + description = "Drag and Drop using the DragHelper and DropHelper from DragAndDropHelper library", + documentation = "https://developer.android.com/develop/ui/views/touch-and-input/drag-drop#drophelper", + apiSurface = UserInterfaceDragAndDropApiSurface, + tags = listOf("MotionLayout"), + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "drag-and-drop-views", + name = "Drag and Drop using views", + description = "Drag and Drop using the views", + documentation = "https://developer.android.com/develop/ui/views/touch-and-input/drag-drop/view", + apiSurface = UserInterfaceDragAndDropApiSurface, + tags = listOf("MotionLayout"), + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "haptics-1-vibration-effects", + name = "Haptics - 1. Vibration effects", + description = "Shows various vibration effects.", + documentation = "https://source.android.com/docs/core/interaction/haptics", + apiSurface = UserInterfaceHapticsApiSurface, + tags = listOf("Haptics"), + content = { HapticsBasic() } + ), + ComposableSampleDemo( + id = "haptics-2-resist", + name = "Haptics - 2. Resist", + description = "Simulates resistance by increasing the intensity and reducing the duration between vibration effects.", + documentation = "https://source.android.com/docs/core/interaction/haptics", + apiSurface = UserInterfaceHapticsApiSurface, + tags = listOf("Haptics"), + content = { Resist() } + ), + ComposableSampleDemo( + id = "haptics-3-expand", + name = "Haptics - 3. Expand", + description = "Expands and collapses a circle with haptics with an added tick to sharpen that the animation has ended.", + documentation = "https://source.android.com/docs/core/interaction/haptics", + apiSurface = UserInterfaceHapticsApiSurface, + tags = listOf("Haptics"), + content = { Expand() } + ), + ComposableSampleDemo( + id = "haptics-4-bounce", + name = "Haptics - 4. Bounce", + description = "Play primitive effects to simulate physical interactions.", + documentation = "https://source.android.com/docs/core/interaction/haptics", + apiSurface = UserInterfaceHapticsApiSurface, + tags = listOf("Haptics"), + content = { Bounce() } + ), + ComposableSampleDemo( + id = "haptics-5-wobble", + name = "Haptics - 5. Wobble", + description = "Play primitive effects to simulate physical interactions.", + documentation = "https://source.android.com/docs/core/interaction/haptics", + apiSurface = UserInterfaceHapticsApiSurface, + tags = listOf("Haptics"), + content = { Wobble() } + ), + ActivitySampleDemo( + id = "picture-in-picture-video-playback", + name = "Picture in Picture (PiP) - Video playback", + description = "Basic usage of Picture-in-Picture mode showcasing video playback", + documentation = "https://developer.android.com/develop/ui/views/picture-in-picture", + apiSurface = UserInterfacePictureInPictureApiSurface, + content = PiPMovieActivity::class.java + ), + ActivitySampleDemo( + id = "picture-in-picture-stopwatch", + name = "Picture in Picture (PiP) - Stopwatch", + description = "Basic usage of Picture-in-Picture mode showcasing a stopwatch", + documentation = "https://developer.android.com/develop/ui/views/picture-in-picture", + apiSurface = UserInterfacePictureInPictureApiSurface, + content = PiPSampleActivity::class.java + ), + ActivitySampleDemo( + id = "predictive-back", + name = "Predictive Back", + description = "Shows Predictive Back animations.", + documentation = "https://developer.android.com/about/versions/14/features/predictive-back", + apiSurface = UserInterfacePredictiveBackApiSurface, + content = PBHostingActivity::class.java + ), + ComposableSampleDemo( + id = "quick-settings", + name = "Quick Settings", + description = "Add your custom tile to the Quick Settings.", + documentation = "https://developer.android.com/develop/ui/views/quicksettings-tiles", + apiSurface = UserInterfaceQuickSettingsApiSurface, + content = { + MinSdkBox(minSdk = Build.VERSION_CODES.N) { + //noinspection NewApi + QuickSettings() + } + }, + ), + ActivitySampleDemo( + id = "receive-data-shared-by-other-apps", + name = "Receive data shared by other apps", + description = "Receive texts and images from other apps.", + documentation = null, + apiSurface = UserInterfaceShareApiSurface, + content = ShareReceiverActivity::class.java + ), + ComposableSampleDemo( + id = "send-data-with-sharesheet", + name = "Send data with sharesheet", + description = "Send texts and images to other apps using the Android Sharesheet.", + documentation = null, + apiSurface = UserInterfaceShareApiSurface, + content = { ShareSender() } + ), + ComposableSampleDemo( + id = "conversion-suggestions", + name = "Conversion suggestions", + description = "Demonstrates how to implement the incremental search feature for non-alphabet languages with the Conversion Suggestions API.", + documentation = "https://developer.android.com/about/versions/13/features#text-conversion", + apiSurface = UserInterfaceTextApiSurface, + tags = listOf("Text"), + content = { AndroidFragment() } + ), + ComposableSampleDemo( + id = "downloadable-fonts", + name = "Downloadable Fonts", + description = "Download fonts instead of bundling them in the app resources.", + documentation = "https://developer.android.com/develop/ui/views/text-and-emoji/downloadable-fonts", + apiSurface = UserInterfaceTextApiSurface, + tags = listOf("Text"), + content = { AndroidFragment() } + ), + ComposableSampleDemo( + id = "hyphenation", + name = "Hyphenation", + description = "Demonstrates different options for the `android:hyphenationFrequency` attribute", + documentation = "https://developer.android.com/reference/android/widget/TextView#attr_android:hyphenationFrequency", + apiSurface = UserInterfaceTextApiSurface, + tags = listOf("Text"), + content = { AndroidFragment() } + ), + ComposableSampleDemo( + id = "line-break", + name = "LineBreak", + description = "Demonstrates different options for the `android:lineBreakWordStyle` attribute.", + documentation = "https://developer.android.com/about/versions/13/features#japanese-wrapping", + apiSurface = UserInterfaceTextApiSurface, + tags = listOf("Text"), + content = { AndroidFragment() } + ), + ComposableSampleDemo( + id = "linkify", + name = "Linkify", + description = "Linkify is useful for creating links in TextViews.", + documentation = "https://developer.android.com/reference/kotlin/androidx/core/text/util/LinkifyCompat", + apiSurface = UserInterfaceTextApiSurface, + tags = listOf("Text"), + content = { AndroidFragment() } + ), + ComposableSampleDemo( + id = "text-span", + name = "TextSpan", + description = "buildSpannedString is useful for quickly building a rich text.", + documentation = "https://developer.android.com/kotlin/ktx#core", + apiSurface = UserInterfaceTextApiSurface, + tags = listOf("Text"), + content = { AndroidFragment() } + ), + ComposableSampleDemo( + id = "immersive-mode", + name = "Immersive mode", + description = "Immersive mode enables your app to display full-screen by hiding system bars.", + documentation = "https://developer.android.com/develop/ui/views/layout/immersive", + apiSurface = UserInterfaceWindowInsetsApiSurface, + content = { ImmersiveMode() } + ), + ActivitySampleDemo( + id = "window-insets-animation", + name = "WindowInsetsAnimation", + description = "Shows how to react to the on-screen keyboard (IME) changing visibility, and also controlling the IME's visibility.", + documentation = "https://developer.android.com/develop/ui/views/layout/sw-keyboard", + apiSurface = UserInterfaceWindowInsetsApiSurface, + content = WindowInsetsAnimationActivity::class.java + ), + ActivitySampleDemo( + id = "window-manager", + name = "WindowManager", + description = "Demonstrates how to use the Jetpack WindowManager library.", + documentation = "https://developer.android.com/jetpack/androidx/releases/window", + apiSurface = UserInterfaceWindowManagerApiSurface, + content = WindowDemosActivity::class.java + ), + ).associateBy { it.id } +} \ No newline at end of file diff --git a/app/src/main/res/drawable-v24/ic_launcher_background.xml b/app/src/main/res/drawable-v24/ic_launcher_background.xml index c1920261..59087dcc 100644 --- a/app/src/main/res/drawable-v24/ic_launcher_background.xml +++ b/app/src/main/res/drawable-v24/ic_launcher_background.xml @@ -1,5 +1,4 @@ - - - - - \ No newline at end of file + + +