Deleted NewPipedExtractor

This commit is contained in:
Alessio 2025-04-14 10:20:38 +02:00
parent fece073def
commit ae925d7624
185 changed files with 274 additions and 1390 deletions

View File

@ -4,7 +4,7 @@
# This file should be version controlled and should not be manually edited.
version:
revision: "300451adae589accbece3490f4396f10bdf15e6e"
revision: "c23637390482d4cf9598c3ce3f2be31aa7332daf"
channel: "stable"
project_type: app
@ -13,11 +13,11 @@ project_type: app
migration:
platforms:
- platform: root
create_revision: 300451adae589accbece3490f4396f10bdf15e6e
base_revision: 300451adae589accbece3490f4396f10bdf15e6e
- platform: windows
create_revision: 300451adae589accbece3490f4396f10bdf15e6e
base_revision: 300451adae589accbece3490f4396f10bdf15e6e
create_revision: c23637390482d4cf9598c3ce3f2be31aa7332daf
base_revision: c23637390482d4cf9598c3ce3f2be31aa7332daf
- platform: android
create_revision: c23637390482d4cf9598c3ce3f2be31aa7332daf
base_revision: c23637390482d4cf9598c3ce3f2be31aa7332daf
# User provided section

View File

@ -138,11 +138,11 @@ Do the following:
- Create a `.env` in root of the project following the `.env.example` template
- Now run the following to bootstrap the project
```bash
flutter pub get && dart run build_runner build --delete-conflicting-outputs --enable-experiment=records,patterns
flutter pub get && flutter pub run build_runner build --delete-conflicting-outputs --enable-experiment=records,patterns
```
- Finally run these following commands in the root of the project to start the Spotube Locally
```bash
flutter run -d <window|macos|linux|(<android-device-id>)>
flutter run -d [window|macos|linux|android-id]
```
Do debugging/testing/build etc then submit to us with PR against the development branch (dev) & we'll review your code

View File

@ -13,10 +13,10 @@ tar:
&& tar -cJf build/spotube-linux-${VERSION}-${PKG_ARCH}.tar.xz -C $(TEMP_DIR) .\
&& rm -rf $(TEMP_DIR)
aursrcinfo:
aurInfo:
docker run -e EXPORT_SRC=1 -v ${PWD}/aur-struct:/pkg -v ${MIRRORLIST}:/etc/pacman.d/mirrorlist:ro whynothugo/makepkg
publishaur:
aurPublish:
echo '[Warning!]: you need SSH paired with AUR'\
&& rm -rf build/spotube\
&& git clone ssh://aur@aur.archlinux.org/spotube-bin.git build/spotube\
@ -26,7 +26,7 @@ publishaur:
&& git commit -m "${MSG}"\
&& git push
innoinstall:
innoInstall:
powershell curl -o build\installer.exe http://files.jrsoftware.org/is/6/innosetup-${INNO_VERSION}.exe
powershell git clone https://github.com/DomGries/InnoDependencyInstaller.git build\inno-depend
powershell build\installer.exe /verysilent /allusers /dir=build\iscc
@ -41,7 +41,7 @@ choco:
apk:
mv build/app/outputs/apk/release/app-release.apk build/Spotube-android-all-arch.apk
gensums:
genSums:
sh -c scripts/gensums.sh
migrate:

3
android/.gitignore vendored
View File

@ -5,9 +5,10 @@ gradle-wrapper.jar
/gradlew.bat
/local.properties
GeneratedPluginRegistrant.java
.cxx/
# Remember to never publicly share your keystore.
# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app
# See https://flutter.dev/to/reference-keystore
key.properties
**/*.keystore
**/*.jks

View File

