Skip to content

Commit

Permalink
Add MediaChip
Browse files Browse the repository at this point in the history
  • Loading branch information
luizgrp committed Jun 29, 2022
1 parent e44b411 commit 24270c8
Show file tree
Hide file tree
Showing 9 changed files with 290 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ import androidx.wear.compose.material.Text
import androidx.wear.compose.material.items
import com.google.android.horologist.compose.layout.StateUtils.rememberStateWithLifecycle
import com.google.android.horologist.compose.navscaffold.scrollableColumn
import com.google.android.horologist.media.ui.components.MediaChip
import com.google.android.horologist.mediasample.R
import com.google.android.horologist.mediasample.ui.components.MediaChip

@Composable
fun UampLibraryScreen(
Expand Down Expand Up @@ -67,7 +67,8 @@ fun UampLibraryScreen(
onClick = {
libraryScreenViewModel.play(it)
onPlayClick()
}
},
defaultTitle = stringResource(id = R.string.horologist_no_title),
)
}
} else {
Expand Down
5 changes: 5 additions & 0 deletions media-ui/api/current.api
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ package com.google.android.horologist.media.ui.components {
method @androidx.compose.runtime.Composable @com.google.android.horologist.media.ui.ExperimentalHorologistMediaUiApi public static void MediaArtwork(String? artworkUri, String? contentDescription, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.graphics.painter.Painter? placeholder);
}

public final class MediaChipKt {
method @androidx.compose.runtime.Composable @com.google.android.horologist.media.ui.ExperimentalHorologistMediaUiApi public static void MediaChip(com.google.android.horologist.media.ui.state.model.MediaItemUiModel mediaItem, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional String defaultTitle, optional androidx.compose.ui.graphics.painter.Painter? placeholder);
method @androidx.compose.runtime.Composable @com.google.android.horologist.media.ui.ExperimentalHorologistMediaUiApi public static void MediaChip(String title, String? artworkUri, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.graphics.painter.Painter? placeholder);
}

public final class MediaControlButtonsKt {
method @androidx.compose.runtime.Composable @com.google.android.horologist.media.ui.ExperimentalHorologistMediaUiApi public static void MediaControlButtons(kotlin.jvm.functions.Function0<kotlin.Unit> onPlayButtonClick, kotlin.jvm.functions.Function0<kotlin.Unit> onPauseButtonClick, boolean playPauseButtonEnabled, boolean playing, float percent, kotlin.jvm.functions.Function0<kotlin.Unit> onSeekToPreviousButtonClick, boolean seekToPreviousButtonEnabled, kotlin.jvm.functions.Function0<kotlin.Unit> onSeekToNextButtonClick, boolean seekToNextButtonEnabled, optional androidx.compose.ui.Modifier modifier, optional androidx.wear.compose.material.ButtonColors colors, optional long progressColour);
method @androidx.compose.runtime.Composable @com.google.android.horologist.media.ui.ExperimentalHorologistMediaUiApi public static void MediaControlButtons(kotlin.jvm.functions.Function0<kotlin.Unit> onPlayButtonClick, kotlin.jvm.functions.Function0<kotlin.Unit> onPauseButtonClick, boolean playPauseButtonEnabled, boolean playing, kotlin.jvm.functions.Function0<kotlin.Unit> onSeekToPreviousButtonClick, boolean seekToPreviousButtonEnabled, kotlin.jvm.functions.Function0<kotlin.Unit> onSeekToNextButtonClick, boolean seekToNextButtonEnabled, optional androidx.compose.ui.Modifier modifier, optional androidx.wear.compose.material.ButtonColors colors);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
* 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.
*/

@file:OptIn(ExperimentalHorologistMediaUiApi::class)

package com.google.android.horologist.media.ui.components

import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithText
import com.google.android.horologist.media.ui.ExperimentalHorologistMediaUiApi
import com.google.android.horologist.media.ui.state.model.MediaItemUiModel
import org.junit.Rule
import org.junit.Test

class MediaChipTest {

@get:Rule
val composeTestRule = createComposeRule()

@Test
fun givenTitle_thenDisplaysTitle() {
// given
val title = "title"

composeTestRule.setContent {
MediaChip(
mediaItem = MediaItemUiModel(id = "id", title = title),
onClick = { },
)
}

// then
composeTestRule.onNodeWithText(title).assertExists()
}

@Test
fun givenNoTitle_thenDisplaysTitle() {
// given
val defaultTitle = "defaultTitle"

composeTestRule.setContent {
MediaChip(
mediaItem = MediaItemUiModel(id = "id"),
onClick = { },
defaultTitle = defaultTitle
)
}

// then
composeTestRule.onNodeWithText(defaultTitle).assertExists()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
/*
* 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.
*/

@file:OptIn(ExperimentalHorologistMediaUiApi::class)

package com.google.android.horologist.media.ui.components

import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Album
import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.vector.rememberVectorPainter
import androidx.compose.ui.tooling.preview.Preview
import com.google.android.horologist.media.ui.ExperimentalHorologistMediaUiApi
import com.google.android.horologist.media.ui.state.model.MediaItemUiModel

@Preview(
backgroundColor = 0xff000000,
showBackground = true,
)
@Composable
fun MediaChipPreview() {
MediaChip(
mediaItem = MediaItemUiModel(
id = "id",
title = "Red Hot Chilli Peppers",
artworkUri = "artworkUri"
),
onClick = {},
placeholder = rememberVectorPainter(image = Icons.Default.Album)
)
}

@Preview(
name = "No artwork",
backgroundColor = 0xff000000,
showBackground = true,
)
@Composable
fun MediaChipPreviewNoArtwork() {
MediaChip(
mediaItem = MediaItemUiModel(id = "id", title = "Red Hot Chilli Peppers"),
onClick = {},
placeholder = rememberVectorPainter(image = Icons.Default.Album)
)
}

@Preview(
name = "No title",
backgroundColor = 0xff000000,
showBackground = true,
)
@Composable
fun MediaChipPreviewNoTitle() {
MediaChip(
mediaItem = MediaItemUiModel(id = "id", artworkUri = "artworkUri"),
onClick = {},
defaultTitle = "No title",
placeholder = rememberVectorPainter(image = Icons.Default.Album)
)
}

@Preview(
name = "Very long title",
backgroundColor = 0xff000000,
showBackground = true,
)
@Composable
fun MediaChipPreviewVeryLongTitle() {
MediaChip(
mediaItem = MediaItemUiModel(
id = "id",
title = "Very very very very very very very very very very very very very very very very very very very long title",
artworkUri = "artworkUri"
),
onClick = {},
placeholder = rememberVectorPainter(image = Icons.Default.Album)
)
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2021 The Android Open Source Project
* 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.
Expand All @@ -14,52 +14,74 @@
* limitations under the License.
*/

package com.google.android.horologist.mediasample.ui.components
package com.google.android.horologist.media.ui.components

import androidx.compose.foundation.layout.BoxScope
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.size
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.wear.compose.material.Chip
import androidx.wear.compose.material.ChipDefaults
import androidx.wear.compose.material.Text
import com.google.android.horologist.media.ui.components.MediaArtwork
import com.google.android.horologist.media.ui.ExperimentalHorologistMediaUiApi
import com.google.android.horologist.media.ui.state.model.MediaItemUiModel
import com.google.android.horologist.mediasample.R

/**
* A rounded chip to show a single [MediaItemUiModel] with an
* optional secondary action
* A rounded chip to show a single [MediaItemUiModel].
*
* @param mediaItem The [MediaItemUiModel] that the [title][MediaItemUiModel.title] and
* [artwork][MediaItemUiModel.artworkUri] will be used to display on the chip.
* @param onClick Will be called when the user clicks the chip.
* @param modifier The Modifier to be applied to the chip.
* @param defaultTitle A text to be used when [MediaItemUiModel.title] is null.
* @param placeholder A placeholder image to be displayed while
* [artwork][MediaItemUiModel.artworkUri] is being loaded.
*/
@ExperimentalHorologistMediaUiApi
@Composable
fun MediaChip(
public fun MediaChip(
mediaItem: MediaItemUiModel,
onClick: () -> Unit,
modifier: Modifier = Modifier,
defaultTitle: String = "",
placeholder: Painter? = null,
) {
val artworkUri = mediaItem.artworkUri
val title = mediaItem.title

MediaChip(artworkUri = artworkUri, title = title, onClick = onClick, modifier = modifier)
MediaChip(
title = title ?: defaultTitle,
artworkUri = artworkUri,
onClick = onClick,
modifier = modifier,
placeholder = placeholder,
)
}

/**
* A rounded chip to show a single media title and its artwork.
*/
@ExperimentalHorologistMediaUiApi
@Composable
fun MediaChip(
public fun MediaChip(
title: String,
artworkUri: String?,
title: String?,
onClick: () -> Unit,
modifier: Modifier = Modifier,
placeholder: Painter? = null,
) {
val appIcon: (@Composable BoxScope.() -> Unit)? = artworkUri?.let {
{
MediaArtwork(
modifier = Modifier.size(ChipDefaults.LargeIconSize),
contentDescription = title ?: stringResource(id = R.string.horologist_no_title),
artworkUri = artworkUri
contentDescription = title,
artworkUri = artworkUri,
placeholder = placeholder,
)
}
}
Expand All @@ -77,8 +99,9 @@ fun MediaChip(
icon = appIcon,
label = {
Text(
text = title ?: stringResource(id = R.string.horologist_no_title),
maxLines = 2
text = title,
maxLines = 2,
overflow = TextOverflow.Ellipsis
)
}
)
Expand Down
Original file line number Diff line number Diff line change
@@ -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.
*/

@file:OptIn(ExperimentalHorologistMediaUiApi::class)

package com.google.android.horologist.media.ui.components

import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Album
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.rememberVectorPainter
import app.cash.paparazzi.Paparazzi
import com.google.android.horologist.media.ui.ExperimentalHorologistMediaUiApi
import com.google.android.horologist.paparazzi.GALAXY_WATCH4_CLASSIC_LARGE
import com.google.android.horologist.paparazzi.WearSnapshotHandler
import com.google.android.horologist.paparazzi.determineHandler
import org.junit.Rule
import org.junit.Test

class MediaChipTest {

@get:Rule
val paparazzi = Paparazzi(
deviceConfig = GALAXY_WATCH4_CLASSIC_LARGE,
theme = "android:ThemeOverlay.Material.Dark",
maxPercentDifference = 0.0,
snapshotHandler = WearSnapshotHandler(determineHandler(0.1))
)

@Test
fun givenMediaItemWithArtwork_thenDisplaysArtwork() {
paparazzi.snapshot {
Box(modifier = Modifier.background(Color.Black), contentAlignment = Alignment.Center) {
MediaChip(
title = "Red Hot Chilli Peppers",
artworkUri = "artworkUri",
onClick = {},
placeholder = rememberVectorPainter(image = Icons.Default.Album)
)
}
}
}

@Test
fun givenMediaItemWithNOArtwork_thenDoesNOTDisplayArtwork() {
paparazzi.snapshot {
Box(modifier = Modifier.background(Color.Black), contentAlignment = Alignment.Center) {
MediaChip(
title = "Red Hot Chilli Peppers",
artworkUri = null,
onClick = {},
placeholder = rememberVectorPainter(image = Icons.Default.Album)
)
}
}
}

@Test
fun givenVeryLongTitle_thenEllipsizeAt2ndLine() {
paparazzi.snapshot {
Box(modifier = Modifier.background(Color.Black), contentAlignment = Alignment.Center) {
MediaChip(
title = "Very very very very very very very very very very very long title",
artworkUri = "artworkUri",
onClick = {},
placeholder = rememberVectorPainter(image = Icons.Default.Album)
)
}
}
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 24270c8

Please sign in to comment.