diff --git a/samples/user-interface/canonical-layouts/.gitignore b/samples/user-interface/canonical-layouts/.gitignore new file mode 100644 index 00000000..1bc29aaf --- /dev/null +++ b/samples/user-interface/canonical-layouts/.gitignore @@ -0,0 +1,16 @@ +*.iml +.gradle +/local.properties +/.idea/caches +/.idea/libraries +/.idea/modules.xml +/.idea/workspace.xml +/.idea/navEditor.xml +/.idea/assetWizardSettings.xml +/.idea +.DS_Store +/build +/captures +.externalNativeBuild +.cxx +local.properties diff --git a/samples/user-interface/canonical-layouts/README.md b/samples/user-interface/canonical-layouts/README.md new file mode 100644 index 00000000..e75fb557 --- /dev/null +++ b/samples/user-interface/canonical-layouts/README.md @@ -0,0 +1,29 @@ +# Large screen canonical layouts + +The large screen canonical layouts are proven design patterns that provide an optimal user experience on large screen devices. The layouts are responsive and adaptive, supporting small screen phones as well as tablets, foldables, and ChromeOS devices. + +For more information see the [Large screen canonical layouts](https://developer.android.com/guide/topics/large-screens/large-screen-canonical-layouts) developer guide. + +## Installation + +Clone the [user-interface-samples](https://github.com/android/user-interface-samples) repository, then open the canonical layout samples as projects in Android Studio. + +## Available Canonical layouts and related implementations +- List-Detail + - [Activities](./list-detail-activity-embedding) + - [Views and Fragments](./list-detail-sliding-pane) + - [Jetpack Compose](./list-detail-compose) +- Feed + - [Views and Fragments](./feed-view) + - [Jetpack Compose](./feed-compose) +- Supporting Pane + - [Views](./supporting-pane-views) + - [Fragments](./supporting-pane-fragments) + - [Jetpack Compose](./supporting-pane-compose) + +## Support + +Stack Overflow: http://stackoverflow.com/questions/tagged/android + +If you find an error in the samples, please file an issue at https://github.com/android/user-interface-samples. + diff --git a/samples/user-interface/canonical-layouts/feed-compose/.gitignore b/samples/user-interface/canonical-layouts/feed-compose/.gitignore new file mode 100644 index 00000000..aa724b77 --- /dev/null +++ b/samples/user-interface/canonical-layouts/feed-compose/.gitignore @@ -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 diff --git a/samples/user-interface/canonical-layouts/feed-compose/.idea/.gitignore b/samples/user-interface/canonical-layouts/feed-compose/.idea/.gitignore new file mode 100644 index 00000000..26d33521 --- /dev/null +++ b/samples/user-interface/canonical-layouts/feed-compose/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/samples/user-interface/canonical-layouts/feed-compose/.idea/appInsightsSettings.xml b/samples/user-interface/canonical-layouts/feed-compose/.idea/appInsightsSettings.xml new file mode 100644 index 00000000..371f2e29 --- /dev/null +++ b/samples/user-interface/canonical-layouts/feed-compose/.idea/appInsightsSettings.xml @@ -0,0 +1,26 @@ + + + + + + \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/feed-compose/.idea/compiler.xml b/samples/user-interface/canonical-layouts/feed-compose/.idea/compiler.xml new file mode 100644 index 00000000..b589d56e --- /dev/null +++ b/samples/user-interface/canonical-layouts/feed-compose/.idea/compiler.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/feed-compose/.idea/deploymentTargetSelector.xml b/samples/user-interface/canonical-layouts/feed-compose/.idea/deploymentTargetSelector.xml new file mode 100644 index 00000000..b268ef36 --- /dev/null +++ b/samples/user-interface/canonical-layouts/feed-compose/.idea/deploymentTargetSelector.xml @@ -0,0 +1,10 @@ + + + + + + + + + \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/feed-compose/.idea/kotlinc.xml b/samples/user-interface/canonical-layouts/feed-compose/.idea/kotlinc.xml new file mode 100644 index 00000000..6d0ee1c2 --- /dev/null +++ b/samples/user-interface/canonical-layouts/feed-compose/.idea/kotlinc.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/feed-compose/.idea/migrations.xml b/samples/user-interface/canonical-layouts/feed-compose/.idea/migrations.xml new file mode 100644 index 00000000..f8051a6f --- /dev/null +++ b/samples/user-interface/canonical-layouts/feed-compose/.idea/migrations.xml @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/feed-compose/.idea/misc.xml b/samples/user-interface/canonical-layouts/feed-compose/.idea/misc.xml new file mode 100644 index 00000000..0ad17cbd --- /dev/null +++ b/samples/user-interface/canonical-layouts/feed-compose/.idea/misc.xml @@ -0,0 +1,10 @@ + + + + + + + + + \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/feed-compose/.idea/studiobot.xml b/samples/user-interface/canonical-layouts/feed-compose/.idea/studiobot.xml new file mode 100644 index 00000000..539e3b80 --- /dev/null +++ b/samples/user-interface/canonical-layouts/feed-compose/.idea/studiobot.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/feed-compose/.idea/vcs.xml b/samples/user-interface/canonical-layouts/feed-compose/.idea/vcs.xml new file mode 100644 index 00000000..b2bdec2d --- /dev/null +++ b/samples/user-interface/canonical-layouts/feed-compose/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/feed-compose/app/.gitignore b/samples/user-interface/canonical-layouts/feed-compose/app/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/samples/user-interface/canonical-layouts/feed-compose/app/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/feed-compose/app/build.gradle b/samples/user-interface/canonical-layouts/feed-compose/app/build.gradle new file mode 100644 index 00000000..08ea2360 --- /dev/null +++ b/samples/user-interface/canonical-layouts/feed-compose/app/build.gradle @@ -0,0 +1,90 @@ +/* + * 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 + * + * 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. + */ + +plugins { + id 'com.android.application' + id 'org.jetbrains.kotlin.android' + id("org.jetbrains.kotlin.plugin.compose") version "2.0.0" +} + +android { + compileSdk 35 + + defaultConfig { + applicationId "com.example.feed_compose" + minSdk 21 + targetSdk 35 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + vectorDrawables { + useSupportLibrary true + } + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + kotlinOptions { + jvmTarget = '1.8' + } + buildFeatures { + compose true + } + packagingOptions { + resources { + excludes += '/META-INF/{AL2.0,LGPL2.1}' + } + } + namespace 'com.example.feedcompose' +} + +composeCompiler { + enableStrongSkippingMode = true + + reportsDestination = layout.buildDirectory.dir("compose_compiler") +} + +dependencies { + def composeBom = platform('androidx.compose:compose-bom:2024.09.00') + implementation(composeBom) + androidTestImplementation(composeBom) + + implementation 'androidx.core:core-ktx:1.13.1' + implementation "androidx.compose.ui:ui" + implementation 'androidx.compose.material3:material3' + implementation 'androidx.compose.material3:material3-window-size-class' + implementation "androidx.compose.ui:ui-tooling-preview" + implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.8.5' + implementation 'androidx.activity:activity-compose:1.9.2' + implementation 'androidx.navigation:navigation-compose:2.8.0' + implementation 'io.coil-kt:coil-compose:2.2.1' + + testImplementation 'junit:junit:4.13.2' + androidTestImplementation 'androidx.test.ext:junit:1.2.1' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.6.1' + androidTestImplementation "androidx.compose.ui:ui-test-junit4" + debugImplementation "androidx.compose.ui:ui-tooling" + debugImplementation "androidx.compose.ui:ui-test-manifest" +} \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/feed-compose/app/proguard-rules.pro b/samples/user-interface/canonical-layouts/feed-compose/app/proguard-rules.pro new file mode 100644 index 00000000..481bb434 --- /dev/null +++ b/samples/user-interface/canonical-layouts/feed-compose/app/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/feed-compose/app/src/main/AndroidManifest.xml b/samples/user-interface/canonical-layouts/feed-compose/app/src/main/AndroidManifest.xml new file mode 100644 index 00000000..9f0ea582 --- /dev/null +++ b/samples/user-interface/canonical-layouts/feed-compose/app/src/main/AndroidManifest.xml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/feed-compose/app/src/main/java/com/example/feedcompose/MainActivity.kt b/samples/user-interface/canonical-layouts/feed-compose/app/src/main/java/com/example/feedcompose/MainActivity.kt new file mode 100644 index 00000000..dac7c4e8 --- /dev/null +++ b/samples/user-interface/canonical-layouts/feed-compose/app/src/main/java/com/example/feedcompose/MainActivity.kt @@ -0,0 +1,46 @@ +/* + * 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. + */ + +package com.example.feedcompose + +import android.os.Bundle +import androidx.activity.ComponentActivity +import androidx.activity.compose.setContent +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.material3.windowsizeclass.ExperimentalMaterial3WindowSizeClassApi +import androidx.compose.material3.windowsizeclass.calculateWindowSizeClass +import androidx.compose.ui.Modifier +import com.example.feedcompose.ui.FeedSampleApp +import com.example.feedcompose.ui.theme.FeedComposeTheme + +class MainActivity : ComponentActivity() { + @OptIn(ExperimentalMaterial3WindowSizeClassApi::class) + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContent { + FeedComposeTheme { + Surface( + modifier = Modifier.fillMaxSize(), + color = MaterialTheme.colorScheme.background + ) { + FeedSampleApp(windowSizeClass = calculateWindowSizeClass(activity = this)) + } + } + } + } +} diff --git a/samples/user-interface/canonical-layouts/feed-compose/app/src/main/java/com/example/feedcompose/data/DataProvider.kt b/samples/user-interface/canonical-layouts/feed-compose/app/src/main/java/com/example/feedcompose/data/DataProvider.kt new file mode 100644 index 00000000..c54d2539 --- /dev/null +++ b/samples/user-interface/canonical-layouts/feed-compose/app/src/main/java/com/example/feedcompose/data/DataProvider.kt @@ -0,0 +1,281 @@ +/* + * 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. + */ + +package com.example.feedcompose.data + +import com.example.feedcompose.R + +object DataProvider { + val sweets = listOf( + Sweets( + id = 0, + imageUrl = "https://source.unsplash.com/V4MBq8kue3U", + description = R.string.lorem_ipsum, + category = Category.Pastry + ), + Sweets( + id = 1, + imageUrl = "https://source.unsplash.com/cSzyY2UaFSI", + description = R.string.lorem_ipsum, + category = Category.Misc + ), + Sweets( + id = 2, + imageUrl = "https://source.unsplash.com/mGP8gyGb8zY", + description = R.string.lorem_ipsum, + category = Category.Candy + ), + Sweets( + id = 3, + imageUrl = "https://source.unsplash.com/PL5FZkW0Qkk", + description = R.string.lorem_ipsum, + category = Category.Candy + ), + Sweets( + id = 4, + imageUrl = "https://source.unsplash.com/xLvIcAYuuMQ", + description = R.string.lorem_ipsum, + category = Category.Candy + ), + Sweets( + id = 5, + imageUrl = "https://source.unsplash.com/PMOoaWCqX_Q", + description = R.string.lorem_ipsum, + category = Category.Chocolate + ), + Sweets( + id = 6, + imageUrl = "https://source.unsplash.com/yCOzRIbL08E", + description = R.string.lorem_ipsum, + category = Category.Misc + ), + Sweets( + id = 7, + imageUrl = "https://source.unsplash.com/ZYKCgsRz9Mg", + description = R.string.lorem_ipsum, + category = Category.Pastry + ), + Sweets( + id = 8, + imageUrl = "https://source.unsplash.com/Fq54FqucgCE", + description = R.string.lorem_ipsum, + category = Category.Pastry + ), + Sweets( + id = 9, + imageUrl = "https://source.unsplash.com/WqCRDVs7ZI8", + description = R.string.lorem_ipsum, + category = Category.Misc + ), + Sweets( + id = 10, + imageUrl = "https://source.unsplash.com/gP1YecpRyD8", + description = R.string.lorem_ipsum, + category = Category.Chocolate + ), + Sweets( + id = 11, + imageUrl = "https://source.unsplash.com/hLOLcUwR0Y4", + description = R.string.lorem_ipsum, + category = Category.Pastry + ), + Sweets( + id = 12, + imageUrl = "https://source.unsplash.com/mtut50xOeC4", + description = R.string.lorem_ipsum, + category = Category.Pastry + ), + Sweets( + id = 13, + imageUrl = "https://source.unsplash.com/qZ5lPCPvdXE", + description = R.string.lorem_ipsum, + category = Category.Pastry + ), + Sweets( + id = 14, + imageUrl = "https://source.unsplash.com/uG3Vu5TXKxE", + description = R.string.lorem_ipsum, + category = Category.Pastry + ), + Sweets( + id = 15, + imageUrl = "https://source.unsplash.com/90HdOlGbjck", + description = R.string.lorem_ipsum, + category = Category.Pastry + ), + Sweets( + id = 16, + imageUrl = "https://source.unsplash.com/BhK9JdaBTvk", + description = R.string.lorem_ipsum, + category = Category.Pastry + ), + Sweets( + id = 17, + imageUrl = "https://source.unsplash.com/w0_w3N_hG00", + description = R.string.lorem_ipsum, + category = Category.Pastry + ), + Sweets( + id = 18, + imageUrl = "https://source.unsplash.com/AguGBqWbmME", + description = R.string.lorem_ipsum, + category = Category.Pastry + ), + Sweets( + id = 19, + imageUrl = "https://source.unsplash.com/yE_jI4KApfc", + description = R.string.lorem_ipsum, + category = Category.Chocolate + ), + Sweets( + id = 20, + imageUrl = "https://source.unsplash.com/p6OLZPnq810", + description = R.string.lorem_ipsum, + category = Category.Pastry + ), + Sweets( + id = 21, + imageUrl = "https://source.unsplash.com/AHF_ZktTL6Q", + description = R.string.lorem_ipsum, + category = Category.Candy + ), + Sweets( + id = 22, + imageUrl = "https://source.unsplash.com//LU_fCezP9-o", + description = R.string.lorem_ipsum, + category = Category.Candy + ), + Sweets( + id = 23, + imageUrl = "https://source.unsplash.com/_C5zsV_p-YI", + description = R.string.lorem_ipsum, + category = Category.Candy + ), + Sweets( + id = 24, + imageUrl = "https://source.unsplash.com/aXq1oCCjlVM", + description = R.string.lorem_ipsum, + category = Category.Candy + ), + Sweets( + id = 25, + imageUrl = "https://source.unsplash.com/cRwZACu3kQI", + description = R.string.lorem_ipsum, + category = Category.Pastry + ), + Sweets( + id = 26, + imageUrl = "https://source.unsplash.com/8XkNFQG_cgk", + description = R.string.lorem_ipsum, + category = Category.Candy + ), + Sweets( + id = 27, + imageUrl = "https://source.unsplash.com/FDYbS43jUrU", + description = R.string.lorem_ipsum, + category = Category.Pastry + ), + Sweets( + id = 28, + imageUrl = "https://source.unsplash.com/-ayOfwsd9mY", + description = R.string.lorem_ipsum, + category = Category.Candy + ), + Sweets( + id = 29, + imageUrl = "https://source.unsplash.com/dcPNZeSY3yk", + description = R.string.lorem_ipsum, + category = Category.Chocolate + ), + Sweets( + id = 30, + imageUrl = "https://source.unsplash.com/tWe8ib-cnXY", + description = R.string.lorem_ipsum, + category = Category.Chocolate + ), + Sweets( + id = 31, + imageUrl = "https://source.unsplash.com/r-hQw_obFd0", + description = R.string.lorem_ipsum, + category = Category.Chocolate + ), + Sweets( + id = 32, + imageUrl = "https://source.unsplash.com/EwaJbJvS9io", + description = R.string.lorem_ipsum, + category = Category.Chocolate + ), + Sweets( + id = 33, + imageUrl = "https://source.unsplash.com/LjzAqkZnGFM", + description = R.string.lorem_ipsum, + category = Category.Chocolate + ), + Sweets( + id = 34, + imageUrl = "https://source.unsplash.com/PqYvDBwpXpU", + description = R.string.lorem_ipsum, + category = Category.Pastry + ), + Sweets( + id = 35, + imageUrl = "https://source.unsplash.com/89h9zKa0L0g", + description = R.string.lorem_ipsum, + category = Category.Pastry + ), + Sweets( + id = 36, + imageUrl = "https://source.unsplash.com/OAC2cpzNCxs", + description = R.string.lorem_ipsum, + category = Category.Pastry + ), + Sweets( + id = 37, + imageUrl = "https://source.unsplash.com/PGQxoFvt14o", + description = R.string.lorem_ipsum, + category = Category.Misc + ), + Sweets( + id = 38, + imageUrl = "https://source.unsplash.com/cLpdEA23Z44", + description = R.string.lorem_ipsum, + category = Category.Misc + ), + Sweets( + id = 39, + imageUrl = "https://source.unsplash.com/ewOrvEa87j4", + description = R.string.lorem_ipsum, + category = Category.Misc + ), + Sweets( + id = 40, + imageUrl = "https://source.unsplash.com/uy9DJw9e_vs", + description = R.string.lorem_ipsum, + category = Category.Misc + ), + Sweets( + id = 41, + imageUrl = "https://source.unsplash.com/dIs-MqalSSE", + description = R.string.lorem_ipsum, + category = Category.Misc + ) + ) + + val chocolates = sweets.filter { Category.Chocolate == it.category } + val misc = sweets.filter { Category.Misc == it.category } + + fun getSweetsById(id: Int): Sweets = sweets[id] +} diff --git a/samples/user-interface/canonical-layouts/feed-compose/app/src/main/java/com/example/feedcompose/data/Sweets.kt b/samples/user-interface/canonical-layouts/feed-compose/app/src/main/java/com/example/feedcompose/data/Sweets.kt new file mode 100644 index 00000000..9d1980b1 --- /dev/null +++ b/samples/user-interface/canonical-layouts/feed-compose/app/src/main/java/com/example/feedcompose/data/Sweets.kt @@ -0,0 +1,34 @@ +/* + * 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. + */ + +package com.example.feedcompose.data + +import androidx.annotation.StringRes +import com.example.feedcompose.R + +enum class Category(@StringRes val labelId: Int) { + Pastry(R.string.pastry), + Candy(R.string.candy), + Chocolate(R.string.chocolate), + Misc(R.string.misc), +} + +class Sweets( + val id: Int, + val imageUrl: String, + @StringRes val description: Int, + val category: Category = Category.Misc +) diff --git a/samples/user-interface/canonical-layouts/feed-compose/app/src/main/java/com/example/feedcompose/ui/FeedSampleApp.kt b/samples/user-interface/canonical-layouts/feed-compose/app/src/main/java/com/example/feedcompose/ui/FeedSampleApp.kt new file mode 100644 index 00000000..cc2d866f --- /dev/null +++ b/samples/user-interface/canonical-layouts/feed-compose/app/src/main/java/com/example/feedcompose/ui/FeedSampleApp.kt @@ -0,0 +1,76 @@ +/* + * 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. + */ + +package com.example.feedcompose.ui + +import androidx.compose.material3.windowsizeclass.WindowSizeClass +import androidx.compose.runtime.Composable +import androidx.navigation.NavController +import androidx.navigation.NavType +import androidx.navigation.compose.NavHost +import androidx.navigation.compose.composable +import androidx.navigation.compose.rememberNavController +import androidx.navigation.navArgument +import com.example.feedcompose.data.DataProvider +import com.example.feedcompose.data.Sweets +import com.example.feedcompose.ui.screen.SweetsDetails +import com.example.feedcompose.ui.screen.SweetsFeed + +@Composable +fun FeedSampleApp(windowSizeClass: WindowSizeClass) { + val navController = rememberNavController() + val router = Router(navController) + NavHost(navController = navController, startDestination = "/") { + composable(Destination.Feed.path) { + SweetsFeed(windowSizeClass = windowSizeClass) { + router.showSweets(it) + } + } + composable( + Destination.Details.path, + arguments = listOf(navArgument("sweetsId") { type = NavType.IntType }) + ) { + val selectedSweetsId = it.arguments?.getInt("sweetsId") ?: 0 + SweetsDetails( + sweets = DataProvider.getSweetsById(selectedSweetsId), + windowSizeClass = windowSizeClass + ) { + navController.popBackStack() + } + } + } +} + +private sealed interface Destination { + val base: String + val path: String + + object Feed : Destination { + override val base: String = "/" + override val path: String = base + } + + object Details : Destination { + override val base: String = "/show" + override val path: String = "$base/{sweetsId}" + } +} + +private class Router(val navController: NavController) { + fun showSweets(sweets: Sweets) { + navController.navigate("${Destination.Details.base}/${sweets.id}") + } +} diff --git a/samples/user-interface/canonical-layouts/feed-compose/app/src/main/java/com/example/feedcompose/ui/components/TopAppBar.kt b/samples/user-interface/canonical-layouts/feed-compose/app/src/main/java/com/example/feedcompose/ui/components/TopAppBar.kt new file mode 100644 index 00000000..b0bbfb0b --- /dev/null +++ b/samples/user-interface/canonical-layouts/feed-compose/app/src/main/java/com/example/feedcompose/ui/components/TopAppBar.kt @@ -0,0 +1,47 @@ +/* + * 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. + */ + +package com.example.feedcompose.ui.components + +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar +import androidx.compose.runtime.Composable +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import com.example.feedcompose.R + + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +internal fun SweetTopAppBar(onBackPressed: () -> Unit) { + TopAppBar(title = { Text(text = stringResource(id = R.string.app_name)) }, + navigationIcon = { + BackButton(onBackPressed = onBackPressed) + }) +} + +@Composable +private fun BackButton(onBackPressed: () -> Unit) { + IconButton(onClick = onBackPressed) { + Icon( + painter = painterResource(id = R.drawable.ic_baseline_arrow_back_24), + contentDescription = null + ) + } +} diff --git a/samples/user-interface/canonical-layouts/feed-compose/app/src/main/java/com/example/feedcompose/ui/components/feed/Feed.kt b/samples/user-interface/canonical-layouts/feed-compose/app/src/main/java/com/example/feedcompose/ui/components/feed/Feed.kt new file mode 100644 index 00000000..7e8d69ba --- /dev/null +++ b/samples/user-interface/canonical-layouts/feed-compose/app/src/main/java/com/example/feedcompose/ui/components/feed/Feed.kt @@ -0,0 +1,231 @@ +/* + * 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. + */ + +package com.example.feedcompose.ui.components.feed + +import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.focusGroup +import androidx.compose.foundation.gestures.FlingBehavior +import androidx.compose.foundation.gestures.ScrollableDefaults +import androidx.compose.foundation.horizontalScroll +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.RowScope +import androidx.compose.foundation.lazy.grid.GridCells +import androidx.compose.foundation.lazy.grid.GridItemSpan +import androidx.compose.foundation.lazy.grid.LazyGridItemScope +import androidx.compose.foundation.lazy.grid.LazyGridItemSpanScope +import androidx.compose.foundation.lazy.grid.LazyGridState +import androidx.compose.foundation.lazy.grid.LazyVerticalGrid +import androidx.compose.foundation.lazy.grid.rememberLazyGridState +import androidx.compose.foundation.rememberScrollState +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp + +@Composable +fun Feed( + modifier: Modifier = Modifier, + columns: GridCells = GridCells.Fixed(1), + state: LazyGridState = rememberLazyGridState(), + contentPadding: PaddingValues = PaddingValues(0.dp), + verticalArrangement: Arrangement.Vertical = Arrangement.Top, + horizontalArrangement: Arrangement.Horizontal = Arrangement.Start, + flingBehavior: FlingBehavior = ScrollableDefaults.flingBehavior(), + content: @ExtensionFunctionType FeedScope.() -> Unit +) { + val feedScope = FeedScopeImpl().apply(content) + LazyVerticalGrid( + columns = columns, + modifier = modifier, + state = state, + contentPadding = contentPadding, + verticalArrangement = verticalArrangement, + horizontalArrangement = horizontalArrangement, + flingBehavior = flingBehavior + ) { + feedScope.items.forEach { feedItem -> + items( + count = feedItem.count, + key = feedItem.key, + contentType = feedItem.contentType, + span = feedItem.span, + itemContent = feedItem.itemContent + ) + } + } +} + +interface FeedScope { + fun item( + key: Any? = null, + span: (@ExtensionFunctionType LazyGridItemSpanScope.() -> GridItemSpan)? = null, + contentType: Any? = null, + content: @Composable LazyGridItemScope.() -> Unit + ) + + fun items( + count: Int, + key: ((index: Int) -> Any)? = null, + span: (@ExtensionFunctionType LazyGridItemSpanScope.(index: Int) -> GridItemSpan)? = null, + contentType: (index: Int) -> Any? = { null }, + itemContent: @Composable LazyGridItemScope.(index: Int) -> Unit + ) +} + +inline fun FeedScope.items( + items: List, + noinline key: ((item: T) -> Any)? = null, + noinline span: (@ExtensionFunctionType LazyGridItemSpanScope.(item: T) -> GridItemSpan)? = null, + noinline contentType: (item: T) -> Any? = { null }, + crossinline itemContent: @Composable LazyGridItemScope.(item: T) -> Unit +) = items( + count = items.size, + key = if (key != null) { + { index: Int -> key(items[index]) } + } else { + null + }, + span = if (span != null) { + { index: Int -> span(items[index]) } + } else { + null + }, + contentType = { index: Int -> contentType(items[index]) } +) { index -> + itemContent(items[index]) +} + +inline fun FeedScope.itemsIndexed( + items: List, + noinline key: ((index: Int, item: T) -> Any)? = null, + noinline span: (@ExtensionFunctionType LazyGridItemSpanScope.(index: Int, item: T) -> GridItemSpan)? = null, + noinline contentType: (index: Int, item: T) -> Any? = { _, _ -> null }, + crossinline itemContent: @Composable LazyGridItemScope.(index: Int, item: T) -> Unit +) = items( + count = items.size, + key = if (key != null) { + { index: Int -> key(index, items[index]) } + } else { + null + }, + span = if (span != null) { + { index: Int -> span(index, items[index]) } + } else { + null + }, + contentType = { index: Int -> contentType(index, items[index]) } +) { index -> + itemContent(index, items[index]) +} + +inline fun FeedScope.items( + items: Array, + noinline key: ((item: T) -> Any)? = null, + noinline span: (@ExtensionFunctionType LazyGridItemSpanScope.(item: T) -> GridItemSpan)? = null, + noinline contentType: (item: T) -> Any? = { null }, + crossinline itemContent: @Composable LazyGridItemScope.(item: T) -> Unit +) = items( + count = items.size, + key = if (key != null) { + { index: Int -> key(items[index]) } + } else { + null + }, + span = if (span != null) { + { index: Int -> span(items[index]) } + } else { + null + }, + contentType = { index: Int -> contentType(items[index]) } +) { index -> + itemContent(items[index]) +} + +inline fun FeedScope.itemsIndexed( + items: Array, + noinline key: ((index: Int, item: T) -> Any)? = null, + noinline span: (@ExtensionFunctionType LazyGridItemSpanScope.(index: Int, item: T) -> GridItemSpan)? = null, + noinline contentType: (index: Int, item: T) -> Any? = { _, _ -> null }, + crossinline itemContent: @Composable LazyGridItemScope.(index: Int, item: T) -> Unit +) = items( + count = items.size, + key = if (key != null) { + { index: Int -> key(index, items[index]) } + } else { + null + }, + span = if (span != null) { + { index: Int -> span(index, items[index]) } + } else { + null + }, + contentType = { index: Int -> contentType(index, items[index]) } +) { index -> + itemContent(index, items[index]) +} + +inline fun FeedScope.row( + key: Any? = null, + contentType: Any? = null, + crossinline content: @Composable LazyGridItemScope.() -> Unit +) = item( + key = key, + span = { GridItemSpan(maxLineSpan) }, + contentType = contentType +) { + content() +} + +@OptIn(ExperimentalFoundationApi::class) +inline fun FeedScope.title( + key: Any? = null, + contentType: Any? = null, + crossinline content: @Composable LazyGridItemScope.() -> Unit +) = row(key = key, contentType = contentType) { content() } + +@OptIn(ExperimentalFoundationApi::class) +inline fun FeedScope.action( + key: Any? = null, + contentType: Any? = null, + horizontalArrangement: Arrangement.Horizontal = Arrangement.Start, + crossinline content: @Composable RowScope.() -> Unit +) = row( + key = key, + contentType = contentType +) { + Row( + modifier = Modifier + .focusGroup() + .horizontalScroll(rememberScrollState()), + horizontalArrangement = horizontalArrangement + ) { + content() + } +} + +inline fun FeedScope.footer( + key: Any? = null, + contentType: Any? = null, + crossinline content: @Composable LazyGridItemScope.() -> Unit +) = item( + key = key, + span = { GridItemSpan(maxLineSpan) }, + contentType = contentType +) { + content() +} diff --git a/samples/user-interface/canonical-layouts/feed-compose/app/src/main/java/com/example/feedcompose/ui/components/feed/FeedScopeImpl.kt b/samples/user-interface/canonical-layouts/feed-compose/app/src/main/java/com/example/feedcompose/ui/components/feed/FeedScopeImpl.kt new file mode 100644 index 00000000..16ab1e32 --- /dev/null +++ b/samples/user-interface/canonical-layouts/feed-compose/app/src/main/java/com/example/feedcompose/ui/components/feed/FeedScopeImpl.kt @@ -0,0 +1,79 @@ +/* + * 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. + */ + +package com.example.feedcompose.ui.components.feed + +import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.lazy.grid.GridItemSpan +import androidx.compose.foundation.lazy.grid.LazyGridItemScope +import androidx.compose.foundation.lazy.grid.LazyGridItemSpanScope +import androidx.compose.runtime.Composable + +@OptIn(ExperimentalFoundationApi::class) +internal class FeedScopeImpl : FeedScope { + val items = mutableListOf() + + override fun item( + key: Any?, + span: (LazyGridItemSpanScope.() -> GridItemSpan)?, + contentType: Any?, + content: @Composable LazyGridItemScope.() -> Unit + ) { + items.add( + FeedItem( + count = 1, + key = if (key != null) { + { key } + } else { + null + }, + span = if (span != null) { + { span() } + } else { + null + }, + contentType = { contentType }, + itemContent = { content() } + ) + ) + } + + override fun items( + count: Int, + key: ((index: Int) -> Any)?, + span: (LazyGridItemSpanScope.(index: Int) -> GridItemSpan)?, + contentType: (index: Int) -> Any?, + itemContent: @Composable LazyGridItemScope.(index: Int) -> Unit + ) { + items.add( + FeedItem( + count = count, + key = key, + span = span, + contentType = contentType, + itemContent = itemContent + ) + ) + } +} + +internal data class FeedItem( + val count: Int, + val key: ((index: Int) -> Any)?, + val span: (LazyGridItemSpanScope.(index: Int) -> GridItemSpan)?, + val contentType: (index: Int) -> Any?, + val itemContent: @Composable LazyGridItemScope.(index: Int) -> Unit +) diff --git a/samples/user-interface/canonical-layouts/feed-compose/app/src/main/java/com/example/feedcompose/ui/screen/SweetsDetails.kt b/samples/user-interface/canonical-layouts/feed-compose/app/src/main/java/com/example/feedcompose/ui/screen/SweetsDetails.kt new file mode 100644 index 00000000..67488a44 --- /dev/null +++ b/samples/user-interface/canonical-layouts/feed-compose/app/src/main/java/com/example/feedcompose/ui/screen/SweetsDetails.kt @@ -0,0 +1,131 @@ +/* + * 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. + */ + +package com.example.feedcompose.ui.screen + +import androidx.compose.foundation.ScrollState +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.aspectRatio +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.Text +import androidx.compose.material3.windowsizeclass.WindowHeightSizeClass +import androidx.compose.material3.windowsizeclass.WindowSizeClass +import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import coil.compose.AsyncImage +import com.example.feedcompose.R +import com.example.feedcompose.data.Sweets +import com.example.feedcompose.ui.components.SweetTopAppBar + +@Composable +fun SweetsDetails( + sweets: Sweets, + windowSizeClass: WindowSizeClass, + onBackPressed: () -> Unit = {} +) { + val scrollState = rememberScrollState() + when (windowSizeClass.widthSizeClass) { + WindowWidthSizeClass.Expanded -> SweetsDetailsHorizontal( + sweets = sweets, + scrollState = scrollState, + onBackPressed = onBackPressed + ) + + WindowWidthSizeClass.Compact -> SweetsDetailsVertical( + sweets = sweets, + scrollState = scrollState, + onBackPressed = onBackPressed + ) + + else -> { + when (windowSizeClass.heightSizeClass) { + WindowHeightSizeClass.Expanded -> SweetsDetailsVertical( + sweets = sweets, + scrollState = scrollState, + onBackPressed = onBackPressed + ) + + else -> SweetsDetailsHorizontal( + sweets = sweets, + scrollState = scrollState, + onBackPressed = onBackPressed + ) + } + } + } +} + +@Composable +private fun SweetsDetailsVertical( + sweets: Sweets, + scrollState: ScrollState, + onBackPressed: () -> Unit +) { + Column(modifier = Modifier.verticalScroll(scrollState)) { + SweetTopAppBar(onBackPressed = onBackPressed) + AsyncImage( + model = sweets.imageUrl, + contentDescription = stringResource(id = R.string.thumbnail_content_description), + modifier = Modifier + .aspectRatio(1.414f) + .fillMaxWidth(), + contentScale = ContentScale.Crop + ) + Text( + text = stringResource(id = sweets.description), + modifier = Modifier.padding(32.dp) + ) + } +} + +@Composable +private fun SweetsDetailsHorizontal( + sweets: Sweets, + scrollState: ScrollState, + onBackPressed: () -> Unit +) { + Column { + SweetTopAppBar(onBackPressed = onBackPressed) + Row { + AsyncImage( + model = sweets.imageUrl, + contentDescription = stringResource(id = R.string.thumbnail_content_description), + contentScale = ContentScale.Crop, + placeholder = painterResource(id = R.drawable.placeholder_sweets), + modifier = Modifier + .fillMaxSize() + .weight(1.0f) + ) + Text( + text = stringResource(id = sweets.description), + modifier = Modifier + .padding(start = 32.dp, end = 32.dp, bottom = 32.dp) + .weight(1.0f) + .verticalScroll(scrollState) + ) + } + } +} diff --git a/samples/user-interface/canonical-layouts/feed-compose/app/src/main/java/com/example/feedcompose/ui/screen/SweetsFeed.kt b/samples/user-interface/canonical-layouts/feed-compose/app/src/main/java/com/example/feedcompose/ui/screen/SweetsFeed.kt new file mode 100644 index 00000000..ca22f530 --- /dev/null +++ b/samples/user-interface/canonical-layouts/feed-compose/app/src/main/java/com/example/feedcompose/ui/screen/SweetsFeed.kt @@ -0,0 +1,279 @@ +/* + * 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. + */ + +package com.example.feedcompose.ui.screen + +import androidx.compose.foundation.border +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.aspectRatio +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.lazy.LazyRow +import androidx.compose.foundation.lazy.grid.GridCells +import androidx.compose.foundation.lazy.grid.rememberLazyGridState +import androidx.compose.material3.Button +import androidx.compose.material3.Card +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.FilterChip +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.material3.windowsizeclass.WindowSizeClass +import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass +import androidx.compose.runtime.Composable +import androidx.compose.runtime.MutableState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.focus.onFocusChanged +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp +import coil.compose.AsyncImage +import com.example.feedcompose.R +import com.example.feedcompose.data.Category +import com.example.feedcompose.data.DataProvider +import com.example.feedcompose.data.DataProvider.chocolates +import com.example.feedcompose.data.Sweets +import com.example.feedcompose.ui.components.feed.Feed +import com.example.feedcompose.ui.components.feed.action +import com.example.feedcompose.ui.components.feed.footer +import com.example.feedcompose.ui.components.feed.items +import com.example.feedcompose.ui.components.feed.row +import com.example.feedcompose.ui.components.feed.title +import kotlinx.coroutines.launch + +@Composable +internal fun SweetsFeed(windowSizeClass: WindowSizeClass, onSweetsSelected: (Sweets) -> Unit = {}) { + val selectedFilter: MutableState = remember { + mutableStateOf(Filter.All) + } + val sweets = DataProvider.sweets.filter { selectedFilter.value.apply(it) } + val chocolates = DataProvider.chocolates + + val state = rememberLazyGridState() + val coroutineScope = rememberCoroutineScope() + + Feed( + columns = rememberColumns(windowSizeClass = windowSizeClass), + state = state, + contentPadding = PaddingValues(horizontal = 32.dp, vertical = 48.dp), + horizontalArrangement = Arrangement.spacedBy(8.dp), + verticalArrangement = Arrangement.spacedBy(8.dp) + ) { + title(contentType = "feed-title") { + FeedTitle(text = stringResource(id = R.string.app_name)) + } + items(DataProvider.misc, contentType = { "sweets" }, key = { it.id }) { + SquareSweetsCard(sweets = it, onClick = onSweetsSelected) + } + title(contentType = "section-title") { + SectionTitle(text = stringResource(id = R.string.chocolate)) + } + row(contentType = "chocolate-list") { + HorizontalSweetsList( + sweets = chocolates, + cardWidth = if (windowSizeClass.widthSizeClass == WindowWidthSizeClass.Expanded) { + 240.dp + } else { + 128.dp + }, + onSweetsSelected = onSweetsSelected + ) + } + title(contentType = "section-title") { + SectionTitle(text = stringResource(id = R.string.candy_or_pastry)) + } + action( + contentType = "filter-selector", + horizontalArrangement = Arrangement.spacedBy(8.dp) + ) { + FilterSelector(selectedFilter = selectedFilter.value) { selectedFilter.value = it } + } + items(sweets, contentType = { "sweets" }, key = { it.id }) { + SquareSweetsCard(sweets = it, onClick = onSweetsSelected) + } + footer { + BackToTopButton(modifier = Modifier.padding(PaddingValues(top = 32.dp))) { + coroutineScope.launch { + state.animateScrollToItem(0) + } + } + } + } +} + +@Composable +private fun rememberColumns(windowSizeClass: WindowSizeClass) = remember(windowSizeClass) { + when (windowSizeClass.widthSizeClass) { + WindowWidthSizeClass.Compact -> GridCells.Fixed(1) + WindowWidthSizeClass.Medium -> GridCells.Fixed(2) + else -> GridCells.Adaptive(240.dp) + } +} + +@Composable +private fun FeedTitle(text: String) { + Text( + text = text, + style = MaterialTheme.typography.headlineLarge, + modifier = Modifier.padding(PaddingValues(vertical = 24.dp)) + ) +} + +@Composable +private fun SectionTitle(text: String) { + Text( + text = text, + style = MaterialTheme.typography.headlineSmall, + modifier = Modifier.padding(PaddingValues(top = 32.dp, bottom = 8.dp)) + ) +} + +@Composable +private fun HorizontalSweetsList( + sweets: List, + cardWidth: Dp, + onSweetsSelected: (Sweets) -> Unit = {} +) { + LazyRow( + modifier = Modifier.padding(PaddingValues(bottom = 16.dp)), + horizontalArrangement = Arrangement.spacedBy(8.dp) + ) { + items(sweets.size, key = { sweets[it].id }, contentType = { "sweets" }) { + PortraitSweetsCard( + sweets = chocolates[it], + onClick = onSweetsSelected, + modifier = Modifier.width(cardWidth) + ) + } + } +} + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +private fun FilterSelector(selectedFilter: Filter, onFilterSelected: (Filter) -> Unit) { + val filters = listOf( + Filter.All to R.string.all, + Filter.Candy to R.string.candy, + Filter.Pastry to R.string.pastry + ) + filters.forEach { (filter, labelId) -> + val selected = selectedFilter == filter + FilterChip( + selected = selected, + onClick = { onFilterSelected(filter) }, + label = { Text(text = stringResource(id = labelId)) }, + leadingIcon = { + if (selected) { + Icon( + painter = painterResource(id = R.drawable.ic_baseline_check_24), + contentDescription = null + ) + } + } + ) + } +} + +@Composable +private fun BackToTopButton(modifier: Modifier = Modifier, onClick: () -> Unit = {}) { + Box(contentAlignment = Alignment.Center) { + Button(onClick = onClick, modifier = modifier) { + Icon(painter = painterResource(id = R.drawable.ic_baseline_arrow_upward_24), null) + Text(text = "Back to top") + } + } +} + +@Composable +private fun SquareSweetsCard( + sweets: Sweets, + modifier: Modifier = Modifier, + onClick: (Sweets) -> Unit = {} +) { + SweetsCard( + sweets = sweets, + modifier = modifier.aspectRatio(1.0f), + onClick = onClick + ) +} + +@Composable +private fun PortraitSweetsCard( + sweets: Sweets, + modifier: Modifier = Modifier, + onClick: (Sweets) -> Unit = {} +) { + SweetsCard( + sweets = sweets, + modifier = modifier.aspectRatio(0.707f), + onClick = onClick + ) +} + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun SweetsCard( + sweets: Sweets, + modifier: Modifier = Modifier, + onClick: (Sweets) -> Unit = {} +) { + var isFocused by remember { + mutableStateOf(false) + } + val outlineColor = if (isFocused) { + MaterialTheme.colorScheme.outline + } else { + MaterialTheme.colorScheme.background + } + + Card( + modifier = modifier + .onFocusChanged { + isFocused = it.isFocused + } + .border(width = 2.dp, color = outlineColor), + onClick = { onClick(sweets) } + ) { + AsyncImage( + modifier = Modifier.fillMaxSize(), + model = sweets.imageUrl, + contentDescription = stringResource(id = R.string.thumbnail_content_description), + placeholder = painterResource(id = R.drawable.placeholder_sweets), + contentScale = ContentScale.Crop + ) + } +} + +sealed class Filter(private val categories: List) { + fun apply(sweets: Sweets): Boolean = categories.indexOf(sweets.category) != -1 + + object All : Filter(listOf(Category.Candy, Category.Pastry)) + + object Candy : Filter(listOf(Category.Candy)) + + object Pastry : Filter(listOf(Category.Pastry)) +} diff --git a/samples/user-interface/canonical-layouts/feed-compose/app/src/main/java/com/example/feedcompose/ui/theme/Color.kt b/samples/user-interface/canonical-layouts/feed-compose/app/src/main/java/com/example/feedcompose/ui/theme/Color.kt new file mode 100644 index 00000000..e4caa4e9 --- /dev/null +++ b/samples/user-interface/canonical-layouts/feed-compose/app/src/main/java/com/example/feedcompose/ui/theme/Color.kt @@ -0,0 +1,27 @@ +/* + * 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. + */ + +package com.example.feedcompose.ui.theme + +import androidx.compose.ui.graphics.Color + +val Purple80 = Color(0xFFD0BCFF) +val PurpleGrey80 = Color(0xFFCCC2DC) +val Pink80 = Color(0xFFEFB8C8) + +val Purple40 = Color(0xFF6650a4) +val PurpleGrey40 = Color(0xFF625b71) +val Pink40 = Color(0xFF7D5260) diff --git a/samples/user-interface/canonical-layouts/feed-compose/app/src/main/java/com/example/feedcompose/ui/theme/FeedComposeTheme.kt b/samples/user-interface/canonical-layouts/feed-compose/app/src/main/java/com/example/feedcompose/ui/theme/FeedComposeTheme.kt new file mode 100644 index 00000000..4b129ee4 --- /dev/null +++ b/samples/user-interface/canonical-layouts/feed-compose/app/src/main/java/com/example/feedcompose/ui/theme/FeedComposeTheme.kt @@ -0,0 +1,84 @@ +/* + * 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. + */ + +package com.example.feedcompose.ui.theme + +import android.app.Activity +import android.os.Build +import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.darkColorScheme +import androidx.compose.material3.dynamicDarkColorScheme +import androidx.compose.material3.dynamicLightColorScheme +import androidx.compose.material3.lightColorScheme +import androidx.compose.runtime.Composable +import androidx.compose.runtime.SideEffect +import androidx.compose.ui.graphics.toArgb +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalView +import androidx.core.view.ViewCompat + +private val DarkColorScheme = darkColorScheme( + primary = Purple80, + secondary = PurpleGrey80, + tertiary = Pink80 +) + +private val LightColorScheme = lightColorScheme( + primary = Purple40, + secondary = PurpleGrey40, + tertiary = Pink40 + + /* Other default colors to override + background = Color(0xFFFFFBFE), + surface = Color(0xFFFFFBFE), + onPrimary = Color.White, + onSecondary = Color.White, + onTertiary = Color.White, + onBackground = Color(0xFF1C1B1F), + onSurface = Color(0xFF1C1B1F), + */ +) + +@Composable +fun FeedComposeTheme( + darkTheme: Boolean = isSystemInDarkTheme(), + // Dynamic color is available on Android 12+ + dynamicColor: Boolean = true, + content: @Composable () -> Unit +) { + val colorScheme = when { + dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> { + val context = LocalContext.current + if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) + } + darkTheme -> DarkColorScheme + else -> LightColorScheme + } + val view = LocalView.current + if (!view.isInEditMode) { + SideEffect { + (view.context as Activity).window.statusBarColor = colorScheme.primary.toArgb() + ViewCompat.getWindowInsetsController(view)?.isAppearanceLightStatusBars = darkTheme + } + } + + MaterialTheme( + colorScheme = colorScheme, + typography = Typography, + content = content + ) +} diff --git a/samples/user-interface/canonical-layouts/feed-compose/app/src/main/java/com/example/feedcompose/ui/theme/Type.kt b/samples/user-interface/canonical-layouts/feed-compose/app/src/main/java/com/example/feedcompose/ui/theme/Type.kt new file mode 100644 index 00000000..6ade32d7 --- /dev/null +++ b/samples/user-interface/canonical-layouts/feed-compose/app/src/main/java/com/example/feedcompose/ui/theme/Type.kt @@ -0,0 +1,34 @@ +/* + * 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. + */ + +package com.example.feedcompose.ui.theme + +import androidx.compose.material3.Typography +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.sp + +// Set of Material typography styles to start with +val Typography = Typography( + bodyLarge = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Normal, + fontSize = 16.sp, + lineHeight = 24.sp, + letterSpacing = 0.5.sp + ) +) diff --git a/samples/user-interface/canonical-layouts/feed-compose/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/samples/user-interface/canonical-layouts/feed-compose/app/src/main/res/drawable-v24/ic_launcher_foreground.xml new file mode 100644 index 00000000..e56ae0ab --- /dev/null +++ b/samples/user-interface/canonical-layouts/feed-compose/app/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/feed-compose/app/src/main/res/drawable/ic_baseline_arrow_back_24.xml b/samples/user-interface/canonical-layouts/feed-compose/app/src/main/res/drawable/ic_baseline_arrow_back_24.xml new file mode 100644 index 00000000..a91434a2 --- /dev/null +++ b/samples/user-interface/canonical-layouts/feed-compose/app/src/main/res/drawable/ic_baseline_arrow_back_24.xml @@ -0,0 +1,21 @@ + + + + + diff --git a/samples/user-interface/canonical-layouts/feed-compose/app/src/main/res/drawable/ic_baseline_arrow_upward_24.xml b/samples/user-interface/canonical-layouts/feed-compose/app/src/main/res/drawable/ic_baseline_arrow_upward_24.xml new file mode 100644 index 00000000..2b753e52 --- /dev/null +++ b/samples/user-interface/canonical-layouts/feed-compose/app/src/main/res/drawable/ic_baseline_arrow_upward_24.xml @@ -0,0 +1,21 @@ + + + + + diff --git a/samples/user-interface/canonical-layouts/feed-compose/app/src/main/res/drawable/ic_baseline_check_24.xml b/samples/user-interface/canonical-layouts/feed-compose/app/src/main/res/drawable/ic_baseline_check_24.xml new file mode 100644 index 00000000..705da854 --- /dev/null +++ b/samples/user-interface/canonical-layouts/feed-compose/app/src/main/res/drawable/ic_baseline_check_24.xml @@ -0,0 +1,21 @@ + + + + + diff --git a/samples/user-interface/canonical-layouts/feed-compose/app/src/main/res/drawable/ic_launcher_background.xml b/samples/user-interface/canonical-layouts/feed-compose/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 00000000..7fd402b8 --- /dev/null +++ b/samples/user-interface/canonical-layouts/feed-compose/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,186 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/user-interface/canonical-layouts/feed-compose/app/src/main/res/drawable/placeholder_sweets.xml b/samples/user-interface/canonical-layouts/feed-compose/app/src/main/res/drawable/placeholder_sweets.xml new file mode 100644 index 00000000..db561abb --- /dev/null +++ b/samples/user-interface/canonical-layouts/feed-compose/app/src/main/res/drawable/placeholder_sweets.xml @@ -0,0 +1,28 @@ + + + + + + diff --git a/samples/user-interface/canonical-layouts/feed-compose/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/samples/user-interface/canonical-layouts/feed-compose/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 00000000..3fb19ddc --- /dev/null +++ b/samples/user-interface/canonical-layouts/feed-compose/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,22 @@ + + + + + + + + \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/feed-compose/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/samples/user-interface/canonical-layouts/feed-compose/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 00000000..eb800597 --- /dev/null +++ b/samples/user-interface/canonical-layouts/feed-compose/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,20 @@ + + + + + + \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/feed-compose/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/samples/user-interface/canonical-layouts/feed-compose/app/src/main/res/mipmap-hdpi/ic_launcher.webp new file mode 100644 index 00000000..c209e78e Binary files /dev/null and b/samples/user-interface/canonical-layouts/feed-compose/app/src/main/res/mipmap-hdpi/ic_launcher.webp differ diff --git a/samples/user-interface/canonical-layouts/feed-compose/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/samples/user-interface/canonical-layouts/feed-compose/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp new file mode 100644 index 00000000..b2dfe3d1 Binary files /dev/null and b/samples/user-interface/canonical-layouts/feed-compose/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp differ diff --git a/samples/user-interface/canonical-layouts/feed-compose/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/samples/user-interface/canonical-layouts/feed-compose/app/src/main/res/mipmap-mdpi/ic_launcher.webp new file mode 100644 index 00000000..4f0f1d64 Binary files /dev/null and b/samples/user-interface/canonical-layouts/feed-compose/app/src/main/res/mipmap-mdpi/ic_launcher.webp differ diff --git a/samples/user-interface/canonical-layouts/feed-compose/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/samples/user-interface/canonical-layouts/feed-compose/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp new file mode 100644 index 00000000..62b611da Binary files /dev/null and b/samples/user-interface/canonical-layouts/feed-compose/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp differ diff --git a/samples/user-interface/canonical-layouts/feed-compose/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/samples/user-interface/canonical-layouts/feed-compose/app/src/main/res/mipmap-xhdpi/ic_launcher.webp new file mode 100644 index 00000000..948a3070 Binary files /dev/null and b/samples/user-interface/canonical-layouts/feed-compose/app/src/main/res/mipmap-xhdpi/ic_launcher.webp differ diff --git a/samples/user-interface/canonical-layouts/feed-compose/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/samples/user-interface/canonical-layouts/feed-compose/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp new file mode 100644 index 00000000..1b9a6956 Binary files /dev/null and b/samples/user-interface/canonical-layouts/feed-compose/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp differ diff --git a/samples/user-interface/canonical-layouts/feed-compose/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/samples/user-interface/canonical-layouts/feed-compose/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp new file mode 100644 index 00000000..28d4b77f Binary files /dev/null and b/samples/user-interface/canonical-layouts/feed-compose/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ diff --git a/samples/user-interface/canonical-layouts/feed-compose/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/samples/user-interface/canonical-layouts/feed-compose/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp new file mode 100644 index 00000000..9287f508 Binary files /dev/null and b/samples/user-interface/canonical-layouts/feed-compose/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp differ diff --git a/samples/user-interface/canonical-layouts/feed-compose/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/samples/user-interface/canonical-layouts/feed-compose/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp new file mode 100644 index 00000000..aa7d6427 Binary files /dev/null and b/samples/user-interface/canonical-layouts/feed-compose/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ diff --git a/samples/user-interface/canonical-layouts/feed-compose/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/samples/user-interface/canonical-layouts/feed-compose/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp new file mode 100644 index 00000000..9126ae37 Binary files /dev/null and b/samples/user-interface/canonical-layouts/feed-compose/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp differ diff --git a/samples/user-interface/canonical-layouts/feed-compose/app/src/main/res/values/strings.xml b/samples/user-interface/canonical-layouts/feed-compose/app/src/main/res/values/strings.xml new file mode 100644 index 00000000..3bc52945 --- /dev/null +++ b/samples/user-interface/canonical-layouts/feed-compose/app/src/main/res/values/strings.xml @@ -0,0 +1,67 @@ + + + + Compose-based Feed Sample + + Sweets + + Candy + Chocolate + Pastry + Misc + Both + Candy or Pastry + + A sweets + + + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam in scelerisque sem. Mauris + volutpat, dolor id interdum ullamcorper, risus dolor egestas lectus, sit amet mattis purus + dui nec risus. Maecenas non sodales nisi, vel dictum dolor. Class aptent taciti sociosqu ad + litora torquent per conubia nostra, per inceptos himenaeos. Suspendisse blandit eleifend + diam, vel rutrum tellus vulputate quis. Aliquam eget libero aliquet, imperdiet nisl a, + ornare ex. Sed rhoncus est ut libero porta lobortis. Fusce in dictum tellus.\n\n + Suspendisse interdum ornare ante. Aliquam nec cursus lorem. Morbi id magna felis. Vivamus + egestas, est a condimentum egestas, turpis nisl iaculis ipsum, in dictum tellus dolor sed + neque. Morbi tellus erat, dapibus ut sem a, iaculis tincidunt dui. Interdum et malesuada + fames ac ante ipsum primis in faucibus. Curabitur et eros porttitor, ultricies urna vitae, + molestie nibh. Phasellus at commodo eros, non aliquet metus. Sed maximus nisl nec dolor + bibendum, vel congue leo egestas.\n\n + Sed interdum tortor nibh, in sagittis risus mollis quis. Curabitur mi odio, condimentum sit + amet auctor at, mollis non turpis. Nullam pretium libero vestibulum, finibus orci vel, + molestie quam. Fusce blandit tincidunt nulla, quis sollicitudin libero facilisis et. Integer + interdum nunc ligula, et fermentum metus hendrerit id. Vestibulum lectus felis, dictum at + lacinia sit amet, tristique id quam. Cras eu consequat dui. Suspendisse sodales nunc ligula, + in lobortis sem porta sed. Integer id ultrices magna, in luctus elit. Sed a pellentesque + est.\n\n + Aenean nunc velit, lacinia sed dolor sed, ultrices viverra nulla. Etiam a venenatis nibh. + Morbi laoreet, tortor sed facilisis varius, nibh orci rhoncus nulla, id elementum leo dui + non lorem. Nam mollis ipsum quis auctor varius. Quisque elementum eu libero sed commodo. In + eros nisl, imperdiet vel imperdiet et, scelerisque a mauris. Pellentesque varius ex nunc, + quis imperdiet eros placerat ac. Duis finibus orci et est auctor tincidunt. Sed non viverra + ipsum. Nunc quis augue egestas, cursus lorem at, molestie sem. Morbi a consectetur ipsum, a + placerat diam. Etiam vulputate dignissim convallis. Integer faucibus mauris sit amet finibus + convallis.\n\n + Phasellus in aliquet mi. Pellentesque habitant morbi tristique senectus et netus et + malesuada fames ac turpis egestas. In volutpat arcu ut felis sagittis, in finibus massa + gravida. Pellentesque id tellus orci. Integer dictum, lorem sed efficitur ullamcorper, + libero justo consectetur ipsum, in mollis nisl ex sed nisl. Donec maximus ullamcorper + sodales. Praesent bibendum rhoncus tellus nec feugiat. In a ornare nulla. Donec rhoncus + libero vel nunc consequat, quis tincidunt nisl eleifend. Cras bibendum enim a justo luctus + vestibulum. Fusce dictum libero quis erat maximus, vitae volutpat diam dignissim. + + \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/feed-compose/app/src/main/res/values/themes.xml b/samples/user-interface/canonical-layouts/feed-compose/app/src/main/res/values/themes.xml new file mode 100644 index 00000000..88f1649c --- /dev/null +++ b/samples/user-interface/canonical-layouts/feed-compose/app/src/main/res/values/themes.xml @@ -0,0 +1,20 @@ + + + + + + \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/feed-view/app/src/main/res/values-v29/themes.xml b/samples/user-interface/canonical-layouts/feed-view/app/src/main/res/values-v29/themes.xml new file mode 100644 index 00000000..fc6f761a --- /dev/null +++ b/samples/user-interface/canonical-layouts/feed-view/app/src/main/res/values-v29/themes.xml @@ -0,0 +1,26 @@ + + + + + + + \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/feed-view/app/src/main/res/values-w600dp/dimens.xml b/samples/user-interface/canonical-layouts/feed-view/app/src/main/res/values-w600dp/dimens.xml new file mode 100644 index 00000000..60a3bb22 --- /dev/null +++ b/samples/user-interface/canonical-layouts/feed-view/app/src/main/res/values-w600dp/dimens.xml @@ -0,0 +1,21 @@ + + + + + 8dp + 8dp + \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/feed-view/app/src/main/res/values-w600dp/integers.xml b/samples/user-interface/canonical-layouts/feed-view/app/src/main/res/values-w600dp/integers.xml new file mode 100644 index 00000000..a9fb0172 --- /dev/null +++ b/samples/user-interface/canonical-layouts/feed-view/app/src/main/res/values-w600dp/integers.xml @@ -0,0 +1,20 @@ + + + + + 2 + \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/feed-view/app/src/main/res/values-w840dp/integers.xml b/samples/user-interface/canonical-layouts/feed-view/app/src/main/res/values-w840dp/integers.xml new file mode 100644 index 00000000..21642149 --- /dev/null +++ b/samples/user-interface/canonical-layouts/feed-view/app/src/main/res/values-w840dp/integers.xml @@ -0,0 +1,20 @@ + + + + + 5 + \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/feed-view/app/src/main/res/values/colors.xml b/samples/user-interface/canonical-layouts/feed-view/app/src/main/res/values/colors.xml new file mode 100644 index 00000000..1c1bf75e --- /dev/null +++ b/samples/user-interface/canonical-layouts/feed-view/app/src/main/res/values/colors.xml @@ -0,0 +1,21 @@ + + + + + #FF000000 + #FFFFFFFF + \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/feed-view/app/src/main/res/values/dimens.xml b/samples/user-interface/canonical-layouts/feed-view/app/src/main/res/values/dimens.xml new file mode 100644 index 00000000..a369fe12 --- /dev/null +++ b/samples/user-interface/canonical-layouts/feed-view/app/src/main/res/values/dimens.xml @@ -0,0 +1,28 @@ + + + + + 16dp + 0dp + 4dp + 16dp + + 128dp + 128dp + + 64dp + \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/feed-view/app/src/main/res/values/integers.xml b/samples/user-interface/canonical-layouts/feed-view/app/src/main/res/values/integers.xml new file mode 100644 index 00000000..57ba9d96 --- /dev/null +++ b/samples/user-interface/canonical-layouts/feed-view/app/src/main/res/values/integers.xml @@ -0,0 +1,20 @@ + + + + + 1 + \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/feed-view/app/src/main/res/values/strings.xml b/samples/user-interface/canonical-layouts/feed-view/app/src/main/res/values/strings.xml new file mode 100644 index 00000000..2a88a139 --- /dev/null +++ b/samples/user-interface/canonical-layouts/feed-view/app/src/main/res/values/strings.xml @@ -0,0 +1,66 @@ + + + + + ViewBasedFeedLayoutSample + + Sweets + + Candy + Chocolate + Pastry + Misc + + A sweets + + + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam in scelerisque sem. Mauris + volutpat, dolor id interdum ullamcorper, risus dolor egestas lectus, sit amet mattis purus + dui nec risus. Maecenas non sodales nisi, vel dictum dolor. Class aptent taciti sociosqu ad + litora torquent per conubia nostra, per inceptos himenaeos. Suspendisse blandit eleifend + diam, vel rutrum tellus vulputate quis. Aliquam eget libero aliquet, imperdiet nisl a, + ornare ex. Sed rhoncus est ut libero porta lobortis. Fusce in dictum tellus.\n\n + Suspendisse interdum ornare ante. Aliquam nec cursus lorem. Morbi id magna felis. Vivamus + egestas, est a condimentum egestas, turpis nisl iaculis ipsum, in dictum tellus dolor sed + neque. Morbi tellus erat, dapibus ut sem a, iaculis tincidunt dui. Interdum et malesuada + fames ac ante ipsum primis in faucibus. Curabitur et eros porttitor, ultricies urna vitae, + molestie nibh. Phasellus at commodo eros, non aliquet metus. Sed maximus nisl nec dolor + bibendum, vel congue leo egestas.\n\n + Sed interdum tortor nibh, in sagittis risus mollis quis. Curabitur mi odio, condimentum sit + amet auctor at, mollis non turpis. Nullam pretium libero vestibulum, finibus orci vel, + molestie quam. Fusce blandit tincidunt nulla, quis sollicitudin libero facilisis et. Integer + interdum nunc ligula, et fermentum metus hendrerit id. Vestibulum lectus felis, dictum at + lacinia sit amet, tristique id quam. Cras eu consequat dui. Suspendisse sodales nunc ligula, + in lobortis sem porta sed. Integer id ultrices magna, in luctus elit. Sed a pellentesque + est.\n\n + Aenean nunc velit, lacinia sed dolor sed, ultrices viverra nulla. Etiam a venenatis nibh. + Morbi laoreet, tortor sed facilisis varius, nibh orci rhoncus nulla, id elementum leo dui + non lorem. Nam mollis ipsum quis auctor varius. Quisque elementum eu libero sed commodo. In + eros nisl, imperdiet vel imperdiet et, scelerisque a mauris. Pellentesque varius ex nunc, + quis imperdiet eros placerat ac. Duis finibus orci et est auctor tincidunt. Sed non viverra + ipsum. Nunc quis augue egestas, cursus lorem at, molestie sem. Morbi a consectetur ipsum, a + placerat diam. Etiam vulputate dignissim convallis. Integer faucibus mauris sit amet finibus + convallis.\n\n + Phasellus in aliquet mi. Pellentesque habitant morbi tristique senectus et netus et + malesuada fames ac turpis egestas. In volutpat arcu ut felis sagittis, in finibus massa + gravida. Pellentesque id tellus orci. Integer dictum, lorem sed efficitur ullamcorper, + libero justo consectetur ipsum, in mollis nisl ex sed nisl. Donec maximus ullamcorper + sodales. Praesent bibendum rhoncus tellus nec feugiat. In a ornare nulla. Donec rhoncus + libero vel nunc consequat, quis tincidunt nisl eleifend. Cras bibendum enim a justo luctus + vestibulum. Fusce dictum libero quis erat maximus, vitae volutpat diam dignissim. + + \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/feed-view/app/src/main/res/values/themes.xml b/samples/user-interface/canonical-layouts/feed-view/app/src/main/res/values/themes.xml new file mode 100644 index 00000000..60dad6d4 --- /dev/null +++ b/samples/user-interface/canonical-layouts/feed-view/app/src/main/res/values/themes.xml @@ -0,0 +1,26 @@ + + + + + + + + + diff --git a/samples/user-interface/canonical-layouts/list-detail-activity-embedding/app/src/main/res/values-w1240dp/dimens.xml b/samples/user-interface/canonical-layouts/list-detail-activity-embedding/app/src/main/res/values-w1240dp/dimens.xml new file mode 100644 index 00000000..7ebd41d6 --- /dev/null +++ b/samples/user-interface/canonical-layouts/list-detail-activity-embedding/app/src/main/res/values-w1240dp/dimens.xml @@ -0,0 +1,19 @@ + + + + 200dp + diff --git a/samples/user-interface/canonical-layouts/list-detail-activity-embedding/app/src/main/res/values-w600dp/dimens.xml b/samples/user-interface/canonical-layouts/list-detail-activity-embedding/app/src/main/res/values-w600dp/dimens.xml new file mode 100644 index 00000000..07243524 --- /dev/null +++ b/samples/user-interface/canonical-layouts/list-detail-activity-embedding/app/src/main/res/values-w600dp/dimens.xml @@ -0,0 +1,19 @@ + + + + 48dp + diff --git a/samples/user-interface/canonical-layouts/list-detail-activity-embedding/app/src/main/res/values/colors.xml b/samples/user-interface/canonical-layouts/list-detail-activity-embedding/app/src/main/res/values/colors.xml new file mode 100644 index 00000000..1da805df --- /dev/null +++ b/samples/user-interface/canonical-layouts/list-detail-activity-embedding/app/src/main/res/values/colors.xml @@ -0,0 +1,25 @@ + + + + #FFBB86FC + #FF6200EE + #FF3700B3 + #FF03DAC5 + #FF018786 + #FF000000 + #FFFFFFFF + diff --git a/samples/user-interface/canonical-layouts/list-detail-activity-embedding/app/src/main/res/values/dimens.xml b/samples/user-interface/canonical-layouts/list-detail-activity-embedding/app/src/main/res/values/dimens.xml new file mode 100644 index 00000000..47c0c27b --- /dev/null +++ b/samples/user-interface/canonical-layouts/list-detail-activity-embedding/app/src/main/res/values/dimens.xml @@ -0,0 +1,19 @@ + + + + 16dp + diff --git a/samples/user-interface/canonical-layouts/list-detail-activity-embedding/app/src/main/res/values/strings.xml b/samples/user-interface/canonical-layouts/list-detail-activity-embedding/app/src/main/res/values/strings.xml new file mode 100644 index 00000000..082c4a58 --- /dev/null +++ b/samples/user-interface/canonical-layouts/list-detail-activity-embedding/app/src/main/res/values/strings.xml @@ -0,0 +1,62 @@ + + + + ActivityEmbedding Sample + DetailActivity + + First Fragment + Second Fragment + Next + Previous + + + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam in scelerisque sem. Mauris + volutpat, dolor id interdum ullamcorper, risus dolor egestas lectus, sit amet mattis purus + dui nec risus. Maecenas non sodales nisi, vel dictum dolor. Class aptent taciti sociosqu ad + litora torquent per conubia nostra, per inceptos himenaeos. Suspendisse blandit eleifend + diam, vel rutrum tellus vulputate quis. Aliquam eget libero aliquet, imperdiet nisl a, + ornare ex. Sed rhoncus est ut libero porta lobortis. Fusce in dictum tellus.\n\n + Suspendisse interdum ornare ante. Aliquam nec cursus lorem. Morbi id magna felis. Vivamus + egestas, est a condimentum egestas, turpis nisl iaculis ipsum, in dictum tellus dolor sed + neque. Morbi tellus erat, dapibus ut sem a, iaculis tincidunt dui. Interdum et malesuada + fames ac ante ipsum primis in faucibus. Curabitur et eros porttitor, ultricies urna vitae, + molestie nibh. Phasellus at commodo eros, non aliquet metus. Sed maximus nisl nec dolor + bibendum, vel congue leo egestas.\n\n + Sed interdum tortor nibh, in sagittis risus mollis quis. Curabitur mi odio, condimentum sit + amet auctor at, mollis non turpis. Nullam pretium libero vestibulum, finibus orci vel, + molestie quam. Fusce blandit tincidunt nulla, quis sollicitudin libero facilisis et. Integer + interdum nunc ligula, et fermentum metus hendrerit id. Vestibulum lectus felis, dictum at + lacinia sit amet, tristique id quam. Cras eu consequat dui. Suspendisse sodales nunc ligula, + in lobortis sem porta sed. Integer id ultrices magna, in luctus elit. Sed a pellentesque + est.\n\n + Aenean nunc velit, lacinia sed dolor sed, ultrices viverra nulla. Etiam a venenatis nibh. + Morbi laoreet, tortor sed facilisis varius, nibh orci rhoncus nulla, id elementum leo dui + non lorem. Nam mollis ipsum quis auctor varius. Quisque elementum eu libero sed commodo. In + eros nisl, imperdiet vel imperdiet et, scelerisque a mauris. Pellentesque varius ex nunc, + quis imperdiet eros placerat ac. Duis finibus orci et est auctor tincidunt. Sed non viverra + ipsum. Nunc quis augue egestas, cursus lorem at, molestie sem. Morbi a consectetur ipsum, a + placerat diam. Etiam vulputate dignissim convallis. Integer faucibus mauris sit amet finibus + convallis.\n\n + Phasellus in aliquet mi. Pellentesque habitant morbi tristique senectus et netus et + malesuada fames ac turpis egestas. In volutpat arcu ut felis sagittis, in finibus massa + gravida. Pellentesque id tellus orci. Integer dictum, lorem sed efficitur ullamcorper, + libero justo consectetur ipsum, in mollis nisl ex sed nisl. Donec maximus ullamcorper + sodales. Praesent bibendum rhoncus tellus nec feugiat. In a ornare nulla. Donec rhoncus + libero vel nunc consequat, quis tincidunt nisl eleifend. Cras bibendum enim a justo luctus + vestibulum. Fusce dictum libero quis erat maximus, vitae volutpat diam dignissim. + + \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/list-detail-activity-embedding/app/src/main/res/values/themes.xml b/samples/user-interface/canonical-layouts/list-detail-activity-embedding/app/src/main/res/values/themes.xml new file mode 100644 index 00000000..d59b482c --- /dev/null +++ b/samples/user-interface/canonical-layouts/list-detail-activity-embedding/app/src/main/res/values/themes.xml @@ -0,0 +1,32 @@ + + + + + + diff --git a/samples/user-interface/canonical-layouts/list-detail-activity-embedding/app/src/main/res/xml/backup_rules.xml b/samples/user-interface/canonical-layouts/list-detail-activity-embedding/app/src/main/res/xml/backup_rules.xml new file mode 100644 index 00000000..96208caf --- /dev/null +++ b/samples/user-interface/canonical-layouts/list-detail-activity-embedding/app/src/main/res/xml/backup_rules.xml @@ -0,0 +1,29 @@ + + + + + + diff --git a/samples/user-interface/canonical-layouts/list-detail-activity-embedding/app/src/main/res/xml/data_extraction_rules.xml b/samples/user-interface/canonical-layouts/list-detail-activity-embedding/app/src/main/res/xml/data_extraction_rules.xml new file mode 100644 index 00000000..74b15adc --- /dev/null +++ b/samples/user-interface/canonical-layouts/list-detail-activity-embedding/app/src/main/res/xml/data_extraction_rules.xml @@ -0,0 +1,35 @@ + + + + + + + + + diff --git a/samples/user-interface/canonical-layouts/list-detail-activity-embedding/app/src/main/res/xml/split_configuration.xml b/samples/user-interface/canonical-layouts/list-detail-activity-embedding/app/src/main/res/xml/split_configuration.xml new file mode 100644 index 00000000..33bd9abb --- /dev/null +++ b/samples/user-interface/canonical-layouts/list-detail-activity-embedding/app/src/main/res/xml/split_configuration.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + diff --git a/samples/user-interface/canonical-layouts/list-detail-activity-embedding/build.gradle b/samples/user-interface/canonical-layouts/list-detail-activity-embedding/build.gradle new file mode 100644 index 00000000..6dd3c517 --- /dev/null +++ b/samples/user-interface/canonical-layouts/list-detail-activity-embedding/build.gradle @@ -0,0 +1,21 @@ +/* + * 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 + * + * 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. + */ +// Top-level build file where you can add configuration options common to all sub-projects/modules. +plugins { + id 'com.android.application' version '8.6.0' apply false + id 'com.android.library' version '8.6.0' apply false + id 'org.jetbrains.kotlin.android' version '2.0.0' apply false +} diff --git a/samples/user-interface/canonical-layouts/list-detail-activity-embedding/gradle.properties b/samples/user-interface/canonical-layouts/list-detail-activity-embedding/gradle.properties new file mode 100644 index 00000000..d6dbd9ad --- /dev/null +++ b/samples/user-interface/canonical-layouts/list-detail-activity-embedding/gradle.properties @@ -0,0 +1,30 @@ +# 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 +android.nonFinalResIds=false \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/list-detail-activity-embedding/gradle/wrapper/gradle-wrapper.jar b/samples/user-interface/canonical-layouts/list-detail-activity-embedding/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 00000000..e708b1c0 Binary files /dev/null and b/samples/user-interface/canonical-layouts/list-detail-activity-embedding/gradle/wrapper/gradle-wrapper.jar differ diff --git a/samples/user-interface/canonical-layouts/list-detail-activity-embedding/gradle/wrapper/gradle-wrapper.properties b/samples/user-interface/canonical-layouts/list-detail-activity-embedding/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..3e3ea3fa --- /dev/null +++ b/samples/user-interface/canonical-layouts/list-detail-activity-embedding/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Mon Sep 12 11:25:57 GST 2022 +distributionBase=GRADLE_USER_HOME +distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionPath=wrapper/dists +zipStorePath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME diff --git a/samples/user-interface/canonical-layouts/list-detail-activity-embedding/gradlew b/samples/user-interface/canonical-layouts/list-detail-activity-embedding/gradlew new file mode 100755 index 00000000..4f906e0c --- /dev/null +++ b/samples/user-interface/canonical-layouts/list-detail-activity-embedding/gradlew @@ -0,0 +1,185 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or 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 UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# 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"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# 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 + ;; + 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" + which java >/dev/null 2>&1 || 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 + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/samples/user-interface/canonical-layouts/list-detail-activity-embedding/gradlew.bat b/samples/user-interface/canonical-layouts/list-detail-activity-embedding/gradlew.bat new file mode 100644 index 00000000..107acd32 --- /dev/null +++ b/samples/user-interface/canonical-layouts/list-detail-activity-embedding/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/samples/user-interface/canonical-layouts/list-detail-activity-embedding/settings.gradle b/samples/user-interface/canonical-layouts/list-detail-activity-embedding/settings.gradle new file mode 100644 index 00000000..4ecdb893 --- /dev/null +++ b/samples/user-interface/canonical-layouts/list-detail-activity-embedding/settings.gradle @@ -0,0 +1,31 @@ +/* + * 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 + * + * 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. + */ +pluginManagement { + repositories { + gradlePluginPortal() + google() + mavenCentral() + } +} +dependencyResolutionManagement { + repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) + repositories { + google() + mavenCentral() + } +} +rootProject.name = "ActivityEmbedding Sample" +include ':app' diff --git a/samples/user-interface/canonical-layouts/list-detail-compose/.gitignore b/samples/user-interface/canonical-layouts/list-detail-compose/.gitignore new file mode 100644 index 00000000..aa724b77 --- /dev/null +++ b/samples/user-interface/canonical-layouts/list-detail-compose/.gitignore @@ -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 diff --git a/samples/user-interface/canonical-layouts/list-detail-compose/.idea/.gitignore b/samples/user-interface/canonical-layouts/list-detail-compose/.idea/.gitignore new file mode 100644 index 00000000..26d33521 --- /dev/null +++ b/samples/user-interface/canonical-layouts/list-detail-compose/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/samples/user-interface/canonical-layouts/list-detail-compose/.idea/.name b/samples/user-interface/canonical-layouts/list-detail-compose/.idea/.name new file mode 100644 index 00000000..7e70adc2 --- /dev/null +++ b/samples/user-interface/canonical-layouts/list-detail-compose/.idea/.name @@ -0,0 +1 @@ +ListDetailCompose \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/list-detail-compose/.idea/appInsightsSettings.xml b/samples/user-interface/canonical-layouts/list-detail-compose/.idea/appInsightsSettings.xml new file mode 100644 index 00000000..371f2e29 --- /dev/null +++ b/samples/user-interface/canonical-layouts/list-detail-compose/.idea/appInsightsSettings.xml @@ -0,0 +1,26 @@ + + + + + + \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/list-detail-compose/.idea/codeStyles/Project.xml b/samples/user-interface/canonical-layouts/list-detail-compose/.idea/codeStyles/Project.xml new file mode 100644 index 00000000..7643783a --- /dev/null +++ b/samples/user-interface/canonical-layouts/list-detail-compose/.idea/codeStyles/Project.xml @@ -0,0 +1,123 @@ + + + + + + + + + + \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/list-detail-compose/.idea/codeStyles/codeStyleConfig.xml b/samples/user-interface/canonical-layouts/list-detail-compose/.idea/codeStyles/codeStyleConfig.xml new file mode 100644 index 00000000..79ee123c --- /dev/null +++ b/samples/user-interface/canonical-layouts/list-detail-compose/.idea/codeStyles/codeStyleConfig.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/list-detail-compose/.idea/compiler.xml b/samples/user-interface/canonical-layouts/list-detail-compose/.idea/compiler.xml new file mode 100644 index 00000000..b86273d9 --- /dev/null +++ b/samples/user-interface/canonical-layouts/list-detail-compose/.idea/compiler.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/list-detail-compose/.idea/deploymentTargetDropDown.xml b/samples/user-interface/canonical-layouts/list-detail-compose/.idea/deploymentTargetDropDown.xml new file mode 100644 index 00000000..0c0c3383 --- /dev/null +++ b/samples/user-interface/canonical-layouts/list-detail-compose/.idea/deploymentTargetDropDown.xml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/list-detail-compose/.idea/deploymentTargetSelector.xml b/samples/user-interface/canonical-layouts/list-detail-compose/.idea/deploymentTargetSelector.xml new file mode 100644 index 00000000..4f2595c0 --- /dev/null +++ b/samples/user-interface/canonical-layouts/list-detail-compose/.idea/deploymentTargetSelector.xml @@ -0,0 +1,18 @@ + + + + + + + + + \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/list-detail-compose/.idea/inspectionProfiles/Project_Default.xml b/samples/user-interface/canonical-layouts/list-detail-compose/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 00000000..910c7a20 --- /dev/null +++ b/samples/user-interface/canonical-layouts/list-detail-compose/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,59 @@ + + + + \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/list-detail-compose/.idea/kotlinc.xml b/samples/user-interface/canonical-layouts/list-detail-compose/.idea/kotlinc.xml new file mode 100644 index 00000000..6d0ee1c2 --- /dev/null +++ b/samples/user-interface/canonical-layouts/list-detail-compose/.idea/kotlinc.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/list-detail-compose/.idea/migrations.xml b/samples/user-interface/canonical-layouts/list-detail-compose/.idea/migrations.xml new file mode 100644 index 00000000..f8051a6f --- /dev/null +++ b/samples/user-interface/canonical-layouts/list-detail-compose/.idea/migrations.xml @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/list-detail-compose/.idea/misc.xml b/samples/user-interface/canonical-layouts/list-detail-compose/.idea/misc.xml new file mode 100644 index 00000000..74dd639e --- /dev/null +++ b/samples/user-interface/canonical-layouts/list-detail-compose/.idea/misc.xml @@ -0,0 +1,10 @@ + + + + + + + + + \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/list-detail-compose/.idea/other.xml b/samples/user-interface/canonical-layouts/list-detail-compose/.idea/other.xml new file mode 100644 index 00000000..94c96f63 --- /dev/null +++ b/samples/user-interface/canonical-layouts/list-detail-compose/.idea/other.xml @@ -0,0 +1,318 @@ + + + + + + \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/list-detail-compose/.idea/runConfigurations.xml b/samples/user-interface/canonical-layouts/list-detail-compose/.idea/runConfigurations.xml new file mode 100644 index 00000000..16660f1d --- /dev/null +++ b/samples/user-interface/canonical-layouts/list-detail-compose/.idea/runConfigurations.xml @@ -0,0 +1,17 @@ + + + + + + \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/list-detail-compose/.idea/studiobot.xml b/samples/user-interface/canonical-layouts/list-detail-compose/.idea/studiobot.xml new file mode 100644 index 00000000..539e3b80 --- /dev/null +++ b/samples/user-interface/canonical-layouts/list-detail-compose/.idea/studiobot.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/list-detail-compose/.idea/vcs.xml b/samples/user-interface/canonical-layouts/list-detail-compose/.idea/vcs.xml new file mode 100644 index 00000000..b2bdec2d --- /dev/null +++ b/samples/user-interface/canonical-layouts/list-detail-compose/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/list-detail-compose/app/.gitignore b/samples/user-interface/canonical-layouts/list-detail-compose/app/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/samples/user-interface/canonical-layouts/list-detail-compose/app/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/list-detail-compose/app/build.gradle b/samples/user-interface/canonical-layouts/list-detail-compose/app/build.gradle new file mode 100644 index 00000000..57db8474 --- /dev/null +++ b/samples/user-interface/canonical-layouts/list-detail-compose/app/build.gradle @@ -0,0 +1,87 @@ +/* + * 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 + * + * 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. + */ +plugins { + id 'com.android.application' + id 'org.jetbrains.kotlin.android' + id("org.jetbrains.kotlin.plugin.compose") version "2.0.0" +} + +android { + namespace 'com.example.listdetailcompose' + compileSdk 35 + + defaultConfig { + applicationId "com.example.listdetailcompose" + minSdk 21 + targetSdk 35 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + vectorDrawables { + useSupportLibrary true + } + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + kotlinOptions { + jvmTarget = '1.8' + } + buildFeatures { + compose true + } + packagingOptions { + resources { + excludes += '/META-INF/{AL2.0,LGPL2.1}' + } + } +} + +composeCompiler { + enableStrongSkippingMode = true + + reportsDestination = layout.buildDirectory.dir("compose_compiler") +} + +dependencies { + def composeBom = platform('androidx.compose:compose-bom:2024.09.03') + implementation(composeBom) + + implementation "com.google.accompanist:accompanist-adaptive:0.32.0" + implementation 'androidx.core:core-ktx:1.13.1' + implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.8.6' + implementation 'androidx.activity:activity-compose:1.9.2' + implementation "androidx.compose.foundation:foundation:1.7.3" + implementation "androidx.compose.ui:ui:1.7.3" + implementation "androidx.compose.ui:ui-tooling-preview" + implementation "androidx.window:window:1.3.0" + implementation 'androidx.compose.material3:material3:1.3.0' + implementation 'androidx.compose.material3.adaptive:adaptive:1.1.0-alpha04' + implementation 'androidx.compose.material3.adaptive:adaptive-layout:1.1.0-alpha04' + implementation 'androidx.compose.material3.adaptive:adaptive-navigation:1.1.0-alpha04' + implementation "androidx.compose.material3:material3-window-size-class:1.3.0" + implementation "androidx.compose.animation:animation:1.7.3" + testImplementation 'junit:junit:4.13.2' +} diff --git a/samples/user-interface/canonical-layouts/list-detail-compose/app/proguard-rules.pro b/samples/user-interface/canonical-layouts/list-detail-compose/app/proguard-rules.pro new file mode 100644 index 00000000..481bb434 --- /dev/null +++ b/samples/user-interface/canonical-layouts/list-detail-compose/app/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/list-detail-compose/app/src/main/AndroidManifest.xml b/samples/user-interface/canonical-layouts/list-detail-compose/app/src/main/AndroidManifest.xml new file mode 100644 index 00000000..d313a901 --- /dev/null +++ b/samples/user-interface/canonical-layouts/list-detail-compose/app/src/main/AndroidManifest.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/list-detail-compose/app/src/main/java/com/example/listdetailcompose/MainActivity.kt b/samples/user-interface/canonical-layouts/list-detail-compose/app/src/main/java/com/example/listdetailcompose/MainActivity.kt new file mode 100644 index 00000000..46b41979 --- /dev/null +++ b/samples/user-interface/canonical-layouts/list-detail-compose/app/src/main/java/com/example/listdetailcompose/MainActivity.kt @@ -0,0 +1,38 @@ +/* + * 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 + * + * 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.listdetailcompose + +import android.os.Bundle +import androidx.activity.ComponentActivity +import androidx.activity.compose.setContent +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.material3.Surface +import androidx.compose.ui.Modifier +import com.example.listdetailcompose.ui.ListDetailSample +import com.example.listdetailcompose.ui.theme.ListDetailComposeTheme + +class MainActivity : ComponentActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContent { + ListDetailComposeTheme { + Surface(modifier = Modifier.fillMaxSize()) { + ListDetailSample() + } + } + } + } +} diff --git a/samples/user-interface/canonical-layouts/list-detail-compose/app/src/main/java/com/example/listdetailcompose/ui/ListDetailSample.kt b/samples/user-interface/canonical-layouts/list-detail-compose/app/src/main/java/com/example/listdetailcompose/ui/ListDetailSample.kt new file mode 100644 index 00000000..cfcee90e --- /dev/null +++ b/samples/user-interface/canonical-layouts/list-detail-compose/app/src/main/java/com/example/listdetailcompose/ui/ListDetailSample.kt @@ -0,0 +1,353 @@ +/* + * 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. + */ + +@file:OptIn(ExperimentalSharedTransitionApi::class, ExperimentalSharedTransitionApi::class) + +package com.example.listdetailcompose.ui + +import android.annotation.SuppressLint +import androidx.activity.compose.BackHandler +import androidx.annotation.DrawableRes +import androidx.compose.animation.AnimatedContent +import androidx.compose.animation.AnimatedVisibilityScope +import androidx.compose.animation.EnterTransition +import androidx.compose.animation.ExperimentalSharedTransitionApi +import androidx.compose.animation.SharedTransitionLayout +import androidx.compose.animation.SharedTransitionScope +import androidx.compose.animation.slideIn +import androidx.compose.animation.slideInVertically +import androidx.compose.animation.slideOutHorizontally +import androidx.compose.animation.slideOutVertically +import androidx.compose.foundation.BorderStroke +import androidx.compose.foundation.Image +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.itemsIndexed +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.selection.selectable +import androidx.compose.foundation.selection.selectableGroup +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.Card +import androidx.compose.material3.CardDefaults +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi +import androidx.compose.material3.adaptive.currentWindowAdaptiveInfo +import androidx.compose.material3.adaptive.layout.AnimatedPane +import androidx.compose.material3.adaptive.layout.ListDetailPaneScaffold +import androidx.compose.material3.adaptive.layout.ListDetailPaneScaffoldRole +import androidx.compose.material3.adaptive.layout.PaneAdaptedValue +import androidx.compose.material3.adaptive.layout.PaneExpansionDragHandle +import androidx.compose.material3.adaptive.layout.rememberPaneExpansionState +import androidx.compose.material3.adaptive.navigation.rememberListDetailPaneScaffoldNavigator +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.LocalConfiguration +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import androidx.window.core.layout.WindowWidthSizeClass +import com.example.listdetailcompose.R +import kotlinx.coroutines.launch + +// Create some simple sample data +private val loremIpsum = """ + |Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Dui nunc mattis enim ut tellus elementum sagittis. Nunc sed augue lacus viverra vitae. Sit amet dictum sit amet justo donec. Fringilla urna porttitor rhoncus dolor purus non enim praesent elementum. Dictum non consectetur a erat nam at lectus urna. Tellus mauris a diam maecenas sed enim ut sem viverra. Commodo ullamcorper a lacus vestibulum sed arcu non. Lorem mollis aliquam ut porttitor leo a diam sollicitudin tempor. Pellentesque habitant morbi tristique senectus et netus et malesuada. Vitae suscipit tellus mauris a diam maecenas sed. Neque ornare aenean euismod elementum nisi quis. Quam vulputate dignissim suspendisse in est ante in nibh mauris. Tellus in metus vulputate eu scelerisque felis imperdiet proin fermentum. Orci ac auctor augue mauris augue neque gravida. + | + |Tempus quam pellentesque nec nam aliquam. Praesent semper feugiat nibh sed. Adipiscing elit duis tristique sollicitudin nibh sit. Netus et malesuada fames ac turpis egestas sed tempus urna. Quis varius quam quisque id diam vel quam. Urna duis convallis convallis tellus id interdum velit laoreet. Id eu nisl nunc mi ipsum. Fermentum dui faucibus in ornare. Nunc lobortis mattis aliquam faucibus. Vulputate mi sit amet mauris commodo quis. Porta nibh venenatis cras sed. Vitae tortor condimentum lacinia quis vel eros donec. Eu non diam phasellus vestibulum. + """.trimMargin() +private val sampleWords = listOf( + "Apple" to R.drawable.ic_food, + "Banana" to R.drawable.ic_no_food, + "Cherry" to R.drawable.ic_food, + "Date" to R.drawable.ic_no_food, + "Elderberry" to R.drawable.ic_food, + "Fig" to R.drawable.ic_no_food, + "Grape" to R.drawable.ic_food, + "Honeydew" to R.drawable.ic_no_food, +).map { (word, icon) -> DefinedWord(word, icon) } + +private data class DefinedWord( + val word: String, + @DrawableRes val icon: Int, + val definition: String = loremIpsum +) + +@SuppressLint("UnusedContentLambdaTargetStateParameter") +@OptIn(ExperimentalMaterial3AdaptiveApi::class) +@Composable +fun ListDetailSample() { + val coroutineScope = rememberCoroutineScope() + var selectedWordIndex: Int? by rememberSaveable { mutableStateOf(null) } + val navigator = rememberListDetailPaneScaffoldNavigator() + val isListAndDetailVisible = + navigator.scaffoldValue[ListDetailPaneScaffoldRole.Detail] == PaneAdaptedValue.Expanded && navigator.scaffoldValue[ListDetailPaneScaffoldRole.List] == PaneAdaptedValue.Expanded + + BackHandler(enabled = navigator.canNavigateBack()) { + coroutineScope.launch { + navigator.navigateBack() + } + } + + SharedTransitionLayout { + AnimatedContent(targetState = isListAndDetailVisible, label = "simple sample") { + ListDetailPaneScaffold( + directive = navigator.scaffoldDirective, + value = navigator.scaffoldValue, + listPane = { + val currentSelectedWordIndex = selectedWordIndex + val isDetailVisible = + navigator.scaffoldValue[ListDetailPaneScaffoldRole.Detail] == PaneAdaptedValue.Expanded + AnimatedPane { + ListContent( + words = sampleWords, + selectionState = if (isDetailVisible && currentSelectedWordIndex != null) { + SelectionVisibilityState.ShowSelection(currentSelectedWordIndex) + } else { + SelectionVisibilityState.NoSelection + }, + onIndexClick = { index -> + selectedWordIndex = index + coroutineScope.launch { + navigator.navigateTo(ListDetailPaneScaffoldRole.Detail) + } + }, + isListAndDetailVisible = isListAndDetailVisible, + isListVisible = !isDetailVisible, + animatedVisibilityScope = this@AnimatedPane, + sharedTransitionScope = this@SharedTransitionLayout + ) + } + }, + detailPane = { + val definedWord = selectedWordIndex?.let(sampleWords::get) + val isDetailVisible = + navigator.scaffoldValue[ListDetailPaneScaffoldRole.Detail] == PaneAdaptedValue.Expanded + AnimatedPane { + DetailContent( + definedWord = definedWord, + modifier = Modifier.animateEnterExit(enter = slideInVertically(), exit = slideOutVertically()), + isListAndDetailVisible = isListAndDetailVisible, + isDetailVisible = isDetailVisible, + animatedVisibilityScope = this@AnimatedPane, + sharedTransitionScope = this@SharedTransitionLayout + ) + } + }, + paneExpansionState = rememberPaneExpansionState(navigator.scaffoldValue), + paneExpansionDragHandle = { state -> + PaneExpansionDragHandle(state, Color.Red) + } + ) + } + } +} + +/** + * The description of the selection state for the [ListContent] + */ +sealed interface SelectionVisibilityState { + + /** + * No selection should be shown, and each item should be clickable. + */ + object NoSelection : SelectionVisibilityState + + /** + * Selection state should be shown, and each item should be selectable. + */ + data class ShowSelection( + /** + * The index of the word that is selected. + */ + val selectedWordIndex: Int + ) : SelectionVisibilityState +} + +/** + * The content for the list pane. + */ +@Composable +private fun ListContent( + words: List, + selectionState: SelectionVisibilityState, + onIndexClick: (index: Int) -> Unit, + modifier: Modifier = Modifier, + isListAndDetailVisible: Boolean, + isListVisible: Boolean, + sharedTransitionScope: SharedTransitionScope, + animatedVisibilityScope: AnimatedVisibilityScope +) { + LazyColumn( + contentPadding = PaddingValues(vertical = 16.dp), + verticalArrangement = Arrangement.spacedBy(16.dp), + modifier = modifier + .then( + when (selectionState) { + SelectionVisibilityState.NoSelection -> Modifier + is SelectionVisibilityState.ShowSelection -> Modifier.selectableGroup() + } + ) + ) { + itemsIndexed(words) { index, word -> + + val interactionModifier = when (selectionState) { + SelectionVisibilityState.NoSelection -> { + Modifier.clickable( + onClick = { onIndexClick(index) } + ) + } + + is SelectionVisibilityState.ShowSelection -> { + Modifier.selectable( + selected = index == selectionState.selectedWordIndex, + onClick = { onIndexClick(index) } + ) + } + } + + val containerColor = when (selectionState) { + SelectionVisibilityState.NoSelection -> MaterialTheme.colorScheme.surface + is SelectionVisibilityState.ShowSelection -> + if (index == selectionState.selectedWordIndex) { + MaterialTheme.colorScheme.surfaceVariant + } else { + MaterialTheme.colorScheme.surface + } + } + val borderStroke = when (selectionState) { + SelectionVisibilityState.NoSelection -> BorderStroke( + 1.dp, + MaterialTheme.colorScheme.outline + ) + + is SelectionVisibilityState.ShowSelection -> + if (index == selectionState.selectedWordIndex) { + null + } else { + BorderStroke( + 1.dp, + MaterialTheme.colorScheme.outline + ) + } + } + + // TODO: Card selection overfills the Card + Card( + colors = CardDefaults.cardColors(containerColor = containerColor), + border = borderStroke, + modifier = Modifier + .then(interactionModifier) + .fillMaxWidth() + ) { + Row { + val imageModifier = Modifier.padding(horizontal = 8.dp) + if (!isListAndDetailVisible && isListVisible) { + with(sharedTransitionScope) { + val state = rememberSharedContentState(key = word.word) + imageModifier.then( + Modifier.sharedElement( + state, + animatedVisibilityScope = animatedVisibilityScope + ) + ) + } + } + + Image( + painter = painterResource(id = word.icon), + contentDescription = word.word, + modifier = imageModifier + ) + Text( + text = word.word, + modifier = Modifier + .fillMaxWidth() + .padding(8.dp) + ) + } + + } + } + } +} + +/** + * The content for the detail pane. + */ +@Composable +private fun DetailContent( + definedWord: DefinedWord?, + modifier: Modifier = Modifier, + isListAndDetailVisible: Boolean, + isDetailVisible: Boolean, + sharedTransitionScope: SharedTransitionScope, + animatedVisibilityScope: AnimatedVisibilityScope +) { + Column( + modifier = modifier + .verticalScroll(rememberScrollState()) + .padding(vertical = 16.dp) + ) { + if (definedWord != null) { + + val imageModifier = Modifier + .padding(horizontal = 8.dp) + .then( + if (!isListAndDetailVisible && isDetailVisible) { + with(sharedTransitionScope) { + val state = rememberSharedContentState(key = definedWord.word) + Modifier.sharedElement( + state, + animatedVisibilityScope = animatedVisibilityScope + ) + } + } else { + Modifier + } + ) + + Image( + painter = painterResource(id = definedWord.icon), + contentDescription = definedWord.word, + modifier = imageModifier + ) + Text( + text = definedWord.word, + style = MaterialTheme.typography.headlineMedium + ) + Text( + text = definedWord.definition + ) + } else { + Text( + text = stringResource(R.string.placeholder) + ) + } + } +} diff --git a/samples/user-interface/canonical-layouts/list-detail-compose/app/src/main/java/com/example/listdetailcompose/ui/theme/Color.kt b/samples/user-interface/canonical-layouts/list-detail-compose/app/src/main/java/com/example/listdetailcompose/ui/theme/Color.kt new file mode 100644 index 00000000..3b7bb1b7 --- /dev/null +++ b/samples/user-interface/canonical-layouts/list-detail-compose/app/src/main/java/com/example/listdetailcompose/ui/theme/Color.kt @@ -0,0 +1,27 @@ +/* + * 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 + * + * 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.listdetailcompose.ui.theme + +import androidx.compose.ui.graphics.Color + +val Purple80 = Color(0xFFD0BCFF) +val PurpleGrey80 = Color(0xFFCCC2DC) +val Pink80 = Color(0xFFEFB8C8) + +val Purple40 = Color(0xFF6650a4) +val PurpleGrey40 = Color(0xFF625b71) +val Pink40 = Color(0xFF7D5260) \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/list-detail-compose/app/src/main/java/com/example/listdetailcompose/ui/theme/Theme.kt b/samples/user-interface/canonical-layouts/list-detail-compose/app/src/main/java/com/example/listdetailcompose/ui/theme/Theme.kt new file mode 100644 index 00000000..c5c514ff --- /dev/null +++ b/samples/user-interface/canonical-layouts/list-detail-compose/app/src/main/java/com/example/listdetailcompose/ui/theme/Theme.kt @@ -0,0 +1,88 @@ +/* + * 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 + * + * 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.listdetailcompose.ui.theme + +import android.app.Activity +import android.os.Build +import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.darkColorScheme +import androidx.compose.material3.dynamicDarkColorScheme +import androidx.compose.material3.dynamicLightColorScheme +import androidx.compose.material3.lightColorScheme +import androidx.compose.runtime.Composable +import androidx.compose.runtime.SideEffect +import androidx.compose.ui.graphics.toArgb +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalView +import androidx.core.view.ViewCompat +import androidx.core.view.WindowCompat + +private val DarkColorScheme = darkColorScheme( + primary = Purple80, + secondary = PurpleGrey80, + tertiary = Pink80 +) + +private val LightColorScheme = lightColorScheme( + primary = Purple40, + secondary = PurpleGrey40, + tertiary = Pink40 + + /* Other default colors to override + background = Color(0xFFFFFBFE), + surface = Color(0xFFFFFBFE), + onPrimary = Color.White, + onSecondary = Color.White, + onTertiary = Color.White, + onBackground = Color(0xFF1C1B1F), + onSurface = Color(0xFF1C1B1F), + */ +) + +@Composable +fun ListDetailComposeTheme( + darkTheme: Boolean = isSystemInDarkTheme(), + // Dynamic color is available on Android 12+ + dynamicColor: Boolean = true, + content: @Composable () -> Unit +) { + val colorScheme = when { + dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> { + val context = LocalContext.current + if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) + } + darkTheme -> DarkColorScheme + else -> LightColorScheme + } + val view = LocalView.current + if (!view.isInEditMode) { + SideEffect { + (view.context as Activity).window.statusBarColor = colorScheme.primary.toArgb() + WindowCompat.getInsetsController( + (view.context as Activity).window, + view + ).isAppearanceLightStatusBars = darkTheme + } + } + + MaterialTheme( + colorScheme = colorScheme, + typography = Typography, + content = content + ) +} \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/list-detail-compose/app/src/main/java/com/example/listdetailcompose/ui/theme/Type.kt b/samples/user-interface/canonical-layouts/list-detail-compose/app/src/main/java/com/example/listdetailcompose/ui/theme/Type.kt new file mode 100644 index 00000000..29f381ee --- /dev/null +++ b/samples/user-interface/canonical-layouts/list-detail-compose/app/src/main/java/com/example/listdetailcompose/ui/theme/Type.kt @@ -0,0 +1,50 @@ +/* + * 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 + * + * 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.listdetailcompose.ui.theme + +import androidx.compose.material3.Typography +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.sp + +// Set of Material typography styles to start with +val Typography = Typography( + bodyLarge = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Normal, + fontSize = 16.sp, + lineHeight = 24.sp, + letterSpacing = 0.5.sp + ) + /* Other default text styles to override + titleLarge = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Normal, + fontSize = 22.sp, + lineHeight = 28.sp, + letterSpacing = 0.sp + ), + labelSmall = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Medium, + fontSize = 11.sp, + lineHeight = 16.sp, + letterSpacing = 0.5.sp + ) + */ +) \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/list-detail-compose/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/samples/user-interface/canonical-layouts/list-detail-compose/app/src/main/res/drawable-v24/ic_launcher_foreground.xml new file mode 100644 index 00000000..c3ba16e5 --- /dev/null +++ b/samples/user-interface/canonical-layouts/list-detail-compose/app/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/list-detail-compose/app/src/main/res/drawable/ic_food.xml b/samples/user-interface/canonical-layouts/list-detail-compose/app/src/main/res/drawable/ic_food.xml new file mode 100644 index 00000000..68632b46 --- /dev/null +++ b/samples/user-interface/canonical-layouts/list-detail-compose/app/src/main/res/drawable/ic_food.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/samples/user-interface/canonical-layouts/list-detail-compose/app/src/main/res/drawable/ic_launcher_background.xml b/samples/user-interface/canonical-layouts/list-detail-compose/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 00000000..8600a55b --- /dev/null +++ b/samples/user-interface/canonical-layouts/list-detail-compose/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,185 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/user-interface/canonical-layouts/list-detail-compose/app/src/main/res/drawable/ic_no_food.xml b/samples/user-interface/canonical-layouts/list-detail-compose/app/src/main/res/drawable/ic_no_food.xml new file mode 100644 index 00000000..54358412 --- /dev/null +++ b/samples/user-interface/canonical-layouts/list-detail-compose/app/src/main/res/drawable/ic_no_food.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/samples/user-interface/canonical-layouts/list-detail-compose/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/samples/user-interface/canonical-layouts/list-detail-compose/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 00000000..eca70cfe --- /dev/null +++ b/samples/user-interface/canonical-layouts/list-detail-compose/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/list-detail-compose/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/samples/user-interface/canonical-layouts/list-detail-compose/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 00000000..eca70cfe --- /dev/null +++ b/samples/user-interface/canonical-layouts/list-detail-compose/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/list-detail-compose/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/samples/user-interface/canonical-layouts/list-detail-compose/app/src/main/res/mipmap-hdpi/ic_launcher.webp new file mode 100644 index 00000000..c209e78e Binary files /dev/null and b/samples/user-interface/canonical-layouts/list-detail-compose/app/src/main/res/mipmap-hdpi/ic_launcher.webp differ diff --git a/samples/user-interface/canonical-layouts/list-detail-compose/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/samples/user-interface/canonical-layouts/list-detail-compose/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp new file mode 100644 index 00000000..b2dfe3d1 Binary files /dev/null and b/samples/user-interface/canonical-layouts/list-detail-compose/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp differ diff --git a/samples/user-interface/canonical-layouts/list-detail-compose/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/samples/user-interface/canonical-layouts/list-detail-compose/app/src/main/res/mipmap-mdpi/ic_launcher.webp new file mode 100644 index 00000000..4f0f1d64 Binary files /dev/null and b/samples/user-interface/canonical-layouts/list-detail-compose/app/src/main/res/mipmap-mdpi/ic_launcher.webp differ diff --git a/samples/user-interface/canonical-layouts/list-detail-compose/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/samples/user-interface/canonical-layouts/list-detail-compose/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp new file mode 100644 index 00000000..62b611da Binary files /dev/null and b/samples/user-interface/canonical-layouts/list-detail-compose/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp differ diff --git a/samples/user-interface/canonical-layouts/list-detail-compose/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/samples/user-interface/canonical-layouts/list-detail-compose/app/src/main/res/mipmap-xhdpi/ic_launcher.webp new file mode 100644 index 00000000..948a3070 Binary files /dev/null and b/samples/user-interface/canonical-layouts/list-detail-compose/app/src/main/res/mipmap-xhdpi/ic_launcher.webp differ diff --git a/samples/user-interface/canonical-layouts/list-detail-compose/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/samples/user-interface/canonical-layouts/list-detail-compose/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp new file mode 100644 index 00000000..1b9a6956 Binary files /dev/null and b/samples/user-interface/canonical-layouts/list-detail-compose/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp differ diff --git a/samples/user-interface/canonical-layouts/list-detail-compose/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/samples/user-interface/canonical-layouts/list-detail-compose/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp new file mode 100644 index 00000000..28d4b77f Binary files /dev/null and b/samples/user-interface/canonical-layouts/list-detail-compose/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ diff --git a/samples/user-interface/canonical-layouts/list-detail-compose/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/samples/user-interface/canonical-layouts/list-detail-compose/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp new file mode 100644 index 00000000..9287f508 Binary files /dev/null and b/samples/user-interface/canonical-layouts/list-detail-compose/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp differ diff --git a/samples/user-interface/canonical-layouts/list-detail-compose/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/samples/user-interface/canonical-layouts/list-detail-compose/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp new file mode 100644 index 00000000..aa7d6427 Binary files /dev/null and b/samples/user-interface/canonical-layouts/list-detail-compose/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ diff --git a/samples/user-interface/canonical-layouts/list-detail-compose/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/samples/user-interface/canonical-layouts/list-detail-compose/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp new file mode 100644 index 00000000..9126ae37 Binary files /dev/null and b/samples/user-interface/canonical-layouts/list-detail-compose/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp differ diff --git a/samples/user-interface/canonical-layouts/list-detail-compose/app/src/main/res/values/colors.xml b/samples/user-interface/canonical-layouts/list-detail-compose/app/src/main/res/values/colors.xml new file mode 100644 index 00000000..69007c22 --- /dev/null +++ b/samples/user-interface/canonical-layouts/list-detail-compose/app/src/main/res/values/colors.xml @@ -0,0 +1,25 @@ + + + + #FFBB86FC + #FF6200EE + #FF3700B3 + #FF03DAC5 + #FF018786 + #FF000000 + #FFFFFFFF + \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/list-detail-compose/app/src/main/res/values/strings.xml b/samples/user-interface/canonical-layouts/list-detail-compose/app/src/main/res/values/strings.xml new file mode 100644 index 00000000..3199b4e5 --- /dev/null +++ b/samples/user-interface/canonical-layouts/list-detail-compose/app/src/main/res/values/strings.xml @@ -0,0 +1,20 @@ + + + + ListDetailCompose + Tap a word to display its definition + \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/list-detail-compose/app/src/main/res/values/themes.xml b/samples/user-interface/canonical-layouts/list-detail-compose/app/src/main/res/values/themes.xml new file mode 100644 index 00000000..cfd89e61 --- /dev/null +++ b/samples/user-interface/canonical-layouts/list-detail-compose/app/src/main/res/values/themes.xml @@ -0,0 +1,20 @@ + + + + + + \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/list-detail-sliding-pane/app/src/main/res/values/colors.xml b/samples/user-interface/canonical-layouts/list-detail-sliding-pane/app/src/main/res/values/colors.xml new file mode 100644 index 00000000..69007c22 --- /dev/null +++ b/samples/user-interface/canonical-layouts/list-detail-sliding-pane/app/src/main/res/values/colors.xml @@ -0,0 +1,25 @@ + + + + #FFBB86FC + #FF6200EE + #FF3700B3 + #FF03DAC5 + #FF018786 + #FF000000 + #FFFFFFFF + \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/list-detail-sliding-pane/app/src/main/res/values/strings.xml b/samples/user-interface/canonical-layouts/list-detail-sliding-pane/app/src/main/res/values/strings.xml new file mode 100644 index 00000000..f7ceee06 --- /dev/null +++ b/samples/user-interface/canonical-layouts/list-detail-sliding-pane/app/src/main/res/values/strings.xml @@ -0,0 +1,19 @@ + + + + list-detail-sliding-pane + \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/list-detail-sliding-pane/app/src/main/res/values/themes.xml b/samples/user-interface/canonical-layouts/list-detail-sliding-pane/app/src/main/res/values/themes.xml new file mode 100644 index 00000000..ca9155d7 --- /dev/null +++ b/samples/user-interface/canonical-layouts/list-detail-sliding-pane/app/src/main/res/values/themes.xml @@ -0,0 +1,32 @@ + + + + + + \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/list-detail-sliding-pane/app/src/main/res/xml/backup_rules.xml b/samples/user-interface/canonical-layouts/list-detail-sliding-pane/app/src/main/res/xml/backup_rules.xml new file mode 100644 index 00000000..18ec4459 --- /dev/null +++ b/samples/user-interface/canonical-layouts/list-detail-sliding-pane/app/src/main/res/xml/backup_rules.xml @@ -0,0 +1,29 @@ + + + + + + \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/list-detail-sliding-pane/app/src/main/res/xml/data_extraction_rules.xml b/samples/user-interface/canonical-layouts/list-detail-sliding-pane/app/src/main/res/xml/data_extraction_rules.xml new file mode 100644 index 00000000..ea522dc6 --- /dev/null +++ b/samples/user-interface/canonical-layouts/list-detail-sliding-pane/app/src/main/res/xml/data_extraction_rules.xml @@ -0,0 +1,35 @@ + + + + + + + + + \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/list-detail-sliding-pane/build.gradle b/samples/user-interface/canonical-layouts/list-detail-sliding-pane/build.gradle new file mode 100644 index 00000000..e7b77424 --- /dev/null +++ b/samples/user-interface/canonical-layouts/list-detail-sliding-pane/build.gradle @@ -0,0 +1,21 @@ +/* + * 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 + * + * 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. + */ +// Top-level build file where you can add configuration options common to all sub-projects/modules. +plugins { + id 'com.android.application' version '8.6.0' apply false + id 'com.android.library' version '8.6.0' apply false + id 'org.jetbrains.kotlin.android' version '2.0.0' apply false +} \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/list-detail-sliding-pane/gradle.properties b/samples/user-interface/canonical-layouts/list-detail-sliding-pane/gradle.properties new file mode 100644 index 00000000..f19c7b9b --- /dev/null +++ b/samples/user-interface/canonical-layouts/list-detail-sliding-pane/gradle.properties @@ -0,0 +1,24 @@ +# 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 +android.nonFinalResIds=false \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/list-detail-sliding-pane/gradle/wrapper/gradle-wrapper.jar b/samples/user-interface/canonical-layouts/list-detail-sliding-pane/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 00000000..e708b1c0 Binary files /dev/null and b/samples/user-interface/canonical-layouts/list-detail-sliding-pane/gradle/wrapper/gradle-wrapper.jar differ diff --git a/samples/user-interface/canonical-layouts/list-detail-sliding-pane/gradle/wrapper/gradle-wrapper.properties b/samples/user-interface/canonical-layouts/list-detail-sliding-pane/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..45dc8686 --- /dev/null +++ b/samples/user-interface/canonical-layouts/list-detail-sliding-pane/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Wed May 25 15:14:50 BST 2022 +distributionBase=GRADLE_USER_HOME +distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionPath=wrapper/dists +zipStorePath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME diff --git a/samples/user-interface/canonical-layouts/list-detail-sliding-pane/gradlew b/samples/user-interface/canonical-layouts/list-detail-sliding-pane/gradlew new file mode 100755 index 00000000..4f906e0c --- /dev/null +++ b/samples/user-interface/canonical-layouts/list-detail-sliding-pane/gradlew @@ -0,0 +1,185 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or 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 UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# 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"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# 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 + ;; + 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" + which java >/dev/null 2>&1 || 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 + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/samples/user-interface/canonical-layouts/list-detail-sliding-pane/gradlew.bat b/samples/user-interface/canonical-layouts/list-detail-sliding-pane/gradlew.bat new file mode 100644 index 00000000..107acd32 --- /dev/null +++ b/samples/user-interface/canonical-layouts/list-detail-sliding-pane/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/samples/user-interface/canonical-layouts/list-detail-sliding-pane/settings.gradle b/samples/user-interface/canonical-layouts/list-detail-sliding-pane/settings.gradle new file mode 100644 index 00000000..ddce6430 --- /dev/null +++ b/samples/user-interface/canonical-layouts/list-detail-sliding-pane/settings.gradle @@ -0,0 +1,31 @@ +/* + * 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 + * + * 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. + */ +pluginManagement { + repositories { + gradlePluginPortal() + google() + mavenCentral() + } +} +dependencyResolutionManagement { + repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) + repositories { + google() + mavenCentral() + } +} +rootProject.name = "list-detail-sliding-pane" +include ':app' diff --git a/samples/user-interface/canonical-layouts/supporting-pane-compose/.gitignore b/samples/user-interface/canonical-layouts/supporting-pane-compose/.gitignore new file mode 100644 index 00000000..aa724b77 --- /dev/null +++ b/samples/user-interface/canonical-layouts/supporting-pane-compose/.gitignore @@ -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 diff --git a/samples/user-interface/canonical-layouts/supporting-pane-compose/.idea/.gitignore b/samples/user-interface/canonical-layouts/supporting-pane-compose/.idea/.gitignore new file mode 100644 index 00000000..26d33521 --- /dev/null +++ b/samples/user-interface/canonical-layouts/supporting-pane-compose/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/samples/user-interface/canonical-layouts/supporting-pane-compose/.idea/.name b/samples/user-interface/canonical-layouts/supporting-pane-compose/.idea/.name new file mode 100644 index 00000000..96fc4e2c --- /dev/null +++ b/samples/user-interface/canonical-layouts/supporting-pane-compose/.idea/.name @@ -0,0 +1 @@ +SupportingPaneCompose \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/supporting-pane-compose/.idea/appInsightsSettings.xml b/samples/user-interface/canonical-layouts/supporting-pane-compose/.idea/appInsightsSettings.xml new file mode 100644 index 00000000..371f2e29 --- /dev/null +++ b/samples/user-interface/canonical-layouts/supporting-pane-compose/.idea/appInsightsSettings.xml @@ -0,0 +1,26 @@ + + + + + + \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/supporting-pane-compose/.idea/compiler.xml b/samples/user-interface/canonical-layouts/supporting-pane-compose/.idea/compiler.xml new file mode 100644 index 00000000..b589d56e --- /dev/null +++ b/samples/user-interface/canonical-layouts/supporting-pane-compose/.idea/compiler.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/supporting-pane-compose/.idea/deploymentTargetDropDown.xml b/samples/user-interface/canonical-layouts/supporting-pane-compose/.idea/deploymentTargetDropDown.xml new file mode 100644 index 00000000..0c0c3383 --- /dev/null +++ b/samples/user-interface/canonical-layouts/supporting-pane-compose/.idea/deploymentTargetDropDown.xml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/supporting-pane-compose/.idea/deploymentTargetSelector.xml b/samples/user-interface/canonical-layouts/supporting-pane-compose/.idea/deploymentTargetSelector.xml new file mode 100644 index 00000000..b268ef36 --- /dev/null +++ b/samples/user-interface/canonical-layouts/supporting-pane-compose/.idea/deploymentTargetSelector.xml @@ -0,0 +1,10 @@ + + + + + + + + + \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/supporting-pane-compose/.idea/inspectionProfiles/Project_Default.xml b/samples/user-interface/canonical-layouts/supporting-pane-compose/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 00000000..1f57b99e --- /dev/null +++ b/samples/user-interface/canonical-layouts/supporting-pane-compose/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,56 @@ + + + + \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/supporting-pane-compose/.idea/kotlinc.xml b/samples/user-interface/canonical-layouts/supporting-pane-compose/.idea/kotlinc.xml new file mode 100644 index 00000000..6d0ee1c2 --- /dev/null +++ b/samples/user-interface/canonical-layouts/supporting-pane-compose/.idea/kotlinc.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/supporting-pane-compose/.idea/migrations.xml b/samples/user-interface/canonical-layouts/supporting-pane-compose/.idea/migrations.xml new file mode 100644 index 00000000..f8051a6f --- /dev/null +++ b/samples/user-interface/canonical-layouts/supporting-pane-compose/.idea/migrations.xml @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/supporting-pane-compose/.idea/misc.xml b/samples/user-interface/canonical-layouts/supporting-pane-compose/.idea/misc.xml new file mode 100644 index 00000000..8978d23d --- /dev/null +++ b/samples/user-interface/canonical-layouts/supporting-pane-compose/.idea/misc.xml @@ -0,0 +1,9 @@ + + + + + + + + \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/supporting-pane-compose/.idea/studiobot.xml b/samples/user-interface/canonical-layouts/supporting-pane-compose/.idea/studiobot.xml new file mode 100644 index 00000000..539e3b80 --- /dev/null +++ b/samples/user-interface/canonical-layouts/supporting-pane-compose/.idea/studiobot.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/supporting-pane-compose/.idea/vcs.xml b/samples/user-interface/canonical-layouts/supporting-pane-compose/.idea/vcs.xml new file mode 100644 index 00000000..b2bdec2d --- /dev/null +++ b/samples/user-interface/canonical-layouts/supporting-pane-compose/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/supporting-pane-compose/app/.gitignore b/samples/user-interface/canonical-layouts/supporting-pane-compose/app/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/samples/user-interface/canonical-layouts/supporting-pane-compose/app/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/supporting-pane-compose/app/build.gradle b/samples/user-interface/canonical-layouts/supporting-pane-compose/app/build.gradle new file mode 100644 index 00000000..743b34ec --- /dev/null +++ b/samples/user-interface/canonical-layouts/supporting-pane-compose/app/build.gradle @@ -0,0 +1,86 @@ +/* + * 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 + * + * 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. + */ + +plugins { + id 'com.android.application' + id 'org.jetbrains.kotlin.android' + id("org.jetbrains.kotlin.plugin.compose") version "2.0.0" + +} + +android { + namespace 'com.example.supportingpanecompose' + compileSdk 35 + + defaultConfig { + applicationId "com.example.supportingpanecompose" + minSdk 21 + targetSdk 35 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + vectorDrawables { + useSupportLibrary true + } + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + kotlinOptions { + jvmTarget = '1.8' + } + buildFeatures { + compose true + } + packagingOptions { + resources { + excludes += '/META-INF/{AL2.0,LGPL2.1}' + } + } +} + +composeCompiler { + enableStrongSkippingMode = true + + reportsDestination = layout.buildDirectory.dir("compose_compiler") +} + +dependencies { + def composeBom = platform('androidx.compose:compose-bom:2024.09.00') + implementation(composeBom) + + implementation 'androidx.core:core-ktx:1.13.1' + implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.8.5' + implementation 'androidx.activity:activity-compose:1.9.2' + implementation "androidx.compose.ui:ui" + implementation "androidx.compose.ui:ui-tooling-preview" + implementation "androidx.window:window:1.3.0" + implementation 'androidx.compose.material3:material3:1.3.0' + implementation 'androidx.compose.material3.adaptive:adaptive:1.1.0-alpha02' + implementation 'androidx.compose.material3.adaptive:adaptive-layout:1.1.0-alpha02' + implementation 'androidx.compose.material3.adaptive:adaptive-navigation:1.1.0-alpha02' + implementation "androidx.compose.material3:material3-window-size-class:1.3.0" + testImplementation 'junit:junit:4.13.2' +} \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/supporting-pane-compose/app/proguard-rules.pro b/samples/user-interface/canonical-layouts/supporting-pane-compose/app/proguard-rules.pro new file mode 100644 index 00000000..481bb434 --- /dev/null +++ b/samples/user-interface/canonical-layouts/supporting-pane-compose/app/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/supporting-pane-compose/app/src/main/AndroidManifest.xml b/samples/user-interface/canonical-layouts/supporting-pane-compose/app/src/main/AndroidManifest.xml new file mode 100644 index 00000000..2e847dbb --- /dev/null +++ b/samples/user-interface/canonical-layouts/supporting-pane-compose/app/src/main/AndroidManifest.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/supporting-pane-compose/app/src/main/java/com/example/supportingpanecompose/MainActivity.kt b/samples/user-interface/canonical-layouts/supporting-pane-compose/app/src/main/java/com/example/supportingpanecompose/MainActivity.kt new file mode 100644 index 00000000..8073585f --- /dev/null +++ b/samples/user-interface/canonical-layouts/supporting-pane-compose/app/src/main/java/com/example/supportingpanecompose/MainActivity.kt @@ -0,0 +1,36 @@ +/* + * Copyright 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 + * + * 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.supportingpanecompose + +import android.os.Bundle +import androidx.activity.ComponentActivity +import androidx.activity.compose.setContent +import androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi +import com.example.supportingpanecompose.ui.SupportingPaneSample +import com.example.supportingpanecompose.ui.theme.SupportingPaneCompose + +@OptIn(ExperimentalMaterial3AdaptiveApi::class) +class MainActivity : ComponentActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContent { + SupportingPaneCompose { + SupportingPaneSample() + } + } + } +} diff --git a/samples/user-interface/canonical-layouts/supporting-pane-compose/app/src/main/java/com/example/supportingpanecompose/ui/SupportingPaneSample.kt b/samples/user-interface/canonical-layouts/supporting-pane-compose/app/src/main/java/com/example/supportingpanecompose/ui/SupportingPaneSample.kt new file mode 100644 index 00000000..18fa78ce --- /dev/null +++ b/samples/user-interface/canonical-layouts/supporting-pane-compose/app/src/main/java/com/example/supportingpanecompose/ui/SupportingPaneSample.kt @@ -0,0 +1,139 @@ +/* + * 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. + */ + +package com.example.supportingpanecompose.ui + +import androidx.activity.compose.BackHandler +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi +import androidx.compose.material3.adaptive.layout.AnimatedPane +import androidx.compose.material3.adaptive.layout.PaneExpansionDragHandle +import androidx.compose.material3.adaptive.layout.SupportingPaneScaffold +import androidx.compose.material3.adaptive.layout.SupportingPaneScaffoldRole +import androidx.compose.material3.adaptive.layout.rememberPaneExpansionState +import androidx.compose.material3.adaptive.navigation.rememberSupportingPaneScaffoldNavigator +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import com.example.supportingpanecompose.R + +// Create some simple sample data +private val data = mapOf( + "android" to listOf("kotlin", "java", "flutter"), + "kotlin" to listOf("backend", "android", "desktop"), + "desktop" to listOf("kotlin", "java", "flutter"), + "backend" to listOf("kotlin", "java"), + "java" to listOf("backend", "android", "desktop"), + "flutter" to listOf("android", "desktop") +) + +@ExperimentalMaterial3AdaptiveApi +@Composable +fun SupportingPaneSample() { + var selectedTopic: String by rememberSaveable { mutableStateOf(data.keys.first()) } + val navigator = rememberSupportingPaneScaffoldNavigator() + + BackHandler(enabled = navigator.canNavigateBack()) { + navigator.navigateBack() + } + + SupportingPaneScaffold( + directive = navigator.scaffoldDirective, + value = navigator.scaffoldValue, + supportingPane = { + AnimatedPane( + modifier = Modifier.padding(all = 16.dp) + ) { + Column { + Text( + stringResource(R.string.related_content_label), + modifier = Modifier.padding(vertical = 16.dp), + style = MaterialTheme.typography.titleLarge + ) + + LazyColumn { + items( + data.getValue(selectedTopic), + key = { it } + ) { relatedTopic -> + Box( + Modifier + .fillMaxWidth() + .padding(all = 4.dp) + .clickable { + selectedTopic = relatedTopic + if (navigator.canNavigateBack()) { + navigator.navigateBack() + } + } + ) { + Text( + text = relatedTopic, + modifier = Modifier + ) + } + } + } + } + } + }, mainPane = { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + modifier = Modifier.fillMaxSize() + ) { + Text( + stringResource(R.string.main_content_label), + style = MaterialTheme.typography.titleLarge + ) + + Box( + Modifier + .fillMaxWidth() + .padding(all = 8.dp) + .clickable { + navigator.navigateTo(SupportingPaneScaffoldRole.Supporting) + }, + contentAlignment = Alignment.Center + ) { + Text( + text = selectedTopic, + modifier = Modifier + .padding(16.dp) + ) + } + } + }, + paneExpansionState = rememberPaneExpansionState(navigator.scaffoldValue), + paneExpansionDragHandle = { state -> + PaneExpansionDragHandle(state, Color.Red) + }) +} diff --git a/samples/user-interface/canonical-layouts/supporting-pane-compose/app/src/main/java/com/example/supportingpanecompose/ui/theme/Color.kt b/samples/user-interface/canonical-layouts/supporting-pane-compose/app/src/main/java/com/example/supportingpanecompose/ui/theme/Color.kt new file mode 100644 index 00000000..d4ad990e --- /dev/null +++ b/samples/user-interface/canonical-layouts/supporting-pane-compose/app/src/main/java/com/example/supportingpanecompose/ui/theme/Color.kt @@ -0,0 +1,27 @@ +/* + * 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 + * + * 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.supportingpanecompose.ui.theme + +import androidx.compose.ui.graphics.Color + +val Purple80 = Color(0xFFD0BCFF) +val PurpleGrey80 = Color(0xFFCCC2DC) +val Pink80 = Color(0xFFEFB8C8) + +val Purple40 = Color(0xFF6650a4) +val PurpleGrey40 = Color(0xFF625b71) +val Pink40 = Color(0xFF7D5260) \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/supporting-pane-compose/app/src/main/java/com/example/supportingpanecompose/ui/theme/Theme.kt b/samples/user-interface/canonical-layouts/supporting-pane-compose/app/src/main/java/com/example/supportingpanecompose/ui/theme/Theme.kt new file mode 100644 index 00000000..3877ac05 --- /dev/null +++ b/samples/user-interface/canonical-layouts/supporting-pane-compose/app/src/main/java/com/example/supportingpanecompose/ui/theme/Theme.kt @@ -0,0 +1,87 @@ +/* + * 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 + * + * 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.supportingpanecompose.ui.theme + +import android.app.Activity +import android.os.Build +import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.darkColorScheme +import androidx.compose.material3.dynamicDarkColorScheme +import androidx.compose.material3.dynamicLightColorScheme +import androidx.compose.material3.lightColorScheme +import androidx.compose.runtime.Composable +import androidx.compose.runtime.SideEffect +import androidx.compose.ui.graphics.toArgb +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalView +import androidx.core.view.WindowCompat + +private val DarkColorScheme = darkColorScheme( + primary = Purple80, + secondary = PurpleGrey80, + tertiary = Pink80 +) + +private val LightColorScheme = lightColorScheme( + primary = Purple40, + secondary = PurpleGrey40, + tertiary = Pink40 + + /* Other default colors to override + background = Color(0xFFFFFBFE), + surface = Color(0xFFFFFBFE), + onPrimary = Color.White, + onSecondary = Color.White, + onTertiary = Color.White, + onBackground = Color(0xFF1C1B1F), + onSurface = Color(0xFF1C1B1F), + */ +) + +@Composable +fun SupportingPaneCompose( + darkTheme: Boolean = isSystemInDarkTheme(), + // Dynamic color is available on Android 12+ + dynamicColor: Boolean = true, + content: @Composable () -> Unit +) { + val colorScheme = when { + dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> { + val context = LocalContext.current + if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) + } + darkTheme -> DarkColorScheme + else -> LightColorScheme + } + val view = LocalView.current + if (!view.isInEditMode) { + SideEffect { + (view.context as Activity).window.statusBarColor = colorScheme.primary.toArgb() + WindowCompat.getInsetsController( + (view.context as Activity).window, + view + ).isAppearanceLightStatusBars = darkTheme + } + } + + MaterialTheme( + colorScheme = colorScheme, + typography = Typography, + content = content + ) +} \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/supporting-pane-compose/app/src/main/java/com/example/supportingpanecompose/ui/theme/Type.kt b/samples/user-interface/canonical-layouts/supporting-pane-compose/app/src/main/java/com/example/supportingpanecompose/ui/theme/Type.kt new file mode 100644 index 00000000..7ab6059d --- /dev/null +++ b/samples/user-interface/canonical-layouts/supporting-pane-compose/app/src/main/java/com/example/supportingpanecompose/ui/theme/Type.kt @@ -0,0 +1,50 @@ +/* + * 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 + * + * 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.supportingpanecompose.ui.theme + +import androidx.compose.material3.Typography +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.sp + +// Set of Material typography styles to start with +val Typography = Typography( + bodyLarge = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Normal, + fontSize = 16.sp, + lineHeight = 24.sp, + letterSpacing = 0.5.sp + ) + /* Other default text styles to override + titleLarge = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Normal, + fontSize = 22.sp, + lineHeight = 28.sp, + letterSpacing = 0.sp + ), + labelSmall = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Medium, + fontSize = 11.sp, + lineHeight = 16.sp, + letterSpacing = 0.5.sp + ) + */ +) \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/supporting-pane-compose/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/samples/user-interface/canonical-layouts/supporting-pane-compose/app/src/main/res/drawable-v24/ic_launcher_foreground.xml new file mode 100644 index 00000000..2b068d11 --- /dev/null +++ b/samples/user-interface/canonical-layouts/supporting-pane-compose/app/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/supporting-pane-compose/app/src/main/res/drawable/ic_launcher_background.xml b/samples/user-interface/canonical-layouts/supporting-pane-compose/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 00000000..07d5da9c --- /dev/null +++ b/samples/user-interface/canonical-layouts/supporting-pane-compose/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/user-interface/canonical-layouts/supporting-pane-compose/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/samples/user-interface/canonical-layouts/supporting-pane-compose/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 00000000..eca70cfe --- /dev/null +++ b/samples/user-interface/canonical-layouts/supporting-pane-compose/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/supporting-pane-compose/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/samples/user-interface/canonical-layouts/supporting-pane-compose/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 00000000..eca70cfe --- /dev/null +++ b/samples/user-interface/canonical-layouts/supporting-pane-compose/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/supporting-pane-compose/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/samples/user-interface/canonical-layouts/supporting-pane-compose/app/src/main/res/mipmap-hdpi/ic_launcher.webp new file mode 100644 index 00000000..c209e78e Binary files /dev/null and b/samples/user-interface/canonical-layouts/supporting-pane-compose/app/src/main/res/mipmap-hdpi/ic_launcher.webp differ diff --git a/samples/user-interface/canonical-layouts/supporting-pane-compose/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/samples/user-interface/canonical-layouts/supporting-pane-compose/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp new file mode 100644 index 00000000..b2dfe3d1 Binary files /dev/null and b/samples/user-interface/canonical-layouts/supporting-pane-compose/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp differ diff --git a/samples/user-interface/canonical-layouts/supporting-pane-compose/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/samples/user-interface/canonical-layouts/supporting-pane-compose/app/src/main/res/mipmap-mdpi/ic_launcher.webp new file mode 100644 index 00000000..4f0f1d64 Binary files /dev/null and b/samples/user-interface/canonical-layouts/supporting-pane-compose/app/src/main/res/mipmap-mdpi/ic_launcher.webp differ diff --git a/samples/user-interface/canonical-layouts/supporting-pane-compose/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/samples/user-interface/canonical-layouts/supporting-pane-compose/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp new file mode 100644 index 00000000..62b611da Binary files /dev/null and b/samples/user-interface/canonical-layouts/supporting-pane-compose/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp differ diff --git a/samples/user-interface/canonical-layouts/supporting-pane-compose/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/samples/user-interface/canonical-layouts/supporting-pane-compose/app/src/main/res/mipmap-xhdpi/ic_launcher.webp new file mode 100644 index 00000000..948a3070 Binary files /dev/null and b/samples/user-interface/canonical-layouts/supporting-pane-compose/app/src/main/res/mipmap-xhdpi/ic_launcher.webp differ diff --git a/samples/user-interface/canonical-layouts/supporting-pane-compose/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/samples/user-interface/canonical-layouts/supporting-pane-compose/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp new file mode 100644 index 00000000..1b9a6956 Binary files /dev/null and b/samples/user-interface/canonical-layouts/supporting-pane-compose/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp differ diff --git a/samples/user-interface/canonical-layouts/supporting-pane-compose/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/samples/user-interface/canonical-layouts/supporting-pane-compose/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp new file mode 100644 index 00000000..28d4b77f Binary files /dev/null and b/samples/user-interface/canonical-layouts/supporting-pane-compose/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ diff --git a/samples/user-interface/canonical-layouts/supporting-pane-compose/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/samples/user-interface/canonical-layouts/supporting-pane-compose/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp new file mode 100644 index 00000000..9287f508 Binary files /dev/null and b/samples/user-interface/canonical-layouts/supporting-pane-compose/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp differ diff --git a/samples/user-interface/canonical-layouts/supporting-pane-compose/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/samples/user-interface/canonical-layouts/supporting-pane-compose/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp new file mode 100644 index 00000000..aa7d6427 Binary files /dev/null and b/samples/user-interface/canonical-layouts/supporting-pane-compose/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ diff --git a/samples/user-interface/canonical-layouts/supporting-pane-compose/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/samples/user-interface/canonical-layouts/supporting-pane-compose/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp new file mode 100644 index 00000000..9126ae37 Binary files /dev/null and b/samples/user-interface/canonical-layouts/supporting-pane-compose/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp differ diff --git a/samples/user-interface/canonical-layouts/supporting-pane-compose/app/src/main/res/values/colors.xml b/samples/user-interface/canonical-layouts/supporting-pane-compose/app/src/main/res/values/colors.xml new file mode 100644 index 00000000..f8c6127d --- /dev/null +++ b/samples/user-interface/canonical-layouts/supporting-pane-compose/app/src/main/res/values/colors.xml @@ -0,0 +1,10 @@ + + + #FFBB86FC + #FF6200EE + #FF3700B3 + #FF03DAC5 + #FF018786 + #FF000000 + #FFFFFFFF + \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/supporting-pane-compose/app/src/main/res/values/strings.xml b/samples/user-interface/canonical-layouts/supporting-pane-compose/app/src/main/res/values/strings.xml new file mode 100644 index 00000000..0fba7ea3 --- /dev/null +++ b/samples/user-interface/canonical-layouts/supporting-pane-compose/app/src/main/res/values/strings.xml @@ -0,0 +1,5 @@ + + SupportingPaneCompose + Related Content + Main Content + \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/supporting-pane-compose/app/src/main/res/values/themes.xml b/samples/user-interface/canonical-layouts/supporting-pane-compose/app/src/main/res/values/themes.xml new file mode 100644 index 00000000..4373a022 --- /dev/null +++ b/samples/user-interface/canonical-layouts/supporting-pane-compose/app/src/main/res/values/themes.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/supporting-pane-fragments/app/src/main/res/values/colors.xml b/samples/user-interface/canonical-layouts/supporting-pane-fragments/app/src/main/res/values/colors.xml new file mode 100644 index 00000000..19d01e1e --- /dev/null +++ b/samples/user-interface/canonical-layouts/supporting-pane-fragments/app/src/main/res/values/colors.xml @@ -0,0 +1,27 @@ + + + + #FFBB86FC + #FF6200EE + #FF3700B3 + #FF03DAC5 + #FF018786 + #FF000000 + #FFFFFFFF + + #33018786 + \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/supporting-pane-fragments/app/src/main/res/values/strings.xml b/samples/user-interface/canonical-layouts/supporting-pane-fragments/app/src/main/res/values/strings.xml new file mode 100644 index 00000000..ff6207dc --- /dev/null +++ b/samples/user-interface/canonical-layouts/supporting-pane-fragments/app/src/main/res/values/strings.xml @@ -0,0 +1,21 @@ + + + + Supporting Pane Fragments + Main content + Related content + \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/supporting-pane-fragments/app/src/main/res/values/themes.xml b/samples/user-interface/canonical-layouts/supporting-pane-fragments/app/src/main/res/values/themes.xml new file mode 100644 index 00000000..6d8fd08f --- /dev/null +++ b/samples/user-interface/canonical-layouts/supporting-pane-fragments/app/src/main/res/values/themes.xml @@ -0,0 +1,32 @@ + + + + + + \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/supporting-pane-fragments/app/src/main/res/xml/backup_rules.xml b/samples/user-interface/canonical-layouts/supporting-pane-fragments/app/src/main/res/xml/backup_rules.xml new file mode 100644 index 00000000..18ec4459 --- /dev/null +++ b/samples/user-interface/canonical-layouts/supporting-pane-fragments/app/src/main/res/xml/backup_rules.xml @@ -0,0 +1,29 @@ + + + + + + \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/supporting-pane-fragments/app/src/main/res/xml/data_extraction_rules.xml b/samples/user-interface/canonical-layouts/supporting-pane-fragments/app/src/main/res/xml/data_extraction_rules.xml new file mode 100644 index 00000000..4b612207 --- /dev/null +++ b/samples/user-interface/canonical-layouts/supporting-pane-fragments/app/src/main/res/xml/data_extraction_rules.xml @@ -0,0 +1,35 @@ + + + + + + + + + \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/supporting-pane-fragments/build.gradle b/samples/user-interface/canonical-layouts/supporting-pane-fragments/build.gradle new file mode 100644 index 00000000..e7b77424 --- /dev/null +++ b/samples/user-interface/canonical-layouts/supporting-pane-fragments/build.gradle @@ -0,0 +1,21 @@ +/* + * 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 + * + * 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. + */ +// Top-level build file where you can add configuration options common to all sub-projects/modules. +plugins { + id 'com.android.application' version '8.6.0' apply false + id 'com.android.library' version '8.6.0' apply false + id 'org.jetbrains.kotlin.android' version '2.0.0' apply false +} \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/supporting-pane-fragments/gradle.properties b/samples/user-interface/canonical-layouts/supporting-pane-fragments/gradle.properties new file mode 100644 index 00000000..f19c7b9b --- /dev/null +++ b/samples/user-interface/canonical-layouts/supporting-pane-fragments/gradle.properties @@ -0,0 +1,24 @@ +# 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 +android.nonFinalResIds=false \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/supporting-pane-fragments/gradle/wrapper/gradle-wrapper.jar b/samples/user-interface/canonical-layouts/supporting-pane-fragments/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 00000000..e708b1c0 Binary files /dev/null and b/samples/user-interface/canonical-layouts/supporting-pane-fragments/gradle/wrapper/gradle-wrapper.jar differ diff --git a/samples/user-interface/canonical-layouts/supporting-pane-fragments/gradle/wrapper/gradle-wrapper.properties b/samples/user-interface/canonical-layouts/supporting-pane-fragments/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..1e687926 --- /dev/null +++ b/samples/user-interface/canonical-layouts/supporting-pane-fragments/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Wed Aug 17 15:01:41 CEST 2022 +distributionBase=GRADLE_USER_HOME +distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionPath=wrapper/dists +zipStorePath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME diff --git a/samples/user-interface/canonical-layouts/supporting-pane-fragments/gradlew b/samples/user-interface/canonical-layouts/supporting-pane-fragments/gradlew new file mode 100755 index 00000000..4f906e0c --- /dev/null +++ b/samples/user-interface/canonical-layouts/supporting-pane-fragments/gradlew @@ -0,0 +1,185 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or 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 UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# 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"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# 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 + ;; + 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" + which java >/dev/null 2>&1 || 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 + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/samples/user-interface/canonical-layouts/supporting-pane-fragments/gradlew.bat b/samples/user-interface/canonical-layouts/supporting-pane-fragments/gradlew.bat new file mode 100644 index 00000000..107acd32 --- /dev/null +++ b/samples/user-interface/canonical-layouts/supporting-pane-fragments/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/samples/user-interface/canonical-layouts/supporting-pane-fragments/settings.gradle b/samples/user-interface/canonical-layouts/supporting-pane-fragments/settings.gradle new file mode 100644 index 00000000..60faefce --- /dev/null +++ b/samples/user-interface/canonical-layouts/supporting-pane-fragments/settings.gradle @@ -0,0 +1,31 @@ +/* + * 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 + * + * 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. + */ +pluginManagement { + repositories { + google() + mavenCentral() + gradlePluginPortal() + } +} +dependencyResolutionManagement { + repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) + repositories { + google() + mavenCentral() + } +} +rootProject.name = "Supporting Pane Fragments" +include ':app' diff --git a/samples/user-interface/canonical-layouts/supporting-pane-views/.gitignore b/samples/user-interface/canonical-layouts/supporting-pane-views/.gitignore new file mode 100644 index 00000000..10cfdbfa --- /dev/null +++ b/samples/user-interface/canonical-layouts/supporting-pane-views/.gitignore @@ -0,0 +1,10 @@ +*.iml +.gradle +/local.properties +/.idea +.DS_Store +/build +/captures +.externalNativeBuild +.cxx +local.properties diff --git a/samples/user-interface/canonical-layouts/supporting-pane-views/app/.gitignore b/samples/user-interface/canonical-layouts/supporting-pane-views/app/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/samples/user-interface/canonical-layouts/supporting-pane-views/app/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/supporting-pane-views/app/build.gradle b/samples/user-interface/canonical-layouts/supporting-pane-views/app/build.gradle new file mode 100644 index 00000000..8ad26701 --- /dev/null +++ b/samples/user-interface/canonical-layouts/supporting-pane-views/app/build.gradle @@ -0,0 +1,66 @@ +/* + * 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 + * + * 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. + */ + +plugins { + id 'com.android.application' + id 'org.jetbrains.kotlin.android' +} + +android { + namespace 'com.google.supporting.pane.views' + compileSdk 35 + + defaultConfig { + applicationId "com.google.supporting.pane.views" + minSdk 21 + targetSdk 35 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + buildFeatures { + viewBinding true + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + kotlinOptions { + jvmTarget = '1.8' + } +} + +dependencies { + + implementation 'androidx.core:core-ktx:1.13.1' + implementation 'androidx.appcompat:appcompat:1.7.0' + implementation 'com.google.android.material:material:1.12.0' + implementation 'androidx.constraintlayout:constraintlayout:2.1.4' + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3' + implementation 'androidx.fragment:fragment-ktx:1.8.2' + testImplementation 'junit:junit:4.13.2' + androidTestImplementation 'androidx.test.ext:junit:1.2.1' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.6.1' +} \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/supporting-pane-views/app/proguard-rules.pro b/samples/user-interface/canonical-layouts/supporting-pane-views/app/proguard-rules.pro new file mode 100644 index 00000000..481bb434 --- /dev/null +++ b/samples/user-interface/canonical-layouts/supporting-pane-views/app/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/supporting-pane-views/app/src/main/AndroidManifest.xml b/samples/user-interface/canonical-layouts/supporting-pane-views/app/src/main/AndroidManifest.xml new file mode 100644 index 00000000..46103061 --- /dev/null +++ b/samples/user-interface/canonical-layouts/supporting-pane-views/app/src/main/AndroidManifest.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/supporting-pane-views/app/src/main/java/com/google/supporting/pane/views/ContentViewModel.kt b/samples/user-interface/canonical-layouts/supporting-pane-views/app/src/main/java/com/google/supporting/pane/views/ContentViewModel.kt new file mode 100644 index 00000000..9723f49e --- /dev/null +++ b/samples/user-interface/canonical-layouts/supporting-pane-views/app/src/main/java/com/google/supporting/pane/views/ContentViewModel.kt @@ -0,0 +1,30 @@ +/* + * 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 + * + * 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.google.supporting.pane.views + +import androidx.lifecycle.ViewModel +import kotlinx.coroutines.flow.MutableStateFlow + +class ContentViewModel : ViewModel() { + + val state = MutableStateFlow(State()) + + fun selectFromSupportingPane(key: String) { + val newState = State(key = key, items = data[key].orEmpty()) + state.value = newState + } +} \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/supporting-pane-views/app/src/main/java/com/google/supporting/pane/views/Data.kt b/samples/user-interface/canonical-layouts/supporting-pane-views/app/src/main/java/com/google/supporting/pane/views/Data.kt new file mode 100644 index 00000000..f46fc15c --- /dev/null +++ b/samples/user-interface/canonical-layouts/supporting-pane-views/app/src/main/java/com/google/supporting/pane/views/Data.kt @@ -0,0 +1,28 @@ +/* + * 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 + * + * 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.google.supporting.pane.views + +val data = mapOf( + "android" to listOf("kotlin", "java", "flutter"), + "kotlin" to listOf("backend", "android", "desktop"), + "desktop" to listOf("kotlin", "java", "flutter"), + "backend" to listOf("kotlin", "java"), + "java" to listOf("backend", "android", "desktop"), + "flutter" to listOf("android", "desktop") +) + +data class State(val key: String = data.keys.first(), val items: List = data.values.first()) diff --git a/samples/user-interface/canonical-layouts/supporting-pane-views/app/src/main/java/com/google/supporting/pane/views/MainActivity.kt b/samples/user-interface/canonical-layouts/supporting-pane-views/app/src/main/java/com/google/supporting/pane/views/MainActivity.kt new file mode 100644 index 00000000..411aa5e0 --- /dev/null +++ b/samples/user-interface/canonical-layouts/supporting-pane-views/app/src/main/java/com/google/supporting/pane/views/MainActivity.kt @@ -0,0 +1,52 @@ +/* + * 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 + * + * 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.google.supporting.pane.views + +import android.os.Bundle +import androidx.appcompat.app.AppCompatActivity +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.repeatOnLifecycle +import com.google.supporting.pane.views.databinding.ActivityMainBinding +import kotlinx.coroutines.launch + + +class MainActivity : AppCompatActivity() { + + private val viewModel = ContentViewModel() + private var binding: ActivityMainBinding? = null + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + binding = ActivityMainBinding.inflate(layoutInflater) + binding?.supportingView?.setOnItemClickListener { newItem -> + viewModel.selectFromSupportingPane(newItem) + } + setContentView(binding?.root) + + lifecycleScope.launch { + repeatOnLifecycle(Lifecycle.State.STARTED) { + viewModel.state.collect { state -> + println("Add main content") + binding?.mainView?.setText(state.key) + binding?.supportingView?.updateItems(state.items) + } + } + } + } +} \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/supporting-pane-views/app/src/main/java/com/google/supporting/pane/views/MainContentView.kt b/samples/user-interface/canonical-layouts/supporting-pane-views/app/src/main/java/com/google/supporting/pane/views/MainContentView.kt new file mode 100644 index 00000000..fb52ce1a --- /dev/null +++ b/samples/user-interface/canonical-layouts/supporting-pane-views/app/src/main/java/com/google/supporting/pane/views/MainContentView.kt @@ -0,0 +1,42 @@ +/* + * 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 + * + * 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.google.supporting.pane.views + +import android.content.Context +import android.util.AttributeSet +import android.view.LayoutInflater +import androidx.constraintlayout.widget.ConstraintLayout +import com.google.supporting.pane.views.databinding.MainContentBinding + + +class MainContentView @JvmOverloads +constructor(ctx: Context, attributeSet: AttributeSet? = null, defStyleAttr: Int = 0) + : ConstraintLayout(ctx, attributeSet, defStyleAttr) { + + private val binding: MainContentBinding + + init { + // get the inflater service from the android system + val inflater = ctx.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater + binding = MainContentBinding.inflate(inflater, this, true) + + } + + fun setText(label: CharSequence){ + binding.contentLabel?.text = label + } +} \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/supporting-pane-views/app/src/main/java/com/google/supporting/pane/views/SupportAdapter.kt b/samples/user-interface/canonical-layouts/supporting-pane-views/app/src/main/java/com/google/supporting/pane/views/SupportAdapter.kt new file mode 100644 index 00000000..e9b27db9 --- /dev/null +++ b/samples/user-interface/canonical-layouts/supporting-pane-views/app/src/main/java/com/google/supporting/pane/views/SupportAdapter.kt @@ -0,0 +1,55 @@ +/* + * 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 + * + * 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.google.supporting.pane.views + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import androidx.recyclerview.widget.RecyclerView.ViewHolder +import com.google.supporting.pane.views.databinding.ItemSupportBinding + +class SupportAdapter(val onItemClick: (String) -> Unit) : RecyclerView.Adapter() { + + private val items = mutableListOf() + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SupportViewHolder { + val binding = ItemSupportBinding.inflate(LayoutInflater.from(parent.context), parent, false) + return SupportViewHolder(binding) + } + + override fun getItemCount(): Int = items.size + + override fun onBindViewHolder(holder: SupportViewHolder, position: Int) { + holder.bind(items[position]) { + onItemClick(it) + } + } + + fun updateItems(newItems: List) { + items.clear() + items.addAll(newItems) + notifyDataSetChanged() + } +} + +class SupportViewHolder(private val binding: ItemSupportBinding) : ViewHolder(binding.root) { + + fun bind(label: String, onClick: (String) -> Unit) { + binding.supportItem.text = label + binding.root.setOnClickListener { onClick(label) } + } +} \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/supporting-pane-views/app/src/main/java/com/google/supporting/pane/views/SupportingContentView.kt b/samples/user-interface/canonical-layouts/supporting-pane-views/app/src/main/java/com/google/supporting/pane/views/SupportingContentView.kt new file mode 100644 index 00000000..f7a3edfe --- /dev/null +++ b/samples/user-interface/canonical-layouts/supporting-pane-views/app/src/main/java/com/google/supporting/pane/views/SupportingContentView.kt @@ -0,0 +1,58 @@ +/* + * 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 + * + * 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.google.supporting.pane.views + +import android.content.Context +import android.util.AttributeSet +import android.view.LayoutInflater +import androidx.constraintlayout.widget.ConstraintLayout +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import com.google.supporting.pane.views.databinding.SupportingContentBinding + + +class SupportingContentView @JvmOverloads +constructor(ctx: Context, attributeSet: AttributeSet? = null, defStyleAttr: Int = 0) + : ConstraintLayout(ctx, attributeSet, defStyleAttr) { + + private val adapter = SupportAdapter { onItemClicked(it) } + private var itemClickListener: (String) -> Unit = {} + + init { + // get the inflater service from the android system + val inflater = ctx.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater + with(SupportingContentBinding.inflate(inflater, this, true)){ + supportList.adapter = adapter + supportList.layoutManager = + LinearLayoutManager(context, RecyclerView.VERTICAL, false) + + } + } + + fun updateItems(newItems: List){ + adapter.updateItems(newItems) + } + + fun setOnItemClickListener(f: (String) -> Unit) { + itemClickListener = f + } + + private fun onItemClicked(item: String){ + itemClickListener(item) + } + +} \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/supporting-pane-views/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/samples/user-interface/canonical-layouts/supporting-pane-views/app/src/main/res/drawable-v24/ic_launcher_foreground.xml new file mode 100644 index 00000000..2b068d11 --- /dev/null +++ b/samples/user-interface/canonical-layouts/supporting-pane-views/app/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/supporting-pane-views/app/src/main/res/drawable/ic_launcher_background.xml b/samples/user-interface/canonical-layouts/supporting-pane-views/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 00000000..07d5da9c --- /dev/null +++ b/samples/user-interface/canonical-layouts/supporting-pane-views/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/user-interface/canonical-layouts/supporting-pane-views/app/src/main/res/layout-w840dp/activity_main.xml b/samples/user-interface/canonical-layouts/supporting-pane-views/app/src/main/res/layout-w840dp/activity_main.xml new file mode 100644 index 00000000..e99cd703 --- /dev/null +++ b/samples/user-interface/canonical-layouts/supporting-pane-views/app/src/main/res/layout-w840dp/activity_main.xml @@ -0,0 +1,47 @@ + + + + + + + + + + \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/supporting-pane-views/app/src/main/res/layout/activity_main.xml b/samples/user-interface/canonical-layouts/supporting-pane-views/app/src/main/res/layout/activity_main.xml new file mode 100644 index 00000000..b70e1977 --- /dev/null +++ b/samples/user-interface/canonical-layouts/supporting-pane-views/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,47 @@ + + + + + + + + + + \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/supporting-pane-views/app/src/main/res/layout/item_support.xml b/samples/user-interface/canonical-layouts/supporting-pane-views/app/src/main/res/layout/item_support.xml new file mode 100644 index 00000000..3c3d515a --- /dev/null +++ b/samples/user-interface/canonical-layouts/supporting-pane-views/app/src/main/res/layout/item_support.xml @@ -0,0 +1,38 @@ + + + + + + + \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/supporting-pane-views/app/src/main/res/layout/main_content.xml b/samples/user-interface/canonical-layouts/supporting-pane-views/app/src/main/res/layout/main_content.xml new file mode 100644 index 00000000..e86cb3fb --- /dev/null +++ b/samples/user-interface/canonical-layouts/supporting-pane-views/app/src/main/res/layout/main_content.xml @@ -0,0 +1,50 @@ + + + + + + + + + + \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/supporting-pane-views/app/src/main/res/layout/supporting_content.xml b/samples/user-interface/canonical-layouts/supporting-pane-views/app/src/main/res/layout/supporting_content.xml new file mode 100644 index 00000000..c20f179a --- /dev/null +++ b/samples/user-interface/canonical-layouts/supporting-pane-views/app/src/main/res/layout/supporting_content.xml @@ -0,0 +1,45 @@ + + + + + + + + + + \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/supporting-pane-views/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/samples/user-interface/canonical-layouts/supporting-pane-views/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 00000000..eca70cfe --- /dev/null +++ b/samples/user-interface/canonical-layouts/supporting-pane-views/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/supporting-pane-views/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/samples/user-interface/canonical-layouts/supporting-pane-views/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 00000000..eca70cfe --- /dev/null +++ b/samples/user-interface/canonical-layouts/supporting-pane-views/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/supporting-pane-views/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/samples/user-interface/canonical-layouts/supporting-pane-views/app/src/main/res/mipmap-hdpi/ic_launcher.webp new file mode 100644 index 00000000..c209e78e Binary files /dev/null and b/samples/user-interface/canonical-layouts/supporting-pane-views/app/src/main/res/mipmap-hdpi/ic_launcher.webp differ diff --git a/samples/user-interface/canonical-layouts/supporting-pane-views/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/samples/user-interface/canonical-layouts/supporting-pane-views/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp new file mode 100644 index 00000000..b2dfe3d1 Binary files /dev/null and b/samples/user-interface/canonical-layouts/supporting-pane-views/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp differ diff --git a/samples/user-interface/canonical-layouts/supporting-pane-views/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/samples/user-interface/canonical-layouts/supporting-pane-views/app/src/main/res/mipmap-mdpi/ic_launcher.webp new file mode 100644 index 00000000..4f0f1d64 Binary files /dev/null and b/samples/user-interface/canonical-layouts/supporting-pane-views/app/src/main/res/mipmap-mdpi/ic_launcher.webp differ diff --git a/samples/user-interface/canonical-layouts/supporting-pane-views/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/samples/user-interface/canonical-layouts/supporting-pane-views/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp new file mode 100644 index 00000000..62b611da Binary files /dev/null and b/samples/user-interface/canonical-layouts/supporting-pane-views/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp differ diff --git a/samples/user-interface/canonical-layouts/supporting-pane-views/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/samples/user-interface/canonical-layouts/supporting-pane-views/app/src/main/res/mipmap-xhdpi/ic_launcher.webp new file mode 100644 index 00000000..948a3070 Binary files /dev/null and b/samples/user-interface/canonical-layouts/supporting-pane-views/app/src/main/res/mipmap-xhdpi/ic_launcher.webp differ diff --git a/samples/user-interface/canonical-layouts/supporting-pane-views/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/samples/user-interface/canonical-layouts/supporting-pane-views/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp new file mode 100644 index 00000000..1b9a6956 Binary files /dev/null and b/samples/user-interface/canonical-layouts/supporting-pane-views/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp differ diff --git a/samples/user-interface/canonical-layouts/supporting-pane-views/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/samples/user-interface/canonical-layouts/supporting-pane-views/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp new file mode 100644 index 00000000..28d4b77f Binary files /dev/null and b/samples/user-interface/canonical-layouts/supporting-pane-views/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ diff --git a/samples/user-interface/canonical-layouts/supporting-pane-views/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/samples/user-interface/canonical-layouts/supporting-pane-views/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp new file mode 100644 index 00000000..9287f508 Binary files /dev/null and b/samples/user-interface/canonical-layouts/supporting-pane-views/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp differ diff --git a/samples/user-interface/canonical-layouts/supporting-pane-views/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/samples/user-interface/canonical-layouts/supporting-pane-views/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp new file mode 100644 index 00000000..aa7d6427 Binary files /dev/null and b/samples/user-interface/canonical-layouts/supporting-pane-views/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ diff --git a/samples/user-interface/canonical-layouts/supporting-pane-views/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/samples/user-interface/canonical-layouts/supporting-pane-views/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp new file mode 100644 index 00000000..9126ae37 Binary files /dev/null and b/samples/user-interface/canonical-layouts/supporting-pane-views/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp differ diff --git a/samples/user-interface/canonical-layouts/supporting-pane-views/app/src/main/res/values-night/themes.xml b/samples/user-interface/canonical-layouts/supporting-pane-views/app/src/main/res/values-night/themes.xml new file mode 100644 index 00000000..bdd98895 --- /dev/null +++ b/samples/user-interface/canonical-layouts/supporting-pane-views/app/src/main/res/values-night/themes.xml @@ -0,0 +1,32 @@ + + + + + + \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/supporting-pane-views/app/src/main/res/values/colors.xml b/samples/user-interface/canonical-layouts/supporting-pane-views/app/src/main/res/values/colors.xml new file mode 100644 index 00000000..19d01e1e --- /dev/null +++ b/samples/user-interface/canonical-layouts/supporting-pane-views/app/src/main/res/values/colors.xml @@ -0,0 +1,27 @@ + + + + #FFBB86FC + #FF6200EE + #FF3700B3 + #FF03DAC5 + #FF018786 + #FF000000 + #FFFFFFFF + + #33018786 + \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/supporting-pane-views/app/src/main/res/values/strings.xml b/samples/user-interface/canonical-layouts/supporting-pane-views/app/src/main/res/values/strings.xml new file mode 100644 index 00000000..a4192b5a --- /dev/null +++ b/samples/user-interface/canonical-layouts/supporting-pane-views/app/src/main/res/values/strings.xml @@ -0,0 +1,21 @@ + + + + Supporting Pane Views + Main content + Related content + \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/supporting-pane-views/app/src/main/res/values/themes.xml b/samples/user-interface/canonical-layouts/supporting-pane-views/app/src/main/res/values/themes.xml new file mode 100644 index 00000000..86929ced --- /dev/null +++ b/samples/user-interface/canonical-layouts/supporting-pane-views/app/src/main/res/values/themes.xml @@ -0,0 +1,32 @@ + + + + + + \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/supporting-pane-views/app/src/main/res/xml/backup_rules.xml b/samples/user-interface/canonical-layouts/supporting-pane-views/app/src/main/res/xml/backup_rules.xml new file mode 100644 index 00000000..18ec4459 --- /dev/null +++ b/samples/user-interface/canonical-layouts/supporting-pane-views/app/src/main/res/xml/backup_rules.xml @@ -0,0 +1,29 @@ + + + + + + \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/supporting-pane-views/app/src/main/res/xml/data_extraction_rules.xml b/samples/user-interface/canonical-layouts/supporting-pane-views/app/src/main/res/xml/data_extraction_rules.xml new file mode 100644 index 00000000..4b612207 --- /dev/null +++ b/samples/user-interface/canonical-layouts/supporting-pane-views/app/src/main/res/xml/data_extraction_rules.xml @@ -0,0 +1,35 @@ + + + + + + + + + \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/supporting-pane-views/build.gradle b/samples/user-interface/canonical-layouts/supporting-pane-views/build.gradle new file mode 100644 index 00000000..44313117 --- /dev/null +++ b/samples/user-interface/canonical-layouts/supporting-pane-views/build.gradle @@ -0,0 +1,44 @@ +/* + * 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 + * + * 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. + */ + +plugins { + id 'com.android.application' version '8.6.0' apply false + id 'com.android.library' version '8.6.0' apply false + id 'org.jetbrains.kotlin.android' version '2.0.0' apply false + id 'com.diffplug.spotless' version '6.9.0' apply true +} + +subprojects { + spotless { + java { + target '**/*.java' + googleJavaFormat('1.8').aosp() + licenseHeaderFile project.rootProject.file('scripts/copyright') + } + kotlin { + target '**/*.kt' + ktlint('0.47.0').userData(['android': 'true']) + licenseHeaderFile project.rootProject.file('scripts/copyright') + } + groovyGradle { + target '**/*.gradle' // default target of groovyGradle + // the Groovy Eclipse formatter extends the Java Eclipse formatter, + // so it formats Java files by default (unless `excludeJava` is used). + greclipse().configFile(project.rootProject.file('scripts/greclipse.properties')) + licenseHeaderFile project.rootProject.file('scripts/copyright'), '(plugins|pluginManagement)' + } + } +} \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/supporting-pane-views/gradle.properties b/samples/user-interface/canonical-layouts/supporting-pane-views/gradle.properties new file mode 100644 index 00000000..f19c7b9b --- /dev/null +++ b/samples/user-interface/canonical-layouts/supporting-pane-views/gradle.properties @@ -0,0 +1,24 @@ +# 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 +android.nonFinalResIds=false \ No newline at end of file diff --git a/samples/user-interface/canonical-layouts/supporting-pane-views/gradle/wrapper/gradle-wrapper.jar b/samples/user-interface/canonical-layouts/supporting-pane-views/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 00000000..e708b1c0 Binary files /dev/null and b/samples/user-interface/canonical-layouts/supporting-pane-views/gradle/wrapper/gradle-wrapper.jar differ diff --git a/samples/user-interface/canonical-layouts/supporting-pane-views/gradle/wrapper/gradle-wrapper.properties b/samples/user-interface/canonical-layouts/supporting-pane-views/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..1e687926 --- /dev/null +++ b/samples/user-interface/canonical-layouts/supporting-pane-views/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Wed Aug 17 15:01:41 CEST 2022 +distributionBase=GRADLE_USER_HOME +distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionPath=wrapper/dists +zipStorePath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME diff --git a/samples/user-interface/canonical-layouts/supporting-pane-views/gradlew b/samples/user-interface/canonical-layouts/supporting-pane-views/gradlew new file mode 100755 index 00000000..4f906e0c --- /dev/null +++ b/samples/user-interface/canonical-layouts/supporting-pane-views/gradlew @@ -0,0 +1,185 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or 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 UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# 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"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# 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 + ;; + 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" + which java >/dev/null 2>&1 || 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 + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/samples/user-interface/canonical-layouts/supporting-pane-views/gradlew.bat b/samples/user-interface/canonical-layouts/supporting-pane-views/gradlew.bat new file mode 100644 index 00000000..107acd32 --- /dev/null +++ b/samples/user-interface/canonical-layouts/supporting-pane-views/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/samples/user-interface/canonical-layouts/supporting-pane-views/scripts/copyright b/samples/user-interface/canonical-layouts/supporting-pane-views/scripts/copyright new file mode 100644 index 00000000..8806b97d --- /dev/null +++ b/samples/user-interface/canonical-layouts/supporting-pane-views/scripts/copyright @@ -0,0 +1,16 @@ +/* + * Copyright $YEAR 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. + */ + diff --git a/samples/user-interface/canonical-layouts/supporting-pane-views/scripts/greclipse.properties b/samples/user-interface/canonical-layouts/supporting-pane-views/scripts/greclipse.properties new file mode 100644 index 00000000..2c60a814 --- /dev/null +++ b/samples/user-interface/canonical-layouts/supporting-pane-views/scripts/greclipse.properties @@ -0,0 +1,34 @@ +#Whether to use 'space', 'tab' or 'mixed' (both) characters for indentation. +#The default value is 'tab'. +org.eclipse.jdt.core.formatter.tabulation.char=space + +#Number of spaces used for indentation in case 'space' characters +#have been selected. The default value is 4. +org.eclipse.jdt.core.formatter.tabulation.size=4 + +#Number of spaces used for indentation in case 'mixed' characters +#have been selected. The default value is 4. +org.eclipse.jdt.core.formatter.indentation.size=4 + +#Whether or not indentation characters are inserted into empty lines. +#The default value is 'true'. +org.eclipse.jdt.core.formatter.indent_empty_lines=false + +#Number of spaces used for multiline indentation. +#The default value is 2. +groovy.formatter.multiline.indentation=2 + +#Length after which list are considered too long. These will be wrapped. +#The default value is 30. +groovy.formatter.longListLength=30 + +#Whether opening braces position shall be the next line. +#The default value is 'same'. +groovy.formatter.braces.start=same + +#Whether closing braces position shall be the next line. +#The default value is 'next'. +groovy.formatter.braces.end=next + +#Remove unnecessary semicolons. The default value is 'false'. +groovy.formatter.remove.unnecessary.semicolons=false diff --git a/samples/user-interface/canonical-layouts/supporting-pane-views/settings.gradle b/samples/user-interface/canonical-layouts/supporting-pane-views/settings.gradle new file mode 100644 index 00000000..07aa60ea --- /dev/null +++ b/samples/user-interface/canonical-layouts/supporting-pane-views/settings.gradle @@ -0,0 +1,32 @@ +/* + * Copyright 2020 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. + */ + +pluginManagement { + repositories { + google() + mavenCentral() + gradlePluginPortal() + } +} +dependencyResolutionManagement { + repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) + repositories { + google() + mavenCentral() + } +} +rootProject.name = "Supporting Pane Views" +include ':app'