@ -1,139 +0,0 @@
plugins {
id "com.android.application"
id "kotlin-android"
id "dev.flutter.flutter-gradle-plugin"
}
def localProperties = new Properties()
def localPropertiesFile = rootProject.file('local.properties')
if (localPropertiesFile.exists()) {
localPropertiesFile.withReader('UTF-8') { reader ->
localProperties.load(reader)
}
}
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
if (flutterVersionCode == null) {
flutterVersionCode = '1'
}
def flutterVersionName = localProperties.getProperty('flutter.versionName')
if (flutterVersionName == null) {
flutterVersionName = '1.0'
}
def keystoreProperties = new Properties()
def keystorePropertiesFile = rootProject.file('key.properties')
if (keystorePropertiesFile.exists()) {
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
}
def composeVersion = "1.4.8"
android {
namespace "oss.krtirtho.spotube"
compileSdkVersion 35
ndkVersion = "27.0.12077973"
compileOptions {
coreLibraryDesugaringEnabled true
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
sourceSets {
main.java.srcDirs += 'src/main/kotlin'
}
buildFeatures {
compose true
}
composeOptions {
kotlinCompilerExtensionVersion "$composeVersion" // Correlates with org.jetbrains.kotlin.android plugin in settings.gradle
}
defaultConfig {
applicationId "oss.krtirtho.spotube"
minSdkVersion 24
targetSdkVersion 35
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
multiDexEnabled true
}
signingConfigs {
release {
keyAlias keystoreProperties['keyAlias']
keyPassword keystoreProperties['keyPassword']
storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null
storePassword keystoreProperties['storePassword']
}
}
buildTypes {
release {
signingConfig signingConfigs.release
}
debug {
signingConfig signingConfigs.release
}
}
flavorDimensions "default"
productFlavors {
nightly {
dimension "default"
resValue "string", "app_name_en", "Spotube Nightly"
applicationIdSuffix ".nightly"
versionNameSuffix "-nightly"
signingConfig signingConfigs.release
}
dev {
dimension "default"
resValue "string", "app_name_en", "Spotube Dev"
applicationIdSuffix ".dev"
versionNameSuffix "-dev"
signingConfig signingConfigs.release
}
stable {
dimension "default"
resValue "string", "app_name_en", "Spotube"
signingConfig signingConfigs.release
}
}
packagingOptions {
resources.excludes += "DebugProbesKt.bin"
}
}
flutter {
source '../..'
}
def glanceVersion = "1.1.1"
dependencies {
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.1.4'
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1'
// other deps so just ignore
implementation 'com.android.support:multidex:2.0.1'
implementation "androidx.glance:glance-appwidget:$glanceVersion"
implementation "androidx.glance:glance-appwidget-preview:$glanceVersion"
implementation "androidx.glance:glance-preview:$glanceVersion"
implementation "androidx.glance:glance-material3:$glanceVersion"
implementation "androidx.glance:glance-material:$glanceVersion"
implementation "androidx.work:work-runtime-ktx:2.8.1"
implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.3"
implementation 'com.google.code.gson:gson:2.11.0'
}

View File

@ -0,0 +1,44 @@
plugins {
id("com.android.application")
id("kotlin-android")
// The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins.
id("dev.flutter.flutter-gradle-plugin")
}
android {
namespace = "com.example.spotube"
compileSdk = flutter.compileSdkVersion
ndkVersion = flutter.ndkVersion
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
kotlinOptions {
jvmTarget = JavaVersion.VERSION_11.toString()
}
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId = "com.example.spotube"
// You can update the following values to match your application needs.
// For more information, see: https://flutter.dev/to/review-gradle-config.
minSdk = flutter.minSdkVersion
targetSdk = flutter.targetSdkVersion
versionCode = flutter.versionCode
versionName = flutter.versionName
}
buildTypes {
release {
// TODO: Add your own signing config for the release build.
// Signing with the debug keys for now, so `flutter run --release` works.
signingConfig = signingConfigs.getByName("debug")
}
}
}
flutter {
source = "../.."
}

View File

@ -1,21 +0,0 @@
-keep class androidx.lifecycle.DefaultLifecycleObserver
-keepnames class kotlinx.serialization.** { *; }
-keepnames class oss.krtirtho.spotube.glance.models.** { *; }
-keep @kotlinx.serialization.Serializable class *
-keepclassmembers class ** {
@kotlinx.serialization.* <fields>;
}
## We don't need beans
-dontwarn java.beans.BeanDescriptor
-dontwarn java.beans.BeanInfo
-dontwarn java.beans.IntrospectionException
-dontwarn java.beans.Introspector
-dontwarn java.beans.PropertyDescriptor
## Rules for NewPipeExtractor
-keep class org.schabi.newpipe.extractor.timeago.patterns.** { *; }
-keep class org.mozilla.javascript.** { *; }
-keep class org.mozilla.classfile.ClassFileWriter
-dontwarn org.mozilla.javascript.tools.**

View File

@ -1,6 +1,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Flutter needs it to communicate with the running application
<!-- The INTERNET permission is required for development. Specifically,
the Flutter tool needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
-->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.INTERNET"/>
</manifest>

View File

