feat: enhance Android build configuration with stable/nightly flavors and update launcher icons
38
.github/workflows/spotube-release-binary.yml
vendored
@ -141,11 +141,16 @@ jobs:
|
||||
EOF
|
||||
|
||||
- name: Assemble Release
|
||||
run: ./gradlew :composeApp:assembleRelease
|
||||
run: |
|
||||
if [ "${{ inputs.release_type }}" = "nightly" ]; then
|
||||
./gradlew :composeApp:assembleNightlyRelease -Pnightly=true -PbuildNumber=${{ github.run_number }}
|
||||
else
|
||||
./gradlew :composeApp:assembleStableRelease
|
||||
fi
|
||||
|
||||
- name: Rename APK
|
||||
run: |
|
||||
APK_FILE=$(find composeApp/build/outputs/apk/release -name "*.apk" -type f | head -1)
|
||||
APK_FILE=$(find composeApp/build/outputs/apk -path "*elease*" -name "*.apk" -type f | head -1)
|
||||
cp "$APK_FILE" Spotube-android-all-arch.apk
|
||||
|
||||
- name: Upload APK
|
||||
@ -194,7 +199,7 @@ jobs:
|
||||
run: chmod +x gradlew
|
||||
|
||||
- name: Package Linux
|
||||
run: ./gradlew :composeApp:packageReleaseDeb :composeApp:packageReleaseRpm
|
||||
run: ./gradlew :composeApp:packageReleaseDeb :composeApp:packageReleaseRpm -Pnightly=${{ inputs.release_type == 'nightly' }} -PbuildNumber=${{ github.run_number }}
|
||||
|
||||
- name: Rename Linux artifacts
|
||||
run: |
|
||||
@ -255,7 +260,7 @@ jobs:
|
||||
run: chmod +x gradlew
|
||||
|
||||
- name: Package Linux
|
||||
run: ./gradlew :composeApp:packageReleaseDeb :composeApp:packageReleaseRpm
|
||||
run: ./gradlew :composeApp:packageReleaseDeb :composeApp:packageReleaseRpm -Pnightly=${{ inputs.release_type == 'nightly' }} -PbuildNumber=${{ github.run_number }}
|
||||
|
||||
- name: Rename Linux artifacts
|
||||
run: |
|
||||
@ -308,7 +313,7 @@ jobs:
|
||||
uses: gradle/actions/setup-gradle@v4
|
||||
|
||||
- name: Package EXE
|
||||
run: ./gradlew :composeApp:packageReleaseExe
|
||||
run: ./gradlew :composeApp:packageReleaseExe -Pnightly=${{ inputs.release_type == 'nightly' }} -PbuildNumber=${{ github.run_number }}
|
||||
|
||||
- name: Rename Windows artifacts
|
||||
shell: pwsh
|
||||
@ -357,7 +362,7 @@ jobs:
|
||||
run: chmod +x gradlew
|
||||
|
||||
- name: Package DMG
|
||||
run: ./gradlew :composeApp:packageReleaseDmg
|
||||
run: ./gradlew :composeApp:packageReleaseDmg -Pnightly=${{ inputs.release_type == 'nightly' }} -PbuildNumber=${{ github.run_number }}
|
||||
|
||||
- name: Rename macOS artifact
|
||||
run: |
|
||||
@ -389,18 +394,35 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
# The build resolves its plugins from mavenLocal, so Gradle needs the same setup as the build jobs
|
||||
- name: Download maven local
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: maven-local
|
||||
path: ~/.m2/repository/
|
||||
|
||||
- name: Set up JDK 21
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: 21
|
||||
|
||||
- name: Set up Gradle
|
||||
uses: gradle/actions/setup-gradle@v4
|
||||
|
||||
- name: Set release variables
|
||||
id: vars
|
||||
run: |
|
||||
if [ "${{ inputs.release_type }}" = "stable" ]; then
|
||||
VERSION=$(grep 'versionName' composeApp/build.gradle.kts | head -1 | sed 's/.*"\(.*\)".*/\1/')
|
||||
chmod +x gradlew
|
||||
VERSION=$(./gradlew -q :composeApp:printVersion)
|
||||
TAG="v${VERSION}"
|
||||
echo "is_prerelease=false" >> "$GITHUB_OUTPUT"
|
||||
echo "is_draft=true" >> "$GITHUB_OUTPUT"
|
||||
echo "release_name=Spotube ${TAG}" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
TAG="nightly"
|
||||
VERSION="$(date +%Y-%m-%d)"
|
||||
VERSION="$(date +%Y-%m-%d)-${{ github.run_number }}"
|
||||
echo "is_prerelease=true" >> "$GITHUB_OUTPUT"
|
||||
echo "is_draft=false" >> "$GITHUB_OUTPUT"
|
||||
echo "release_name=Nightly ${VERSION}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
@ -12,7 +12,7 @@
|
||||
## High-value commands
|
||||
- Use Gradle wrapper (`./gradlew` or `./gradlew.bat`) only.
|
||||
- Run desktop: `:composeApp:run`
|
||||
- Build Android debug: `:composeApp:assembleDebug`
|
||||
- Build Android debug: `:composeApp:assembleStableDebug` (or `assembleNightlyDebug`). Android has `stable`/`nightly` product flavors (nightly = beta channel: `.nightly` applicationId suffix, own icons/name in `composeApp/src/nightly/res`). Desktop icons come from `composeApp/icons/{stable,nightly}`, chosen with `-Pnightly=true`.
|
||||
- Module checks: `:composeApp:check`, `:plugin_interfaces:check`, `:js_plugin_example:check`
|
||||
- Focused tests: `:composeApp:jvmTest`, `:composeApp:iosSimulatorArm64Test`, `:plugin_interfaces:jvmTest`, `:plugin_interfaces:jsTest`, `:js_plugin_example:jsTest`
|
||||
- No lint/typecheck/formatter tasks are configured; `:composeApp:check` is the only aggregated check.
|
||||
@ -70,6 +70,6 @@
|
||||
- **Adaptive Components:** Use `AdaptiveDropdownBottomSheet` or `AdaptiveDialogBottomSheet` inside `commonMain` for adaptive layouts[cite: 2]. Do NOT create platform-specific `expect/actual` variants unless platform-native APIs (like windowing or native scrollbars) are explicitly required[cite: 2].
|
||||
|
||||
### 3. Verification & Diagnostic Loops
|
||||
- **Post-Edit Verification:** After editing Kotlin code, validate compilation by running `./gradlew :composeApp:check` or `./gradlew :composeApp:assembleDebug` via MCP[cite: 2].
|
||||
- **Post-Edit Verification:** After editing Kotlin code, validate compilation by running `./gradlew :composeApp:check` or `./gradlew :composeApp:assembleStableDebug` via MCP[cite: 2].
|
||||
- **Dispatcher Integrity:** Never remove or bypass `SwingMainDispatcherFactory` in `composeApp/src/jvmMain/.../core/coroutines/`[cite: 2]. It is required to prevent `compose-webview` from hijacking `Dispatchers.Main` away from the Swing EDT[cite: 2].
|
||||
- **Live Preview Iteration:** Use `compose-hot-reload` to evaluate UI layout changes headlessly without forcing full Gradle rebuilds.
|
||||
|
||||
@ -43,12 +43,24 @@ plugins {
|
||||
val bundleId = "org.spotube.spotube"
|
||||
val version = "6.0.0"
|
||||
val buildNumber = 46
|
||||
// Desktop icon channel; Android picks its icons via the stable/nightly product flavor. Use -Pnightly=true.
|
||||
val isNightly = providers.gradleProperty("nightly").map { it.toBoolean() }.getOrElse(false)
|
||||
val desktopIconDir = if (isNightly) "icons/nightly" else "icons/stable"
|
||||
// Nightlies are numbered by the CI run (-PbuildNumber=<run>, or GITHUB_RUN_NUMBER) so every build is traceable to its run.
|
||||
val nightlyBuildNumber = providers.gradleProperty("buildNumber").orNull?.toIntOrNull()
|
||||
?: System.getenv("GITHUB_RUN_NUMBER")?.toIntOrNull()
|
||||
?: buildNumber
|
||||
val targetName = "Spotube"
|
||||
val homePageUrl = "https://spotube.org"
|
||||
val repositoryUrl = "https://github.com/team-spotube/spotube"
|
||||
val appVersionName = version
|
||||
val appBuildNumber = buildNumber
|
||||
val appBuildNumber = if (isNightly) nightlyBuildNumber else buildNumber
|
||||
val appRepositoryUrl = repositoryUrl
|
||||
// CI reads the release version from here instead of scraping this file.
|
||||
tasks.register("printVersion") {
|
||||
val appVersion = appVersionName
|
||||
doLast { println(appVersion) }
|
||||
}
|
||||
val githubRepositoryPath = repositoryUrl.removePrefix("https://github.com/")
|
||||
val latestReleaseApiUrl = "https://api.github.com/repos/$githubRepositoryPath/releases/latest"
|
||||
val generatedAppInfoDir = layout.buildDirectory.dir("generated/appBuildInfo/commonMain/kotlin")
|
||||
@ -297,6 +309,19 @@ android {
|
||||
versionCode = buildNumber
|
||||
versionName = version
|
||||
}
|
||||
// stable = production channel, nightly = beta channel (own icon, name and installable side by side)
|
||||
flavorDimensions += "channel"
|
||||
productFlavors {
|
||||
create("stable") {
|
||||
dimension = "channel"
|
||||
}
|
||||
create("nightly") {
|
||||
dimension = "channel"
|
||||
applicationIdSuffix = ".nightly"
|
||||
versionCode = nightlyBuildNumber
|
||||
versionNameSuffix = "-nightly.$nightlyBuildNumber"
|
||||
}
|
||||
}
|
||||
packaging {
|
||||
resources {
|
||||
excludes += "/META-INF/{AL2.0,LGPL2.1}"
|
||||
@ -382,11 +407,13 @@ nucleus.application {
|
||||
homepage = homePageUrl
|
||||
|
||||
linux {
|
||||
iconFile.set(project.file("$desktopIconDir/icon.png"))
|
||||
modules("jdk.security.auth")
|
||||
debMaintainer = "Team Spotube <team.spotube@proton.me>"
|
||||
rpmLicenseType = "AGPL-3.0-or-later"
|
||||
}
|
||||
windows {
|
||||
iconFile.set(project.file("$desktopIconDir/icon.ico"))
|
||||
shortcut = true
|
||||
menu = true
|
||||
dirChooser = true
|
||||
@ -394,6 +421,7 @@ nucleus.application {
|
||||
}
|
||||
|
||||
macOS {
|
||||
iconFile.set(project.file("$desktopIconDir/icon.icns"))
|
||||
notarization {
|
||||
|
||||
}
|
||||
|
||||
BIN
composeApp/icons/nightly/icon.icns
Normal file
BIN
composeApp/icons/nightly/icon.ico
Normal file
|
After Width: | Height: | Size: 364 KiB |
BIN
composeApp/icons/nightly/icon.png
Normal file
|
After Width: | Height: | Size: 117 KiB |
BIN
composeApp/icons/stable/icon.icns
Normal file
BIN
composeApp/icons/stable/icon.ico
Normal file
|
After Width: | Height: | Size: 364 KiB |
BIN
composeApp/icons/stable/icon.png
Normal file
|
After Width: | Height: | Size: 114 KiB |
BIN
composeApp/src/androidMain/ic_launcher-playstore.png
Normal file
|
After Width: | Height: | Size: 64 KiB |
@ -29,6 +29,7 @@ import androidx.core.app.NotificationCompat
|
||||
import androidx.media3.common.MediaItem as Media3MediaItem
|
||||
import androidx.media3.common.Player
|
||||
import androidx.media3.common.util.UnstableApi
|
||||
import androidx.media3.session.DefaultMediaNotificationProvider
|
||||
import androidx.media3.session.LibraryResult
|
||||
import androidx.media3.session.MediaLibraryService
|
||||
import androidx.media3.session.MediaLibraryService.LibraryParams
|
||||
@ -41,6 +42,7 @@ import com.google.common.util.concurrent.Futures
|
||||
import com.google.common.util.concurrent.ListenableFuture
|
||||
import com.google.common.util.concurrent.SettableFuture
|
||||
import org.spotube.spotube.MainActivity
|
||||
import org.spotube.spotube.R
|
||||
import org.spotube.spotube.core.audioplayer.AudioPlayer
|
||||
import org.spotube.spotube.core.audioplayer.AudioPlayerInterface
|
||||
import org.spotube.spotube.core.audioplayer.AudioPlayerQueue
|
||||
@ -75,6 +77,16 @@ class PlaybackService : MediaLibraryService(), KoinComponent {
|
||||
|
||||
createNotificationChannel()
|
||||
|
||||
// Media3 posts the real playback notification; point it at our channel,
|
||||
// ID and monochrome icon so it replaces the placeholder below in place.
|
||||
setMediaNotificationProvider(
|
||||
DefaultMediaNotificationProvider.Builder(this)
|
||||
.setChannelId(CHANNEL_ID)
|
||||
.setNotificationId(NOTIFICATION_ID)
|
||||
.build()
|
||||
.apply { setSmallIcon(R.drawable.ic_stat_spotube) }
|
||||
)
|
||||
|
||||
val sessionActivity = PendingIntent.getActivity(
|
||||
this,
|
||||
0,
|
||||
@ -87,7 +99,7 @@ class PlaybackService : MediaLibraryService(), KoinComponent {
|
||||
val notification = NotificationCompat.Builder(this, CHANNEL_ID)
|
||||
.setContentTitle("Spotube")
|
||||
.setContentText(runBlocking { getString(Res.string.notification_playback_service_text) })
|
||||
.setSmallIcon(android.R.drawable.ic_media_play)
|
||||
.setSmallIcon(R.drawable.ic_stat_spotube)
|
||||
.setContentIntent(sessionActivity)
|
||||
.setOngoing(true)
|
||||
.build()
|
||||
|
||||
BIN
composeApp/src/androidMain/res/drawable-hdpi/ic_stat_spotube.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
composeApp/src/androidMain/res/drawable-mdpi/ic_stat_spotube.png
Normal file
|
After Width: | Height: | Size: 737 B |
@ -1,47 +0,0 @@
|
||||
<!--
|
||||
~ Copyright (C) 2026 Kingkor Roy Tirtho and Spotube Contributors
|
||||
~
|
||||
~ This program is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU Affero General Public License as published by
|
||||
~ the Free Software Foundation, either version 3 of the License, or
|
||||
~ (at your option) any later version.
|
||||
~
|
||||
~ This program is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU Affero General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU Affero General Public License
|
||||
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:aapt="http://schemas.android.com/aapt"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
<path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:endX="85.84757"
|
||||
android:endY="92.4963"
|
||||
android:startX="42.9492"
|
||||
android:startY="49.59793"
|
||||
android:type="linear">
|
||||
<item
|
||||
android:color="#44000000"
|
||||
android:offset="0.0" />
|
||||
<item
|
||||
android:color="#00000000"
|
||||
android:offset="1.0" />
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:fillColor="#FFFFFF"
|
||||
android:fillType="nonZero"
|
||||
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
|
||||
android:strokeWidth="1"
|
||||
android:strokeColor="#00000000" />
|
||||
</vector>
|
||||
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 2.8 KiB |
|
After Width: | Height: | Size: 4.0 KiB |
@ -1,187 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (C) 2026 Kingkor Roy Tirtho and Spotube Contributors
|
||||
~
|
||||
~ This program is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU Affero General Public License as published by
|
||||
~ the Free Software Foundation, either version 3 of the License, or
|
||||
~ (at your option) any later version.
|
||||
~
|
||||
~ This program is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU Affero General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU Affero General Public License
|
||||
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
<path
|
||||
android:fillColor="#3DDC84"
|
||||
android:pathData="M0,0h108v108h-108z" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M9,0L9,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,0L19,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M29,0L29,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M39,0L39,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M49,0L49,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M59,0L59,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M69,0L69,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M79,0L79,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M89,0L89,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M99,0L99,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,9L108,9"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,19L108,19"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,29L108,29"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,39L108,39"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,49L108,49"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,59L108,59"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,69L108,69"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,79L108,79"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,89L108,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,99L108,99"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,29L89,29"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,39L89,39"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,49L89,49"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,59L89,59"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,69L89,69"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,79L89,79"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M29,19L29,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M39,19L39,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M49,19L49,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M59,19L59,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M69,19L69,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M79,19L79,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
</vector>
|
||||
@ -17,6 +17,7 @@
|
||||
-->
|
||||
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/ic_launcher_background" />
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||
<background android:drawable="@color/ic_launcher_background" />
|
||||
<foreground android:drawable="@mipmap/ic_launcher_foreground" />
|
||||
<monochrome android:drawable="@mipmap/ic_launcher_monochrome" />
|
||||
</adaptive-icon>
|
||||
@ -17,6 +17,7 @@
|
||||
-->
|
||||
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/ic_launcher_background" />
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||
<background android:drawable="@color/ic_launcher_background" />
|
||||
<foreground android:drawable="@mipmap/ic_launcher_foreground" />
|
||||
<monochrome android:drawable="@mipmap/ic_launcher_monochrome" />
|
||||
</adaptive-icon>
|
||||
|
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.5 KiB |
|
After Width: | Height: | Size: 7.0 KiB |
|
After Width: | Height: | Size: 4.1 KiB |
|
Before Width: | Height: | Size: 5.2 KiB After Width: | Height: | Size: 5.3 KiB |
|
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 3.7 KiB |
|
After Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 3.0 KiB |
|
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 5.5 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 6.1 KiB |
|
Before Width: | Height: | Size: 7.3 KiB After Width: | Height: | Size: 8.0 KiB |
|
Before Width: | Height: | Size: 7.7 KiB After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 36 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 23 KiB |
21
composeApp/src/androidMain/res/values/colors.xml
Normal file
@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (C) 2026 Kingkor Roy Tirtho and Spotube Contributors
|
||||
~
|
||||
~ This program is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU Affero General Public License as published by
|
||||
~ the Free Software Foundation, either version 3 of the License, or
|
||||
~ (at your option) any later version.
|
||||
~
|
||||
~ This program is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU Affero General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU Affero General Public License
|
||||
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<resources>
|
||||
<color name="ic_launcher_background">#000000</color>
|
||||
</resources>
|
||||
@ -907,6 +907,7 @@
|
||||
<string name="plugin_discover_badge_verified">Verified</string>
|
||||
<string name="plugin_discover_stars_content_description">GitHub stars</string>
|
||||
<string name="plugin_discover_repository_content_description">GitHub repository URL</string>
|
||||
<string name="plugin_discover_repository_host">github.com</string>
|
||||
|
||||
<!-- Plugin release install dialog -->
|
||||
<string name="plugin_install_no_releases">No releases found</string>
|
||||
|
||||
@ -40,6 +40,8 @@ import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.ExperimentalLayoutApi
|
||||
import androidx.compose.foundation.layout.FlowRow
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
@ -134,6 +136,7 @@ import spotube.composeapp.generated.resources.Res
|
||||
import spotube.composeapp.generated.resources.plugin_discover_badge_official
|
||||
import spotube.composeapp.generated.resources.plugin_discover_badge_verified
|
||||
import spotube.composeapp.generated.resources.plugin_discover_repository_content_description
|
||||
import spotube.composeapp.generated.resources.plugin_discover_repository_host
|
||||
import spotube.composeapp.generated.resources.plugin_discover_stars_content_description
|
||||
import spotube.composeapp.generated.resources.plugin_info_abilities
|
||||
import spotube.composeapp.generated.resources.plugin_info_api_version
|
||||
@ -848,6 +851,7 @@ private fun SupportDialog(
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalLayoutApi::class)
|
||||
@Composable
|
||||
private fun DiscoverRepoCard(
|
||||
repo: GitHubRepo,
|
||||
@ -952,9 +956,10 @@ private fun DiscoverRepoCard(
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
}
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||
FlowRow(
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(4.dp),
|
||||
itemVerticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Text(
|
||||
repo.owner.login,
|
||||
@ -997,9 +1002,10 @@ private fun DiscoverRepoCard(
|
||||
tint = AppTheme.colors.onSurfaceVariant
|
||||
)
|
||||
Text(
|
||||
"github.com",
|
||||
stringResource(Res.string.plugin_discover_repository_host),
|
||||
style = AppTheme.typography.labelSmall,
|
||||
color = AppTheme.colors.onSurfaceVariant
|
||||
color = AppTheme.colors.onSurfaceVariant,
|
||||
singleLine = true
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -32,6 +32,9 @@ import androidx.compose.ui.unit.isSpecified
|
||||
import androidx.compose.ui.window.WindowPlacement
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.window.rememberWindowState
|
||||
import org.jetbrains.compose.resources.painterResource
|
||||
import spotube.composeapp.generated.resources.Res
|
||||
import spotube.composeapp.generated.resources.spotube_logo
|
||||
import dev.nucleusframework.application.DecoratedWindow
|
||||
import dev.nucleusframework.application.NucleusBackend
|
||||
import dev.nucleusframework.application.nucleusApplication
|
||||
@ -200,6 +203,7 @@ fun main() {
|
||||
}
|
||||
},
|
||||
title = "Spotube",
|
||||
icon = painterResource(Res.drawable.spotube_logo),
|
||||
visible = isWindowVisible
|
||||
) {
|
||||
CompositionLocalProvider(
|
||||
|
||||
BIN
composeApp/src/nightly/res/mipmap-hdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 4.0 KiB |
|
After Width: | Height: | Size: 8.1 KiB |
|
After Width: | Height: | Size: 4.9 KiB |
BIN
composeApp/src/nightly/res/mipmap-hdpi/ic_launcher_round.png
Normal file
|
After Width: | Height: | Size: 4.8 KiB |
BIN
composeApp/src/nightly/res/mipmap-mdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 4.2 KiB |
|
After Width: | Height: | Size: 2.7 KiB |
BIN
composeApp/src/nightly/res/mipmap-mdpi/ic_launcher_round.png
Normal file
|
After Width: | Height: | Size: 2.7 KiB |
BIN
composeApp/src/nightly/res/mipmap-xhdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 6.1 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 7.3 KiB |
BIN
composeApp/src/nightly/res/mipmap-xhdpi/ic_launcher_round.png
Normal file
|
After Width: | Height: | Size: 7.4 KiB |
BIN
composeApp/src/nightly/res/mipmap-xxhdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 13 KiB |
BIN
composeApp/src/nightly/res/mipmap-xxhdpi/ic_launcher_round.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
composeApp/src/nightly/res/mipmap-xxxhdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 46 KiB |
|
After Width: | Height: | Size: 20 KiB |
BIN
composeApp/src/nightly/res/mipmap-xxxhdpi/ic_launcher_round.png
Normal file
|
After Width: | Height: | Size: 22 KiB |
21
composeApp/src/nightly/res/values/strings.xml
Normal file
@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (C) 2026 Kingkor Roy Tirtho and Spotube Contributors
|
||||
~
|
||||
~ This program is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU Affero General Public License as published by
|
||||
~ the Free Software Foundation, either version 3 of the License, or
|
||||
~ (at your option) any later version.
|
||||
~
|
||||
~ This program is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU Affero General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU Affero General Public License
|
||||
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<resources>
|
||||
<string name="app_name">Spotube Nightly</string>
|
||||
</resources>
|
||||
@ -15,7 +15,8 @@
|
||||
],
|
||||
"idiom" : "universal",
|
||||
"platform" : "ios",
|
||||
"size" : "1024x1024"
|
||||
"size" : "1024x1024",
|
||||
"filename" : "app-icon-1024-dark.png"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
@ -26,7 +27,8 @@
|
||||
],
|
||||
"idiom" : "universal",
|
||||
"platform" : "ios",
|
||||
"size" : "1024x1024"
|
||||
"size" : "1024x1024",
|
||||
"filename" : "app-icon-1024-tinted.png"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
|
||||
|
After Width: | Height: | Size: 179 KiB |
|
After Width: | Height: | Size: 86 KiB |
|
Before Width: | Height: | Size: 66 KiB After Width: | Height: | Size: 179 KiB |