Skip to content

Commit

Permalink
Merge branch 'z-huang:dev' into dev
Browse files Browse the repository at this point in the history
  • Loading branch information
netrunner-exe authored Jul 18, 2023
2 parents 6475d31 + 61cc54f commit c1fbbc3
Show file tree
Hide file tree
Showing 47 changed files with 768 additions and 371 deletions.
4 changes: 4 additions & 0 deletions app/src/main/java/com/zionhuang/music/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,8 @@ class MainActivity : ComponentActivity() {
songs.firstOrNull()?.album?.id?.let { browseId ->
navController.navigate("album/$browseId")
}
}.onFailure {
it.printStackTrace()
}
}
} else {
Expand All @@ -365,6 +367,8 @@ class MainActivity : ComponentActivity() {
YouTube.queue(listOf(videoId))
}.onSuccess {
sharedSong = it.firstOrNull()
}.onFailure {
it.printStackTrace()
}
}
}
Expand Down
18 changes: 18 additions & 0 deletions app/src/main/java/com/zionhuang/music/constants/StatPeriod.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.zionhuang.music.constants

import java.time.LocalDateTime
import java.time.ZoneOffset

enum class StatPeriod {
`1_WEEK`, `1_MONTH`, `3_MONTH`, `6_MONTH`, `1_YEAR`, ALL;

fun toTimeMillis(): Long =
when (this) {
`1_WEEK` -> LocalDateTime.now().minusWeeks(1).toInstant(ZoneOffset.UTC).toEpochMilli()
`1_MONTH` -> LocalDateTime.now().minusMonths(1).toInstant(ZoneOffset.UTC).toEpochMilli()
`3_MONTH` -> LocalDateTime.now().minusMonths(3).toInstant(ZoneOffset.UTC).toEpochMilli()
`6_MONTH` -> LocalDateTime.now().minusMonths(6).toInstant(ZoneOffset.UTC).toEpochMilli()
`1_YEAR` -> LocalDateTime.now().minusMonths(12).toInstant(ZoneOffset.UTC).toEpochMilli()
ALL -> 0
}
}
41 changes: 29 additions & 12 deletions app/src/main/java/com/zionhuang/music/db/DatabaseDao.kt
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ interface DatabaseDao {
song.artists.joinToString(separator = "") { it.name }
}
}

SongSortType.PLAY_TIME -> songsByPlayTimeAsc()
}.map { it.reversed(descending) }

Expand Down Expand Up @@ -71,6 +72,7 @@ interface DatabaseDao {
song.artists.joinToString(separator = "") { it.name }
}
}

SongSortType.PLAY_TIME -> likedSongsByPlayTimeAsc()
}.map { it.reversed(descending) }