@ -1,141 +1,45 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application
android:label="spotube"
android:name="${applicationName}"
android:icon="@mipmap/ic_launcher">
<activity
android:name=".MainActivity"
android:exported="true"
android:launchMode="singleTop"
android:taskAffinity=""
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
<!-- Specifies an Android theme to apply to this Activity as soon as
the Android process has started. This theme is visible to the user
while the Flutter UI initializes. After that, this theme continues
to determine the Window background behind the Flutter UI. -->
<meta-data
android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme"
/>
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<!-- Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
<meta-data
android:name="flutterEmbedding"
android:value="2" />
</application>
<!-- Required to query activities that can process text, see:
https://developer.android.com/training/package-visibility and
https://developer.android.com/reference/android/content/Intent#ACTION_PROCESS_TEXT.
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
<queries>
<!-- If your app opens https URLs -->
<intent>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="https" />
</intent>
</queries>
<application
android:name="${applicationName}"
android:allowBackup="false"
android:fullBackupContent="false"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name_en"
android:requestLegacyExternalStorage="true"
android:usesCleartextTraffic="true">
<!-- Enable Impeller -->
<!-- <meta-data
android:name="io.flutter.embedding.android.EnableImpeller"
android:value="false" /> -->
<activity
android:name="com.ryanheise.audioservice.AudioServiceActivity"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:exported="true"
android:hardwareAccelerated="true"
android:launchMode="singleInstance"
android:theme="@style/LaunchTheme"
android:windowSoftInputMode="adjustResize">
<!--
Specifies an Android theme to apply to this Activity as soon as
the Android process has started. This theme is visible to the user
while the Flutter UI initializes. After that, this theme continues
to determine the Window background behind the Flutter UI.
-->
<meta-data
android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme" />
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:host="open.spotify.com"
android:scheme="https" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/*" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<!-- Accepts URIs that begin with "spotify:// -->
<data android:scheme="spotify" />
<data android:scheme="spotube" />
</intent-filter>
<intent-filter>
<action android:name="es.antonborri.home_widget.action.LAUNCH" />
</intent-filter>
</activity>
<!-- AudioService Config -->
<service
android:name="com.ryanheise.audioservice.AudioService"
android:exported="true"
android:foregroundServiceType="mediaPlayback">
<intent-filter>
<action android:name="android.media.browse.MediaBrowserService" />
</intent-filter>
</service>
<receiver
android:name="com.ryanheise.audioservice.MediaButtonReceiver"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MEDIA_BUTTON" />
</intent-filter>
</receiver>
<!-- =================== -->
<meta-data
android:name="com.google.android.gms.car.application"
android:resource="@xml/automotive_app_desc" />
<!-- Home Widget config -->
<receiver
android:name=".glance.HomePlayerWidgetReceiver"
android:exported="true">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/home_player_widget_config" />
</receiver>
<receiver
android:name="es.antonborri.home_widget.HomeWidgetBackgroundReceiver"
android:exported="true">
<intent-filter>
<action android:name="es.antonborri.home_widget.action.BACKGROUND" />
</intent-filter>
</receiver>
<service
android:name="es.antonborri.home_widget.HomeWidgetBackgroundService"
android:exported="true"
android:permission="android.permission.BIND_JOB_SERVICE" />
<!-- =================== -->
<!-- Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
<meta-data
android:name="flutterEmbedding"
android:value="2" />
</application>
In particular, this is used by the Flutter engine in io.flutter.plugin.text.ProcessTextPlugin. -->
<queries>
<intent>
<action android:name="android.intent.action.PROCESS_TEXT"/>
<data android:mimeType="text/plain"/>
</intent>
</queries>
</manifest>

View File

@ -1,25 +0,0 @@
// Generated file.
//
// If you wish to remove Flutter's multidex support, delete this entire file.
//
// Modifications to this file should be done in a copy under a different name
// as this file may be regenerated.
package io.flutter.app;
import android.app.Application;
import android.content.Context;
import androidx.annotation.CallSuper;
import androidx.multidex.MultiDex;
/**
* Extension of {@link android.app.Application}, adding multidex support.
*/
public class FlutterMultiDexApplication extends Application {
@Override
@CallSuper
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
MultiDex.install(this);
}
}

View File

@ -0,0 +1,5 @@
package com.example.spotube
import io.flutter.embedding.android.FlutterActivity
class MainActivity : FlutterActivity()

View File

@ -1,6 +0,0 @@
package oss.krtirtho.spotube
import io.flutter.embedding.android.FlutterActivity
class MainActivity: FlutterActivity() {
}

View File

