Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3bd29f75e6 | ||
|
|
d7a3a41ab2 | ||
|
|
05e318f9ab | ||
|
|
e8fb0ac564 | ||
|
|
7379f3d93e |
@@ -85,7 +85,9 @@ jobs:
|
||||
echo "value=false" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
- name: Check release notes exist
|
||||
run: test -f "metadata/en-US/changelogs/${{ needs.build.outputs.version-code }}.txt"
|
||||
run: |
|
||||
test -f "metadata/en-US/changelogs/${{ needs.build.outputs.version-code }}.txt"
|
||||
test -f "docs/releases/$GITHUB_REF_NAME.md"
|
||||
- name: Write Play service account
|
||||
if: steps.stable.outputs.value == 'true'
|
||||
env:
|
||||
@@ -123,7 +125,7 @@ jobs:
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
args=(--verify-tag --title "$GITHUB_REF_NAME" --notes-file "metadata/en-US/changelogs/${{ needs.build.outputs.version-code }}.txt")
|
||||
args=(--verify-tag --title "$GITHUB_REF_NAME" --notes-file "docs/releases/$GITHUB_REF_NAME.md")
|
||||
if [ "${{ steps.stable.outputs.value }}" != "true" ]; then
|
||||
args+=(--prerelease)
|
||||
fi
|
||||
|
||||
@@ -37,6 +37,7 @@ See [signing/README.md](signing/README.md) for the fingerprints and the commands
|
||||
- **Adaptive Theming:** Colours drawn from the current song's artwork.
|
||||
- **Search:** Fast search across the whole library, with per-list sort and filter controls.
|
||||
- **Playback:** Gapless playback, an editable queue you can reorder and jump through, plus loop and shuffle modes.
|
||||
- **ReplayGain:** Loudness normalization from the tags your server reports, with Auto, track and album modes and a preamp.
|
||||
- **Up Next:** Add items into the currently playing queue without rewriting it.
|
||||
- **Multiple Servers:** Connect several servers and switch between them quickly.
|
||||
- **Stream Quality:** Bitrate and format settings for Wi-Fi and mobile, with automatic switching.
|
||||
|
||||
@@ -36,8 +36,8 @@ android {
|
||||
applicationId = "com.subtracks"
|
||||
minSdk = 24
|
||||
targetSdk = 37
|
||||
versionCode = 13
|
||||
versionName = "3.0.0"
|
||||
versionCode = 14
|
||||
versionName = "3.1.0"
|
||||
buildConfigField("String", "COMMIT_HASH", "\"$commitHash\"")
|
||||
}
|
||||
|
||||
|
||||
@@ -40,12 +40,14 @@ import com.subtracks.data.model.Source
|
||||
import com.subtracks.data.model.coverArtKey
|
||||
import com.subtracks.data.prefs.ListQuery
|
||||
import com.subtracks.data.repo.SEARCH_RESULT_LIMIT
|
||||
import com.subtracks.data.repo.normalizeBiography
|
||||
import com.subtracks.data.source.subsonic.SubsonicSource
|
||||
import com.subtracks.data.source.subsonic.TestServer
|
||||
import com.subtracks.data.source.subsonic.TestServers
|
||||
import com.subtracks.data.sync.SyncService
|
||||
import com.subtracks.playback.PlaybackState
|
||||
import com.subtracks.playback.QueueItem
|
||||
import com.subtracks.ui.components.ArtistArt
|
||||
import com.subtracks.ui.home.HomeFeed
|
||||
import com.subtracks.ui.home.HomeScreen
|
||||
import com.subtracks.ui.library.AlbumDetailScreen
|
||||
@@ -82,11 +84,15 @@ import javax.imageio.ImageIO
|
||||
* by `:*UnitTest` or the integration suite; run it explicitly with `gradle :app:demoScreenshots`.
|
||||
* The synced database is rebuilt each run; downloaded cover art is cached under the system temp dir
|
||||
* so reruns do not re-fetch it.
|
||||
*
|
||||
* The device is a 393x786 dp phone at 440 dpi, which renders 1080x2161. A capture one pixel over
|
||||
* 1080x2160 breaks the stores' 2:1 limit, so the captures are cropped to 1080x2160 afterwards, with
|
||||
* the last row taken from the row above it.
|
||||
*/
|
||||
@OptIn(DelicateCoilApi::class)
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
@GraphicsMode(GraphicsMode.Mode.NATIVE)
|
||||
@Config(qualifiers = RobolectricDeviceQualifiers.PixelXL)
|
||||
@Config(qualifiers = "w393dp-h786dp-normal-long-notround-any-440dpi-keyshidden-nonav")
|
||||
class DemoScreenshotTest {
|
||||
@get:Rule
|
||||
val composeRule = createAndroidComposeRule<ComponentActivity>()
|
||||
@@ -123,6 +129,7 @@ class DemoScreenshotTest {
|
||||
HomeScreen(
|
||||
feed = demo.feed,
|
||||
coverArt = demo.coverArt,
|
||||
artistArt = demo::artistArt,
|
||||
playingSongId = demo.homePlayingSong.id,
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
)
|
||||
@@ -178,6 +185,7 @@ class DemoScreenshotTest {
|
||||
ArtistDetailScreen(
|
||||
artist = demo.artist,
|
||||
artistName = demo.artist.name,
|
||||
bio = demo.artistBio,
|
||||
albums =
|
||||
ArtistAlbums(
|
||||
albumCredits =
|
||||
@@ -216,6 +224,7 @@ class DemoScreenshotTest {
|
||||
onSync = {},
|
||||
onOpenSettings = {},
|
||||
artwork = demo.artwork(demo.nowPlayingAlbum.coverArt),
|
||||
artistArt = demo::artistArt,
|
||||
modifier = Modifier.weight(1f),
|
||||
)
|
||||
demo.MiniPlayerOverlay(demo.nowPlayingSong, demo.nowPlayingAlbum, progressInset = true)
|
||||
@@ -228,6 +237,7 @@ class DemoScreenshotTest {
|
||||
onQueryChange = {},
|
||||
results = demo.search,
|
||||
coverArt = demo.coverArt,
|
||||
artistArt = demo::artistArt,
|
||||
playingSongId = demo.nowPlayingSong.id,
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
)
|
||||
@@ -288,7 +298,7 @@ class DemoScreenshotTest {
|
||||
|
||||
private fun reencodeOpaqueRgb(file: File) {
|
||||
val decoded = ImageIO.read(file)
|
||||
val rgb = BufferedImage(decoded.width, decoded.height, BufferedImage.TYPE_INT_RGB)
|
||||
val rgb = BufferedImage(decoded.width, decoded.width * 2, BufferedImage.TYPE_INT_RGB)
|
||||
rgb.createGraphics().run {
|
||||
drawImage(decoded, 0, 0, null)
|
||||
dispose()
|
||||
@@ -329,6 +339,7 @@ class DemoScreenshotTest {
|
||||
val homePlayingAlbum: Album,
|
||||
val homePlayingSong: Song,
|
||||
val artist: Artist,
|
||||
val artistBio: String?,
|
||||
val artistAlbums: List<Album>,
|
||||
val search: SearchResults,
|
||||
val coverArt: (String?, Boolean) -> CoverArtRef?,
|
||||
@@ -371,6 +382,15 @@ class DemoScreenshotTest {
|
||||
return artworkColorsFromSeeds(primary?.rgb ?: FALLBACK_SEED, null)
|
||||
}
|
||||
|
||||
fun artistArt(artist: Artist): ArtistArt {
|
||||
val thumbnail = artist.coverArt?.let { coverArt(it, true) }
|
||||
return ArtistArt(
|
||||
image = thumbnail,
|
||||
thumbnail = thumbnail,
|
||||
covers = artist.coverArts.take(4).mapNotNull { coverArt(it, true) },
|
||||
)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val FALLBACK_SEED = 0xFF3A7BD5.toInt()
|
||||
private const val DEMO_BASE = "https://demo.navidrome.org/"
|
||||
@@ -417,9 +437,11 @@ class DemoScreenshotTest {
|
||||
val homePlayingAlbum = allAlbums.firstOrNull { it.name == "Shaking The Habitual" } ?: allAlbums.last()
|
||||
val homePlayingSong = songsOf(homePlayingAlbum).first()
|
||||
val artist =
|
||||
allArtists.firstOrNull { it.name == "Ugress" }
|
||||
allArtists.firstOrNull { it.name == "Nine Inch Nails" }
|
||||
?: allArtists.firstOrNull { it.name == "pornophonique" }
|
||||
?: allArtists.maxByOrNull { it.albumCount }
|
||||
?: error("demo library has no artists")
|
||||
val artistBio = normalizeBiography(source.artistBiography(artist.id))
|
||||
val artistAlbums = dao.albumsForArtist(1, artist.id).first()
|
||||
|
||||
val coverArtIds =
|
||||
@@ -509,6 +531,7 @@ class DemoScreenshotTest {
|
||||
homePlayingAlbum = homePlayingAlbum,
|
||||
homePlayingSong = homePlayingSong,
|
||||
artist = artist,
|
||||
artistBio = artistBio,
|
||||
artistAlbums = artistAlbums.filter { it.coverArt != null },
|
||||
search = search,
|
||||
coverArt = coverArt,
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
## What's new
|
||||
|
||||
- ReplayGain: normalizes loudness from the tags your server reports. Auto, the default, uses album gain for album queues and track gain for playlists, singles and shuffles. Playback settings also offer track gain, album gain, off and a preamp
|
||||
- Artist pages show an expandable biography, artwork from the server or built from the artist's albums, and separate sections for album credits and appearances
|
||||
- Sort lists can use regular names (the default), sort names, or sort names only for names without Latin letters
|
||||
- Improved handling for albums and tracks with multiple artists, including their names in song subtitles, the queue and info dialogs
|
||||
- Info dialogs in album, artist and playlist menus, listing credited artists, sort names, owner and sharing, and play and download details
|
||||
- Keep the screen on while now playing is open setting, off by default (thanks @chiangww!)
|
||||
|
||||
## Fixed
|
||||
|
||||
- Album and artist scroll positions survive navigating away and back
|
||||
- Search keeps its query and focus when you return
|
||||
- The library tab strip scrolls, so the settings gear is never squeezed out
|
||||
- Notification progress for streams of unknown length, such as Opus/Ogg transcodes
|
||||
- Album grid no longer shifts covers after a scrobble reloads the list
|
||||
- Now playing screen: artwork wraps with repeat, and cover shadows are no longer clipped
|
||||
|
||||
## Performance
|
||||
|
||||
- Faster library sync on servers without `search3`
|
||||
- Artist lists and downloads stay fast on large libraries
|
||||
|
||||
**Full changelog:** https://github.com/austinried/subtracks/compare/v3.0.0...v3.1.0
|
||||
@@ -0,0 +1,12 @@
|
||||
- ReplayGain: loudness from server tags, with Auto, track, album, off, preamp
|
||||
- Artist pages: bio, art, credits and appearances
|
||||
- Sort lists: regular names or sort names
|
||||
- Improved handling for multi-artist albums/tracks
|
||||
- Keep the screen on while now playing is open setting, off by default (thanks @chiangww!)
|
||||
|
||||
Fixed
|
||||
|
||||
- Notification progress for unknown-length streams (Opus/Ogg)
|
||||
- Now playing artwork and shadow fixes
|
||||
|
||||
Release notes: https://github.com/austinried/subtracks/releases/tag/v3.1.0
|
||||
|
Before Width: | Height: | Size: 1.3 MiB After Width: | Height: | Size: 1.4 MiB |
|
Before Width: | Height: | Size: 513 KiB After Width: | Height: | Size: 577 KiB |
|
Before Width: | Height: | Size: 2.1 MiB After Width: | Height: | Size: 3.0 MiB |
|
Before Width: | Height: | Size: 1.1 MiB After Width: | Height: | Size: 1.2 MiB |
|
Before Width: | Height: | Size: 3.2 MiB After Width: | Height: | Size: 2.6 MiB |
|
Before Width: | Height: | Size: 469 KiB After Width: | Height: | Size: 421 KiB |
|
Before Width: | Height: | Size: 410 KiB After Width: | Height: | Size: 371 KiB |