Skip to content

Commit

Permalink
Merge pull request #292 from 07jasjeet/listen-scrobble-refactor-beta-1
Browse files Browse the repository at this point in the history
Listen Scrobble Service Beta-1
  • Loading branch information
akshaaatt authored Nov 30, 2023
2 parents 96514fa + 5fb7a19 commit 7d6c1d2
Show file tree
Hide file tree
Showing 32 changed files with 844 additions and 269 deletions.
1 change: 0 additions & 1 deletion app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,6 @@ dependencies {
implementation "com.google.accompanist:accompanist-systemuicontroller:$accompanist_version"

// Third party libraries
implementation 'com.github.dariobrux:Timer:1.1.0'
implementation 'com.github.a914-gowtham:compose-ratingbar:1.3.4'
implementation 'com.github.akshaaatt:Logger-Android:1.0.0'
}
9 changes: 6 additions & 3 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
Expand Down Expand Up @@ -62,7 +63,9 @@
</service>
<service
android:name=".service.BrainzPlayerService"
android:exported="true">
android:foregroundServiceType="mediaPlayback"
android:exported="true"
android:permission="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK">
<intent-filter>
<action android:name="android.media.browse.MediaMrowserService" />
</intent-filter>
Expand All @@ -79,7 +82,7 @@
android:name=".ui.screens.onboarding.FeaturesActivity"
android:theme="@style/Theme.Material3.DayNight.NoActionBar" />
<activity
android:name=".ui.screens.dashboard.DashboardActivity"
android:name=".ui.screens.main.MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
Expand All @@ -88,7 +91,7 @@
</intent-filter>
</activity>
<activity
android:name=".ui.screens.dashboard.DonateActivity"
android:name=".ui.screens.main.DonateActivity"
android:label="@string/donate_title"
android:theme="@style/AppTheme"/>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,10 @@ import androidx.work.Configuration
import com.limurse.logger.Logger
import com.limurse.logger.config.Config
import dagger.hilt.android.HiltAndroidApp
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.listenbrainz.android.BuildConfig
import org.listenbrainz.android.R
import org.listenbrainz.android.repository.preferences.AppPreferences
Expand Down Expand Up @@ -54,7 +56,7 @@ class App : Application(), Configuration.Provider {
if(
appPreferences.isNotificationServiceAllowed &&
appPreferences.getLbAccessToken().isNotEmpty() &&
appPreferences.submitListens
withContext(Dispatchers.IO) { appPreferences.submitListens }
) {
startListenService()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import org.listenbrainz.android.repository.remoteplayer.RemotePlaybackHandler
import org.listenbrainz.android.repository.remoteplayer.RemotePlaybackHandlerImpl
import org.listenbrainz.android.repository.scrobblemanager.ScrobbleManager
import org.listenbrainz.android.repository.scrobblemanager.ScrobbleManagerImpl

@Module
@InstallIn(SingletonComponent::class)
Expand All @@ -14,4 +16,7 @@ abstract class RemotePlayerRepositoryModule {
@Binds
abstract fun bindsRemotePlayerRepository(repository: RemotePlaybackHandlerImpl?): RemotePlaybackHandler?


@Binds
abstract fun bindsScrobbleManager(scrobbleManager: ScrobbleManagerImpl): ScrobbleManager
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import org.listenbrainz.android.model.yimdata.YimData
import org.listenbrainz.android.repository.preferences.AppPreferences
import org.listenbrainz.android.service.BlogService
Expand Down Expand Up @@ -41,7 +40,7 @@ class ServiceModule {
okHttpClient
.newBuilder()
.addInterceptor(HeaderInterceptor(appPreferences))
.addInterceptor (HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.HEADERS))
//.addInterceptor (HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.HEADERS))
.build()
)
.baseUrl(LISTENBRAINZ_API_BASE_URL)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package org.listenbrainz.android.model

interface OnTimerListener {
fun onTimerRun(milliseconds: Long) {}
fun onTimerStarted()
fun onTimerPaused(remainingMillis: Long)
fun onTimerStopped() {}
fun onTimerEnded()
}
105 changes: 105 additions & 0 deletions app/src/main/java/org/listenbrainz/android/model/PlayingTrack.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package org.listenbrainz.android.model

import android.media.MediaMetadata
import org.listenbrainz.android.util.ListenSubmissionState.Companion.DEFAULT_DURATION
import org.listenbrainz.android.util.ListenSubmissionState.Companion.extractArtist
import org.listenbrainz.android.util.ListenSubmissionState.Companion.extractDuration
import org.listenbrainz.android.util.ListenSubmissionState.Companion.extractReleaseName
import org.listenbrainz.android.util.ListenSubmissionState.Companion.extractTitle

/** Track metadata class for Listen Scrobble service.*/
data class PlayingTrack(
var artist: String? = null,
var title: String? = null,
var releaseName: String? = null,
var timestamp: Long = 0,
var duration: Long = 0,
var pkgName: String? = null,
var playingNowSubmitted: Boolean = false,
var submitted: Boolean = false,
var playbackState: Int? = null
) {
val timestampSeconds: Long
get() = timestamp / 1000

/** This means there's no track playing.*/
fun isNothing(): Boolean = artist == null && title == null

fun isSubmitted(): Boolean = submitted

/** Determines if this track is a notification scrobbled track or not.*/
fun isNotificationTrack(): Boolean = artist != null && title != null && duration == 0L

fun isCallbackTrack(): Boolean = !isNotificationTrack()

fun reset() {
artist = null
title = null
releaseName = null
timestamp = 0
duration = 0
pkgName = null
playingNowSubmitted = false
submitted = false
}

/** Similar means that the basic metadata matches. A song if replayed will be similar.*/
fun isSimilarTo(other: Any): Boolean {
return when (other) {
is PlayingTrack -> artist == other.artist
&& title == other.title
&& pkgName == other.pkgName
is MediaMetadata -> artist == other.extractArtist()
&& title == other.extractTitle()
else -> {
throw IllegalStateException(
"${other.javaClass.simpleName} is not supported for use in this function."
)
}
}
}

/** Determines if *this* track is outdated in comparison to [newTrack].
*
* Covers case where a track being replayed is similar but is actually outdated.*/
fun isOutdated(newTrack: PlayingTrack): Boolean {
return when {
this.isSimilarTo(newTrack) -> {
// If track is similar.
// If the difference in timestamps is greater than duration of the whole track, it
// means our track is outdated for sure.
when {
newTrack.duration != 0L -> {
// New track is callback track, duration data available.
newTrack.timestamp - timestamp >= newTrack.duration
}
this.duration != 0L -> {
// New track is callback track, duration data available.
newTrack.timestamp - timestamp >= this.duration
}
submitted -> true
else -> {
// New track and old track both are notification track. So, no duration
// available. We use default duration.
newTrack.timestamp - timestamp >= DEFAULT_DURATION
}
}
}
else -> true
}
}

companion object {
fun MediaMetadata.toPlayingTrack(pkgName: String): PlayingTrack {
return PlayingTrack(
timestamp = System.currentTimeMillis(),
artist = extractArtist(),
title = extractTitle(),
duration = extractDuration(),
releaseName = extractReleaseName(),
pkgName = pkgName,
playingNowSubmitted = false
)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import androidx.compose.runtime.Stable
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import org.listenbrainz.android.util.Resource
import org.listenbrainz.android.util.Utils.error
import retrofit2.Response


Expand Down Expand Up @@ -70,7 +71,7 @@ enum class ResponseError(val genericToast: String, var actualResponse: String? =
* returning errors is the sole motive.*/
fun <T> parseError(response: Response<T>) : ApiError =
Gson().fromJson(
/* json = */ response.errorBody()?.string(),
/* json = */ response.error(),
/* typeOfT = */ object : TypeToken<ApiError>() {}.type
)

Expand Down
9 changes: 9 additions & 0 deletions app/src/main/java/org/listenbrainz/android/model/Status.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package org.listenbrainz.android.model

enum class Status {
RUN,
PAUSE,
START,
STOP,
END
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,15 @@ import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.withContext
import org.listenbrainz.android.model.AccessToken
import org.listenbrainz.android.model.PermissionStatus
import org.listenbrainz.android.model.Playable
import org.listenbrainz.android.model.UiMode
import org.listenbrainz.android.model.UiMode.Companion.asUiMode
import org.listenbrainz.android.model.UserInfo
import org.listenbrainz.android.repository.preferences.AppPreferencesImpl.Companion.PreferenceKeys.LISTENING_APPS
import org.listenbrainz.android.repository.preferences.AppPreferencesImpl.Companion.PreferenceKeys.THEME
import org.listenbrainz.android.util.Constants
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package org.listenbrainz.android.repository.scrobblemanager

import android.media.MediaMetadata
import android.media.session.PlaybackState
import android.service.notification.StatusBarNotification

interface ScrobbleManager {

fun onMetadataChanged(metadata: MediaMetadata?, player: String)

fun onPlaybackStateChanged(state: PlaybackState?)

fun onNotificationPosted(sbn: StatusBarNotification?)

fun onNotificationRemoved(sbn: StatusBarNotification?)
}
Loading

0 comments on commit 7d6c1d2

Please sign in to comment.