@ -1,207 +0,0 @@
package oss.krtirtho.spotube.glance
import HomeWidgetGlanceState
import HomeWidgetGlanceStateDefinition
import android.R
import android.content.Context
import android.graphics.drawable.Icon
import android.net.Uri
import android.util.Log
import androidx.compose.runtime.Composable
import androidx.compose.ui.unit.DpSize
import androidx.compose.ui.unit.dp
import androidx.glance.GlanceId
import androidx.glance.GlanceModifier
import androidx.glance.GlanceTheme
import androidx.glance.Image
import androidx.glance.ImageProvider
import androidx.glance.LocalSize
import androidx.glance.action.ActionParameters
import androidx.glance.action.actionParametersOf
import androidx.glance.action.clickable
import androidx.glance.background
import androidx.glance.appwidget.GlanceAppWidget
import androidx.glance.appwidget.SizeMode
import androidx.glance.appwidget.action.ActionCallback
import androidx.glance.appwidget.action.actionRunCallback
import androidx.glance.appwidget.background
import androidx.glance.appwidget.components.CircleIconButton
import androidx.glance.appwidget.components.Scaffold
import androidx.glance.appwidget.cornerRadius
import androidx.glance.appwidget.provideContent
import androidx.glance.background
import androidx.glance.currentState
import androidx.glance.layout.Alignment
import androidx.glance.layout.Box
import androidx.glance.layout.Column
import androidx.glance.layout.ContentScale
import androidx.glance.layout.Row
import androidx.glance.layout.Spacer
import androidx.glance.layout.fillMaxSize
import androidx.glance.layout.fillMaxWidth
import androidx.glance.layout.padding
import androidx.glance.layout.size
import androidx.glance.preview.ExperimentalGlancePreviewApi
import androidx.glance.preview.Preview
import androidx.glance.state.GlanceStateDefinition
import com.google.gson.Gson
import es.antonborri.home_widget.HomeWidgetBackgroundIntent
import es.antonborri.home_widget.actionStartActivity
import oss.krtirtho.spotube.MainActivity
import oss.krtirtho.spotube.glance.models.Track
import oss.krtirtho.spotube.glance.widgets.FlutterAssetImageProvider
import oss.krtirtho.spotube.glance.widgets.TrackDetailsView
import oss.krtirtho.spotube.glance.widgets.TrackProgress
val gson = Gson()
val serverAddressKey = ActionParameters.Key<String>("serverAddress")
class Breakpoints {
companion object {
val SMALL_SQUARE = DpSize(100.dp, 100.dp)
val HORIZONTAL_RECTANGLE = DpSize(250.dp, 100.dp)
val BIG_SQUARE = DpSize(250.dp, 250.dp)
}
}
class HomePlayerWidget : GlanceAppWidget() {
override val sizeMode = SizeMode.Responsive(
setOf(
Breakpoints.SMALL_SQUARE,
Breakpoints.HORIZONTAL_RECTANGLE,
Breakpoints.BIG_SQUARE
)
)
override val stateDefinition: GlanceStateDefinition<*>?
get() = HomeWidgetGlanceStateDefinition()
override suspend fun provideGlance(context: Context, id: GlanceId) {
provideContent {
GlanceContent(context, currentState())
}
}
@OptIn(ExperimentalGlancePreviewApi::class)
@Preview(widthDp = 100, heightDp = 100)
@Composable
private fun GlanceContent(context: Context, currentState: HomeWidgetGlanceState) {
val prefs = currentState.preferences
val size = LocalSize.current
val activeTrackStr = prefs.getString("activeTrack", null)
val isPlaying = prefs.getBoolean("isPlaying", false)
val playbackServerAddress = prefs.getString("playbackServerAddress", null) ?: ""
var activeTrack: Track? = null
if (activeTrackStr != null) {
activeTrack = gson.fromJson(activeTrackStr, Track::class.java)
}
val playIcon = Icon.createWithResource(context, R.drawable.ic_media_play);
val pauseIcon = Icon.createWithResource(context, R.drawable.ic_media_pause);
val previousIcon = Icon.createWithResource(context, R.drawable.ic_media_previous);
val nextIcon = Icon.createWithResource(context, R.drawable.ic_media_next);
GlanceTheme {
Box(
modifier = GlanceModifier
.fillMaxSize()
.cornerRadius(8.dp)
.background(
color = GlanceTheme.colors.surface.getColor(context)
)
.clickable {
actionStartActivity<MainActivity>(context)
}
,
) {
Box(
modifier = GlanceModifier
.background(
color =
GlanceTheme.colors.surface.getColor(context)
.copy(alpha = 0.5f),
)
.fillMaxSize(),
) {}
Column(
modifier = GlanceModifier.padding(top = 10.dp, start = 10.dp, end = 10.dp)
) {
Row(verticalAlignment = Alignment.Vertical.CenterVertically) {
TrackDetailsView(activeTrack)
}
Spacer(modifier = GlanceModifier.size(6.dp))
if (size != Breakpoints.SMALL_SQUARE) {
TrackProgress(prefs)
}
Spacer(modifier = GlanceModifier.size(6.dp))
Row(
modifier = GlanceModifier.fillMaxWidth(),
horizontalAlignment = Alignment.Horizontal.CenterHorizontally
) {
CircleIconButton(
imageProvider = ImageProvider(previousIcon),
contentDescription = "Previous",
onClick = actionRunCallback<PreviousAction>(
parameters = actionParametersOf(serverAddressKey to playbackServerAddress)
)
)
Spacer(modifier = GlanceModifier.size(6.dp))
CircleIconButton(
imageProvider =
if (isPlaying) ImageProvider(pauseIcon)
else ImageProvider(playIcon),
contentDescription = "Play/Pause",
onClick = actionRunCallback<PlayPauseAction>(
parameters = actionParametersOf(serverAddressKey to playbackServerAddress)
)
)
Spacer(modifier = GlanceModifier.size(6.dp))
CircleIconButton(
imageProvider = ImageProvider(nextIcon),
contentDescription = "Previous",
onClick = actionRunCallback<NextAction>(
parameters = actionParametersOf(
serverAddressKey to playbackServerAddress
)
)
)
}
}
}
}
}
}
class PlayPauseAction : InteractiveAction("toggle-playback")
class NextAction : InteractiveAction("next")
class PreviousAction : InteractiveAction("previous")
abstract class InteractiveAction(val command: String) : ActionCallback {
override suspend fun onAction(
context: Context,
glanceId: GlanceId,
parameters: ActionParameters
) {
val serverAddress = parameters[serverAddressKey] ?: ""
Log.d("HomePlayerWidget", "Sending command $command to $serverAddress")
if (serverAddress == null || serverAddress.isEmpty()) {
return
}
val backgroundIntent = HomeWidgetBackgroundIntent.getBroadcast(
context,
Uri.parse("spotube://playback/$command?serverAddress=$serverAddress")
)
backgroundIntent.send()
}
}