Expand Down Expand Up @@ -142,29 +144,44 @@ interface DatabaseDao {
fun quickPicks(now: Long = System.currentTimeMillis()): Flow<List<Song>>

@Transaction
@Query("SELECT * FROM song ORDER BY totalPlayTime DESC LIMIT :limit")
fun mostPlayedSongs(limit: Int = 6): Flow<List<Song>>
@Query(
"""
SELECT *
FROM song
WHERE id IN (SELECT songId
FROM event
WHERE timestamp > :fromTimeStamp
GROUP BY songId
ORDER BY SUM(playTime) DESC
LIMIT :limit)
"""
)
fun mostPlayedSongs(fromTimeStamp: Long, limit: Int = 6): Flow<List<Song>>

@Transaction
@Query(
"""
SELECT artist.*,
SELECT *,
(SELECT COUNT(1)
FROM song_artist_map
JOIN song ON song_artist_map.songId = song.id
WHERE artistId = artist.id
AND song.inLibrary IS NOT NULL) AS songCount
FROM (SELECT artistId, SUM(playtime) AS totalPlaytime
FROM (SELECT *, (SELECT totalPlayTime FROM song WHERE id = songId) AS playtime
FROM song_artist_map)
GROUP BY artistId)
JOIN artist
ON artist.id = artistId
ORDER BY totalPlaytime DESC
LIMIT :limit
FROM artist
JOIN(SELECT artistId, SUM(songTotalPlayTime) AS totalPlayTime
FROM song_artist_map
JOIN (SELECT songId, SUM(playTime) AS songTotalPlayTime
FROM event
WHERE timestamp > :fromTimeStamp
GROUP BY songId) AS e
ON song_artist_map.songId = e.songId
GROUP BY artistId
ORDER BY totalPlayTime DESC
LIMIT :limit)
ON artist.id = artistId
"""
)
fun mostPlayedArtists(limit: Int = 6): Flow<List<Artist>>
fun mostPlayedArtists(fromTimeStamp: Long, limit: Int = 6): Flow<List<Artist>>

@Transaction
@Query("SELECT * FROM song WHERE id = :songId")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package com.zionhuang.music.ui.component

import androidx.annotation.DrawableRes
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.slideInVertically
import androidx.compose.animation.slideOutVertically
import androidx.compose.foundation.ScrollState
import androidx.compose.foundation.layout.BoxScope
import androidx.compose.foundation.layout.WindowInsetsSides
import androidx.compose.foundation.layout.only
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.windowInsetsPadding
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.material3.FloatingActionButton
import androidx.compose.material3.Icon
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import com.zionhuang.music.LocalPlayerAwareWindowInsets
import com.zionhuang.music.ui.utils.isScrollingUp

@Composable
fun BoxScope.HideOnScrollFAB(
visible: Boolean = true,
lazyListState: LazyListState,
@DrawableRes icon: Int,
onClick: () -> Unit,
) {
AnimatedVisibility(
visible = visible && lazyListState.isScrollingUp(),
enter = slideInVertically { it },
exit = slideOutVertically { it },
modifier = Modifier
.align(Alignment.BottomEnd)
.windowInsetsPadding(
LocalPlayerAwareWindowInsets.current
.only(WindowInsetsSides.Bottom + WindowInsetsSides.Horizontal)
)
) {
FloatingActionButton(
modifier = Modifier.padding(16.dp),
onClick = onClick
) {
Icon(
painter = painterResource(icon),
contentDescription = null
)
}
}
}

@Composable
fun BoxScope.HideOnScrollFAB(
visible: Boolean = true,
scrollState: ScrollState,
@DrawableRes icon: Int,
onClick: () -> Unit,
) {
AnimatedVisibility(
visible = visible && scrollState.isScrollingUp(),
enter = slideInVertically { it },
exit = slideOutVertically { it },
modifier = Modifier
.align(Alignment.BottomEnd)
.windowInsetsPadding(
LocalPlayerAwareWindowInsets.current
.only(WindowInsetsSides.Bottom + WindowInsetsSides.Horizontal)
)
) {
FloatingActionButton(
modifier = Modifier.padding(16.dp),
onClick = onClick
) {
Icon(
painter = painterResource(icon),
contentDescription = null
)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package com.zionhuang.music.ui.component

import androidx.annotation.DrawableRes
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp

@Composable
fun NavigationTile(
title: String,
@DrawableRes icon: Int,
onClick: () -> Unit,
modifier: Modifier = Modifier,
) {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(4.dp),
modifier = modifier
.clip(RoundedCornerShape(4.dp))
.clickable(onClick = onClick)
.padding(4.dp),
) {
Icon(
painter = painterResource(icon),
contentDescription = null
)

Text(
text = title,
style = MaterialTheme.typography.labelMedium,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package com.zionhuang.music.ui.component

import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.WindowInsetsSides
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.only
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.systemBars
import androidx.compose.foundation.layout.windowInsetsPadding
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import com.zionhuang.music.R

@Composable
fun NavigationTitle(
title: String,
modifier: Modifier = Modifier,
onClick: (() -> Unit)? = null,
) {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = modifier
.fillMaxWidth()
.windowInsetsPadding(WindowInsets.systemBars.only(WindowInsetsSides.Horizontal))
.clickable(enabled = onClick != null) {
onClick?.invoke()
}
.padding(12.dp)
) {
Column(
modifier = Modifier.weight(1f)
) {
Text(
text = title,
style = MaterialTheme.typography.headlineSmall
)
}

if (onClick != null) {
Icon(
painter = painterResource(R.drawable.navigate_next),
contentDescription = null
)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import androidx.compose.foundation.layout.WindowInsetsSides
import androidx.compose.foundation.layout.asPaddingValues
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.only
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.windowInsetsPadding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
Expand All @@ -23,8 +22,6 @@ import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.navigation.NavController
import com.zionhuang.innertube.models.WatchEndpoint
Expand All @@ -35,6 +32,7 @@ import com.zionhuang.music.extensions.togglePlayPause
import com.zionhuang.music.models.toMediaMetadata
import com.zionhuang.music.playback.queues.YouTubeQueue
import com.zionhuang.music.ui.component.LocalMenuState
import com.zionhuang.music.ui.component.NavigationTitle
import com.zionhuang.music.ui.component.SongListItem
import com.zionhuang.music.ui.menu.SongMenu
import com.zionhuang.music.viewmodels.DateAgo
Expand All @@ -60,20 +58,17 @@ fun HistoryScreen(
) {
events.forEach { (dateAgo, events) ->
stickyHeader {
Text(
text = when (dateAgo) {
NavigationTitle(
title = when (dateAgo) {
DateAgo.Today -> stringResource(R.string.today)
DateAgo.Yesterday -> stringResource(R.string.yesterday)
DateAgo.ThisWeek -> stringResource(R.string.this_week)
DateAgo.LastWeek -> stringResource(R.string.last_week)
is DateAgo.Other -> dateAgo.date.format(DateTimeFormatter.ofPattern("yyyy/MM"))
},
style = MaterialTheme.typography.headlineMedium,
overflow = TextOverflow.Ellipsis,
modifier = Modifier
.fillMaxWidth()
.background(MaterialTheme.colorScheme.background)
.padding(horizontal = 12.dp, vertical = 8.dp)
)
}

Expand Down
Loading

0 comments on commit c1fbbc3

Please sign in to comment.