Skip to content

Commit

Permalink
[Jetcaster] Merge jetcaster/all_form_factors into main (#1345)
Browse files Browse the repository at this point in the history
The new Jetcaster sample now demonstrates how to build an app with
Compose such that it supports the following form factors: phones,
tablets, foldables, TV and Wear.


![readme_cover](https://github.com/android/compose-samples/assets/10263978/a58ab950-71aa-48e0-8bc7-85443a1b4f6b)
  • Loading branch information
arriolac committed Apr 16, 2024
2 parents ed8deef + 893e2bc commit 03ad355
Show file tree
Hide file tree
Showing 222 changed files with 13,758 additions and 1,673 deletions.
43 changes: 9 additions & 34 deletions Jetcaster/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,60 +3,34 @@
# Jetcaster sample 🎙️

Jetcaster is a sample podcast app, built with [Jetpack Compose][compose]. The goal of the sample is to
showcase dynamic theming and full featured architecture.
showcase building with Compose across multiple form factors (mobile, TV, and Wear) and full featured architecture.

To try out this sample app, use the latest stable version
of [Android Studio](https://developer.android.com/studio).
You can clone this repository or import the
project from Android Studio following the steps
[here](https://developer.android.com/jetpack/compose/setup#sample).

### Status: 🚧 In progress 🚧

Jetcaster is still in the early stages of development, and as such only one screen has been created so far. However,
most of the app's architecture has been implemented, as well as the data layer, and early stages of dynamic theming.


## Screenshots

<img src="docs/screenshots.png"/>
![readme_cover](https://github.com/android/compose-samples/assets/10263978/a58ab950-71aa-48e0-8bc7-85443a1b4f6b)

## Features

This sample contains 2 screens so far: the home screen, and a player screen.
This sample has 3 components: the home screen, the podcast details screen, and the player screen

The home screen is split into sub-screens for easy re-use:

- __Home__, allowing the user to see their followed podcasts (top carousel), and navigate between 'Your Library' and 'Discover'
- __Home__, allowing the user to see their subscribed podcasts (top carousel), and navigate between 'Your Library' and 'Discover'
- __Discover__, allowing the user to browse podcast categories
- __Podcast Category__, allowing the user to see a list of recent episodes for podcasts in a given category.

The player screen displays media controls and the currently "playing" podcast (the sample currently doesn't actually play any media).
The player screen layout is adapting to different form factors, including a tabletop layout on foldable devices:

<img src="docs/tabletop.png"/>

### Dynamic theming
The home screen currently implements dynamic theming, using the artwork of the currently selected podcast from the carousel to update the `primary` and `onPrimary` [colors](https://developer.android.com/reference/kotlin/androidx/compose/material/Colors). You can see it in action in the screenshots above: as the carousel item is changed, the background gradient is updated to match the artwork.

This is implemented in [`DynamicTheming.kt`](app/src/main/java/com/example/jetcaster/util/DynamicTheming.kt), which provides the `DynamicThemePrimaryColorsFromImage` composable, to automatically animate the theme colors based on the provided image URL, like so:
Multiple panes will also be shown depending on the device's [window size class][wsc].

``` kotlin
val dominantColorState: DominantColorState = rememberDominantColorState()

DynamicThemePrimaryColorsFromImage(dominantColorState) {
var imageUrl = remember { mutableStateOf("") }

// When the image url changes, call updateColorsFromImageUrl()
launchInComposition(imageUrl) {
dominantColorState.updateColorsFromImageUrl(imageUrl)
}

// Content which will be dynamically themed....
}
```
The player screen displays media controls and the currently "playing" podcast (the sample currently **does not** actually play any media—the behavior is simply mocked).
The player screen layout is adapting to different form factors, including a tabletop layout on foldable devices:

Underneath, [`DominantColorState`](app/src/main/java/com/example/jetcaster/util/DynamicTheming.kt) uses the [Coil][coil] library to fetch the artwork image 🖼️, and then [Palette][palette] to extract the dominant colors from the image 🎨.
![readme_fold](https://github.com/android/compose-samples/assets/10263978/fe02248f-81ce-489b-a6d6-838438c8368e)


### Others
Expand Down Expand Up @@ -139,3 +113,4 @@ limitations under the License.
[rome]: https://rometools.github.io/rome/
[jdk8desugar]: https://developer.android.com/studio/write/java8-support#library-desugaring
[coil]: https://coil-kt.github.io/coil/
[wsc]: https://developer.android.com/guide/topics/large-screens/support-different-screen-sizes#window_size_classes
30 changes: 16 additions & 14 deletions Jetcaster/app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.android)
alias(libs.plugins.ksp)
alias(libs.plugins.hilt)
}

android {
Expand Down Expand Up @@ -84,6 +85,7 @@ android {
}

dependencies {
implementation(project(":core:model"))
val composeBom = platform(libs.androidx.compose.bom)
implementation(composeBom)
androidTestImplementation(composeBom)
Expand All @@ -95,14 +97,20 @@ dependencies {
implementation(libs.androidx.core.ktx)
implementation(libs.androidx.palette)

implementation(libs.androidx.activity.compose)

implementation(libs.androidx.constraintlayout.compose)
// Dependency injection
implementation(libs.androidx.hilt.navigation.compose)
implementation(libs.hilt.android)
ksp(libs.hilt.compiler)

// Compose
implementation(libs.androidx.activity.compose)
implementation(libs.androidx.compose.foundation)
implementation(libs.androidx.compose.material)
implementation(libs.androidx.compose.materialWindow)
implementation(libs.androidx.compose.material.iconsExtended)
implementation(libs.androidx.compose.material3)
implementation(libs.androidx.compose.material3.adaptive)
implementation(libs.androidx.compose.material3.adaptive.layout)
implementation(libs.androidx.compose.material3.adaptive.navigation)
implementation(libs.androidx.compose.ui)
implementation(libs.androidx.compose.ui.tooling.preview)
debugImplementation(libs.androidx.compose.ui.tooling)

Expand All @@ -112,20 +120,14 @@ dependencies {
implementation(libs.androidx.navigation.compose)

implementation(libs.androidx.window)
implementation(libs.androidx.window.core)

implementation(libs.accompanist.adaptive)

implementation(libs.coil.kt.compose)

implementation(libs.okhttp3)
implementation(libs.okhttp.logging)

implementation(libs.rometools.rome)
implementation(libs.rometools.modules)

implementation(libs.androidx.room.runtime)
implementation(libs.androidx.room.ktx)
implementation(project(":core"))
implementation(project(":designsystem"))

ksp(libs.androidx.room.compiler)
coreLibraryDesugaring(libs.core.jdk.desugaring)
}
114 changes: 0 additions & 114 deletions Jetcaster/app/src/main/java/com/example/jetcaster/Graph.kt

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -19,20 +19,16 @@ package com.example.jetcaster
import android.app.Application
import coil.ImageLoader
import coil.ImageLoaderFactory
import dagger.hilt.android.HiltAndroidApp
import javax.inject.Inject

/**
* Application which sets up our dependency [Graph] with a context.
*/
@HiltAndroidApp
class JetcasterApplication : Application(), ImageLoaderFactory {
override fun onCreate() {
super.onCreate()
Graph.provide(this)
}

override fun newImageLoader(): ImageLoader {
return ImageLoader.Builder(this)
// Disable `Cache-Control` header support as some podcast images disable disk caching.
.respectCacheHeaders(false)
.build()
}
@Inject lateinit var imageLoader: ImageLoader

override fun newImageLoader(): ImageLoader = imageLoader
}

This file was deleted.

Loading

0 comments on commit 03ad355

Please sign in to comment.