View File

@ -1,7 +0,0 @@
package oss.krtirtho.spotube.glance
import HomeWidgetGlanceWidgetReceiver
class HomePlayerWidgetReceiver : HomeWidgetGlanceWidgetReceiver<HomePlayerWidget>() {
override val glanceAppWidget = HomePlayerWidget()
}

View File

@ -1,40 +0,0 @@
package oss.krtirtho.spotube.glance.models
import com.google.gson.annotations.SerializedName
import kotlinx.serialization.Serializable
@Serializable
data class AlbumSimple(
@SerializedName("album_type")
val albumType: AlbumType?,
@SerializedName("available_markets")
val availableMarkets: List<Market>?,
val href: String?,
val id: String?,
val images: List<Image>?,
val name: String?,
@SerializedName("release_date")
val releaseDate: String?,
@SerializedName("release_date_precision")
val releaseDatePrecision: DatePrecision?,
val type: String?,
val uri: String?,
)
@Serializable
enum class AlbumType {
album,
single,
compilation
}
enum class DatePrecision {
year,
month,
day
}

View File

@ -1,25 +0,0 @@
package oss.krtirtho.spotube.glance.models
import com.google.gson.annotations.SerializedName
import kotlinx.serialization.Serializable
@Serializable
data class Artist(
val href: String?,
val id: String?,
val name: String?,
val type: String?,
val uri: String?,
val followers: Followers?,
val genres: List<String>?,
val images: List<Image>?,
@SerializedName("popularity")
val popularity: Int?
)
@Serializable
data class Followers(
val total: Int?
)

View File

@ -1,10 +0,0 @@
package oss.krtirtho.spotube.glance.models
import kotlinx.serialization.Serializable
@Serializable
data class Image(
val height: Int?,
val width: Int?,
val path: String,
)

View File

@ -1,37 +0,0 @@
package oss.krtirtho.spotube.glance.models
import com.google.gson.annotations.SerializedName
import kotlinx.serialization.Serializable
import kotlin.time.Duration.Companion.milliseconds
@Serializable
data class Track(
val album: AlbumSimple?, val artists: List<Artist>?,
@SerializedName("available_markets") val availableMarkets: List<Market>?,
@SerializedName("disc_number") val discNumber: Int?,
@SerializedName("duration_ms") val durationMs: Int,
val explicit: Boolean?, val href: String?, val id: String?,
@SerializedName("is_playable") val isPlayable: Boolean?,
val name: String?,
@SerializedName("popularity") val popularity: Int?,
@SerializedName("preview_url") val previewUrl: String?,
@SerializedName("track_number") val trackNumber: Int?,
val type: String?, val uri: String?
) {
val duration: kotlin.time.Duration
get() = durationMs.toLong().milliseconds
}
enum class Market {
AD, AE, AF, AG, AI, AL, AM, AO, AQ, AR, AS, AT, AU, AW, AX, AZ, BA, BB, BD, BE, BF, BG, BH, BI, BJ, BL, BM, BN, BO, BQ, BR, BS, BT, BV, BW, BY, BZ, CA, CC, CD, CF, CG, CH, CI, CK, CL, CM, CN, CO, CR, CU, CV, CW, CX, CY, CZ, DE, DJ, DK, DM, DO, DZ, EC, EE, EG, EH, ER, ES, ET, FI, FJ, FK, FM, FO, FR, GA, GB, GD, GE, GF, GG, GH, GI, GL, GM, GN, GP, GQ, GR, GS, GT, GU, GW, GY, HK, HM, HN, HR, HT, HU, ID, IE, IL, IM, IN, IO, IQ, IR, IS, IT, JE, JM, JO, JP, KE, KG, KH, KI, KM, KN, KP, KR, KW, KY, KZ, LA, LB, LC, LI, LK, LR, LS, LT, LU, LV, LY, MA, MC, MD, ME, MF, MG, MH, MK, ML, MM, MN, MO, MP, MQ, MR, MS, MT, MU, MV, MW, MX, MY, MZ, NA, NC, NE, NF, NG, NI, NL, NO, NP, NR, NU, NZ, OM, PA, PE, PF, PG, PH, PK, PL, PM, PN, PR, PS, PT, PW, PY, QA, RE, RO, RS, RU, RW, SA, SB, SC, SD, SE, SG, SH, SI, SJ, SK, SL, SM, SN, SO, SR, SS, ST, SV, SX, SY, SZ, TC, TD, TF, TG, TH, TJ, TK, TL, TM, TN, TO, TR, TT, TV, TW, TZ, UA, UG, UM, US, UY, UZ, VA, VC, VE, VG, VI, VN, VU, WF, WS, XK, YE, YT, ZA, ZM, ZW,
}

View File

@ -1,14 +0,0 @@
package oss.krtirtho.spotube.glance.widgets
import android.graphics.BitmapFactory
import android.util.Base64
import androidx.glance.ImageProvider
@Suppress("FunctionName")
fun Base64ImageProvider(base64: String): ImageProvider {
var bytes = Base64.decode(base64, Base64.DEFAULT);
var bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.size);
return ImageProvider(bitmap)
}

View File

@ -1,14 +0,0 @@
package oss.krtirtho.spotube.glance.widgets
import android.content.Context
import android.graphics.BitmapFactory
import androidx.glance.ImageProvider
@Suppress("FunctionName")
fun FlutterAssetImageProvider(context: Context, path: String): ImageProvider {
var inputStream = context.assets.open("flutter_assets/$path")
return ImageProvider(
BitmapFactory.decodeStream(inputStream)
)
}

View File

@ -1,78 +0,0 @@
package oss.krtirtho.spotube.glance.widgets
import android.graphics.BitmapFactory
import android.net.Uri
import android.util.Log
import androidx.compose.runtime.Composable
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.glance.GlanceModifier
import androidx.glance.GlanceTheme
import androidx.glance.Image
import androidx.glance.ImageProvider
import androidx.glance.LocalContext
import androidx.glance.LocalSize
import androidx.glance.appwidget.cornerRadius
import androidx.glance.layout.Alignment
import androidx.glance.layout.Row
import androidx.glance.layout.Column
import androidx.glance.layout.ContentScale
import androidx.glance.layout.Spacer
import androidx.glance.layout.size
import androidx.glance.text.FontWeight
import androidx.glance.text.Text
import androidx.glance.text.TextStyle
import oss.krtirtho.spotube.glance.Breakpoints
import oss.krtirtho.spotube.glance.models.Track
@Composable
fun TrackDetailsView(activeTrack: Track?) {
val context = LocalContext.current
val size = LocalSize.current
val artistStr = activeTrack?.artists?.map { it.name }?.joinToString(", ") ?: "<No Artist>"
val imgLocalPath = activeTrack?.album?.images?.get(0)?.path;
val title = activeTrack?.name ?: "<No Track>"
Image(
provider =
if (imgLocalPath == null)
ImageProvider(
BitmapFactory.decodeResource(
context.resources,
android.R.drawable.ic_delete
)
)
else ImageProvider(BitmapFactory.decodeFile(imgLocalPath)),
contentDescription = "Album Art",
modifier = GlanceModifier.cornerRadius(8.dp)
.size(
if (size.height < 200.dp) 50.dp
else 100.dp
),
contentScale = ContentScale.Fit
)
Spacer(modifier = GlanceModifier.size(6.dp))
Column {
Text(
text = title,
style = TextStyle(
fontSize = 16.sp,
fontWeight = FontWeight.Bold,
color = GlanceTheme.colors.onBackground
),
)
if (size != Breakpoints.SMALL_SQUARE) {
Spacer(modifier = GlanceModifier.size(6.dp))
Text(
text = artistStr,
style = TextStyle(
fontSize = 14.sp,
color = GlanceTheme.colors.onBackground
),
)
}
}
}

View File

@ -1,77 +0,0 @@
package oss.krtirtho.spotube.glance.widgets
import android.content.SharedPreferences
import androidx.compose.runtime.Composable
import androidx.compose.ui.unit.dp
import androidx.glance.GlanceModifier
import androidx.glance.GlanceTheme
import androidx.glance.LocalSize
import androidx.glance.appwidget.LinearProgressIndicator
import androidx.glance.layout.Column
import androidx.glance.layout.Row
import androidx.glance.layout.Spacer
import androidx.glance.layout.fillMaxWidth
import androidx.glance.layout.size
import androidx.glance.text.Text
import androidx.glance.text.TextStyle
import kotlin.math.max
import kotlin.time.Duration
import kotlin.time.Duration.Companion.seconds
import oss.krtirtho.spotube.glance.Breakpoints
fun Duration.format(): String {
return this.toComponents { hour, minutes, seconds, nanoseconds ->
var paddedSeconds = seconds.toString().padStart(2, '0')
var paddedMinutes = minutes.toString().padStart(2, '0')
var paddedHour = hour.toString().padStart(2, '0')
if (hour == 0L) {
"$paddedMinutes:$paddedSeconds"
} else {
"$paddedHour:$paddedMinutes:$paddedSeconds"
}
}
}
@Composable
fun TrackProgress(prefs: SharedPreferences) {
val size = LocalSize.current
val position = prefs.getInt("position", 0).seconds
var duration = prefs.getInt("duration", 0).seconds
var progress = position.inWholeSeconds.toFloat() / max(duration.inWholeSeconds.toFloat(), 1.0f)
var textStyle =
TextStyle(
color = GlanceTheme.colors.onBackground,
)
if (size == Breakpoints.HORIZONTAL_RECTANGLE) {
Row(modifier = GlanceModifier.fillMaxWidth()) {
Text(text = position.format(), style = textStyle)
Spacer(modifier = GlanceModifier.size(6.dp))
LinearProgressIndicator(
progress = progress,
modifier = GlanceModifier.defaultWeight(),
color = GlanceTheme.colors.primary,
backgroundColor = GlanceTheme.colors.primaryContainer,
)
Spacer(modifier = GlanceModifier.size(6.dp))
Text(text = duration.format(), style = textStyle)
}
} else {
Column(modifier = GlanceModifier.fillMaxWidth()) {
LinearProgressIndicator(
progress = progress,
modifier = GlanceModifier.fillMaxWidth(),
color = GlanceTheme.colors.primary,
backgroundColor = GlanceTheme.colors.primaryContainer,
)
Spacer(modifier = GlanceModifier.size(6.dp))
Row(modifier = GlanceModifier.fillMaxWidth()) {
Text(text = position.format(), style = textStyle)
Spacer(modifier = GlanceModifier.defaultWeight())
Text(text = duration.format(), style = textStyle)
}
}
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 91 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 214 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 253 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 MiB

View File

@ -1,12 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Modify this file to customize your launch splash screen -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<bitmap android:gravity="fill" android:src="@drawable/background"/>
</item>
<item>
<bitmap android:gravity="center" android:src="@drawable/splash"/>
</item>
<item>
<bitmap android:gravity="bottom" android:src="@drawable/branding"/>
</item>
<item android:drawable="?android:colorBackground" />
<!-- You can insert your own image assets here -->
<!-- <item>
<bitmap
android:gravity="center"
android:src="@mipmap/launch_image" />
</item> -->
</layer-list>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 91 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 214 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 253 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 MiB

View File

@ -1,27 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="762"
android:viewportHeight="762">
<path
android:pathData="M309.08,370.99L309.08,479.87C309.08,486.36 314.33,491.6 320.83,491.6C327.31,491.6 332.58,486.36 332.58,479.87L332.58,370.99C332.58,364.51 327.31,359.26 320.83,359.26C314.33,359.26 309.08,364.51 309.08,370.99Z"
android:strokeLineJoin="miter"
android:strokeWidth="14"
android:fillColor="#00000000"
android:strokeColor="#000000"
android:strokeLineCap="butt"/>
<path
android:pathData="M254.59,491.73L280.46,491.73L280.46,362.47C280.53,361.85 280.64,361.23 280.64,360.6C280.64,304.83 325.72,259.46 381.12,259.46C436.51,259.46 481.59,304.83 481.59,360.6C481.59,361.45 481.71,362.27 481.84,363.1L481.84,491.73L507.71,491.73C525.72,491.73 540.33,476.65 540.33,458.03L540.33,390.62C540.33,375.26 530.37,362.33 516.78,358.26C515.53,284.17 455.17,224.26 381.12,224.26C307.05,224.26 246.69,284.18 245.45,358.29C231.88,362.36 221.96,375.29 221.96,390.63L221.96,458.03C221.96,476.64 236.56,491.73 254.59,491.73Z"
android:strokeLineJoin="miter"
android:strokeWidth="20"
android:fillColor="#00000000"
android:strokeColor="#000000"
android:strokeLineCap="butt"/>
<path
android:pathData="M431.08,370.99L431.08,479.87C431.08,486.36 436.33,491.6 442.83,491.6C449.31,491.6 454.58,486.36 454.58,479.87L454.58,370.99C454.58,364.51 449.31,359.26 442.83,359.26C436.33,359.26 431.08,364.51 431.08,370.99Z"
android:strokeLineJoin="miter"
android:strokeWidth="14"
android:fillColor="#00000000"
android:strokeColor="#000000"
android:strokeLineCap="butt"/>
</vector>

View File

@ -1,12 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Modify this file to customize your launch splash screen -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<bitmap android:gravity="fill" android:src="@drawable/background"/>
</item>
<item>
<bitmap android:gravity="center" android:src="@drawable/splash"/>
</item>
<item>
<bitmap android:gravity="bottom" android:src="@drawable/branding"/>
</item>
<item android:drawable="@android:color/white" />
<!-- You can insert your own image assets here -->
<!-- <item>
<bitmap
android:gravity="center"
android:src="@mipmap/launch_image" />
</item> -->
</layer-list>

View File

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
<monochrome android:drawable="@drawable/ic_launcher_monochrome"/>
</adaptive-icon>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.4 KiB

After

Width:  |  Height:  |  Size: 544 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 442 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.9 KiB

After

Width:  |  Height:  |  Size: 721 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -1,23 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
<item name="android:forceDarkAllowed">false</item>
<item name="android:windowFullscreen">false</item>
<item name="android:windowDrawsSystemBarBackgrounds">false</item>
<item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
<item name="android:windowSplashScreenBackground">#000000</item>
<item name="android:windowSplashScreenBrandingImage">@drawable/android12branding</item>
<item name="android:windowSplashScreenAnimatedIcon">@drawable/android12splash</item>
<item name="android:windowSplashScreenIconBackgroundColor">#000000</item>
</style>
<!-- Theme applied to the Android Window as soon as the process has started.
This theme determines the color of the Android Window while your
Flutter UI initializes, as well as behind your Flutter UI while its
running.
This Theme is only used starting with V2 of Flutter's Android embedding. -->
<style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
<item name="android:windowBackground">?android:colorBackground</item>
</style>
</resources>

View File

@ -3,12 +3,8 @@
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on -->
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
<!-- Show a splash screen on the activity. Automatically removed when
Flutter draws its first frame -->
the Flutter engine draws its first frame -->
<item name="android:windowBackground">@drawable/launch_background</item>
<item name="android:forceDarkAllowed">false</item>
<item name="android:windowFullscreen">false</item>
<item name="android:windowDrawsSystemBarBackgrounds">false</item>
<item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
</style>
<!-- Theme applied to the Android Window as soon as the process has started.
This theme determines the color of the Android Window while your

View File

@ -1,23 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->
<style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar">
<item name="android:forceDarkAllowed">false</item>
<item name="android:windowFullscreen">false</item>
<item name="android:windowDrawsSystemBarBackgrounds">false</item>
<item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
<item name="android:windowSplashScreenBackground">#000000</item>
<item name="android:windowSplashScreenBrandingImage">@drawable/android12branding</item>
<item name="android:windowSplashScreenAnimatedIcon">@drawable/android12splash</item>
<item name="android:windowSplashScreenIconBackgroundColor">#000000</item>
</style>
<!-- Theme applied to the Android Window as soon as the process has started.
This theme determines the color of the Android Window while your
Flutter UI initializes, as well as behind your Flutter UI while its
running.
This Theme is only used starting with V2 of Flutter's Android embedding. -->
<style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar">
<item name="android:windowBackground">?android:colorBackground</item>
</style>
</resources>

View File

@ -1,4 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="ic_launcher_background">#242832</color>
</resources>

View File

@ -3,12 +3,8 @@
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->
<style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar">
<!-- Show a splash screen on the activity. Automatically removed when
Flutter draws its first frame -->
the Flutter engine draws its first frame -->
<item name="android:windowBackground">@drawable/launch_background</item>
<item name="android:forceDarkAllowed">false</item>
<item name="android:windowFullscreen">false</item>
<item name="android:windowDrawsSystemBarBackgrounds">false</item>
<item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
</style>
<!-- Theme applied to the Android Window as soon as the process has started.
This theme determines the color of the Android Window while your

View File

@ -1,3 +0,0 @@
<automotiveApp>
<uses name="media" />
</automotiveApp>

View File

@ -1,7 +0,0 @@
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:initialLayout="@layout/glance_default_loading_layout"
android:minWidth="100dp"
android:minHeight="100dp"
android:resizeMode="horizontal|vertical"
android:updatePeriodMillis="10000">
</appwidget-provider>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 220 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 213 KiB

Some files were not shown because too many files have changed in this diff Show More