diff --git a/Readme.md b/Readme.md index 7f5c1519..8fb5b8c0 100644 --- a/Readme.md +++ b/Readme.md @@ -54,7 +54,9 @@ Got **something interesting** you'd like to **ask or share**? Start a discussion ## Roadmap -Proposed future plans for the app are discussed [here](https://docs.google.com/document/d/1hY5oloIiANeXg1R9oSBIr2ZUHSlm7LU8qy6tW1VJMQg/edit?usp=sharing). +- Proposed future plans for the app are discussed [here](https://docs.google.com/document/d/1hY5oloIiANeXg1R9oSBIr2ZUHSlm7LU8qy6tW1VJMQg/edit?usp=sharing). +- Official Release Plan is [here](https://docs.google.com/document/d/1LSDXWciL4LMS_LJJ0Y47BZtU6jEI-8hiliWbzEOYKsA/edit?usp=sharing) +- Future features are discussed [here](https://docs.google.com/document/d/1fmsgFVvxAN39nUyaMsEdyePBaDyDwxelJXeLhaDylhs/edit?usp=sharing) ## Issues diff --git a/app/build.gradle b/app/build.gradle index 23556267..d0cdc158 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -149,6 +149,10 @@ dependencies { implementation 'androidx.paging:paging-runtime-ktx:3.2.1' implementation "androidx.paging:paging-compose:3.2.1" + // Typesafe-datastore + implementation "com.github.07jasjeet.typesafe-datastore:typesafe-datastore:$typesafe_datastore_version" + implementation "com.github.07jasjeet.typesafe-datastore:typesafe-datastore-gson:$typesafe_datastore_version" + testImplementation "com.github.07jasjeet.typesafe-datastore:typesafe-datastore-test:$typesafe_datastore_version" //Image downloading and Caching library implementation 'com.github.bumptech.glide:glide:4.16.0' @@ -217,6 +221,7 @@ dependencies { // Mockito framework testImplementation 'org.mockito:mockito-core:5.10.0' testImplementation 'org.mockito.kotlin:mockito-kotlin:5.2.1' + androidTestImplementation "org.mockito:mockito-android:5.2.1" debugImplementation "androidx.test:monitor:1.6.1" // Solves "class PlatformTestStorageRegistery not found" error for ui tests. debugImplementation 'androidx.compose.ui:ui-test-manifest:1.6.1' diff --git a/app/src/androidTest/java/org/listenbrainz/android/Extensions.kt b/app/src/androidTest/java/org/listenbrainz/android/Extensions.kt new file mode 100644 index 00000000..d8478f7b --- /dev/null +++ b/app/src/androidTest/java/org/listenbrainz/android/Extensions.kt @@ -0,0 +1,27 @@ +package org.listenbrainz.android + +import androidx.activity.ComponentActivity +import androidx.compose.runtime.Composable +import androidx.compose.runtime.mutableStateOf +import androidx.compose.ui.test.junit4.AndroidComposeTestRule +import androidx.test.ext.junit.rules.ActivityScenarioRule +import org.listenbrainz.android.model.UiMode +import org.listenbrainz.android.ui.theme.ListenBrainzTheme +import org.listenbrainz.android.ui.theme.isUiModeIsDark + +fun AndroidComposeTestRule, ComponentActivity>.setExplicitContent( + uiMode: UiMode = UiMode.DARK, + content: @Composable () -> Unit +) { + isUiModeIsDark = when(uiMode) { + UiMode.LIGHT -> mutableStateOf(false) + UiMode.DARK -> mutableStateOf(true) + UiMode.FOLLOW_SYSTEM -> mutableStateOf(null) + } + + this.setContent { + ListenBrainzTheme { + content() + } + } +} diff --git a/app/src/androidTest/java/org/listenbrainz/android/YearInMusicActivityTest.kt b/app/src/androidTest/java/org/listenbrainz/android/YearInMusicActivityTest.kt index 1c1baa7a..b6bcc57a 100644 --- a/app/src/androidTest/java/org/listenbrainz/android/YearInMusicActivityTest.kt +++ b/app/src/androidTest/java/org/listenbrainz/android/YearInMusicActivityTest.kt @@ -2,23 +2,28 @@ package org.listenbrainz.android import androidx.activity.ComponentActivity import androidx.annotation.StringRes -import androidx.compose.ui.test.* import androidx.compose.ui.test.junit4.createAndroidComposeRule -import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.compose.ui.test.onNodeWithTag +import androidx.compose.ui.test.onNodeWithText +import androidx.compose.ui.test.performClick +import androidx.compose.ui.test.performScrollTo +import androidx.compose.ui.test.performTouchInput import dagger.hilt.android.testing.HiltAndroidRule import dagger.hilt.android.testing.HiltAndroidTest import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith +import org.listenbrainz.android.repository.preferences.AppPreferences import org.listenbrainz.android.ui.screens.yim.navigation.YimNavigation import org.listenbrainz.android.util.connectivityobserver.ConnectivityObserver import org.listenbrainz.android.viewmodel.YimViewModel -import org.listenbrainz.sharedtest.mocks.MockAppPreferences import org.listenbrainz.sharedtest.mocks.MockNetworkConnectivityViewModel import org.listenbrainz.sharedtest.mocks.MockYimRepository +import org.mockito.Mock +import org.mockito.junit.MockitoJUnitRunner -@RunWith(AndroidJUnit4::class) +@RunWith(MockitoJUnitRunner::class) //@LargeTest @HiltAndroidTest class YearInMusicActivityTest { @@ -29,14 +34,16 @@ class YearInMusicActivityTest { @get:Rule(order = 1) val rule = createAndroidComposeRule() + @Mock + private lateinit var mockAppPreferences: AppPreferences + private lateinit var activity : ComponentActivity @Before fun setup(){ activity = rule.activity val yimViewModel = YimViewModel( - MockYimRepository(), - MockAppPreferences() + MockYimRepository(), mockAppPreferences ) val networkViewModel = MockNetworkConnectivityViewModel(ConnectivityObserver.NetworkStatus.AVAILABLE) @@ -86,4 +93,8 @@ class YearInMusicActivityTest { performClick() } } + + private fun initMocks() { + + } } diff --git a/app/src/androidTest/java/org/listenbrainz/android/di/TestAppModule.kt b/app/src/androidTest/java/org/listenbrainz/android/di/TestAppModule.kt index 1a784f70..8b089dda 100644 --- a/app/src/androidTest/java/org/listenbrainz/android/di/TestAppModule.kt +++ b/app/src/androidTest/java/org/listenbrainz/android/di/TestAppModule.kt @@ -1,7 +1,6 @@ package org.listenbrainz.android.di import android.content.Context - import android.util.Log import androidx.work.Configuration import androidx.work.WorkManager @@ -14,7 +13,6 @@ import dagger.hilt.components.SingletonComponent import dagger.hilt.testing.TestInstallIn import org.listenbrainz.android.repository.preferences.AppPreferences import org.listenbrainz.android.service.BrainzPlayerServiceConnection -import org.listenbrainz.sharedtest.mocks.MockAppPreferences import javax.inject.Singleton @Module @@ -47,8 +45,8 @@ class TestAppModule { @Provides fun providesContext(@ApplicationContext context: Context): Context = context - @Singleton + /*@Singleton @Provides - fun providesAppPreferences() : AppPreferences = MockAppPreferences() + fun providesAppPreferences() : AppPreferences = MockAppPreferences()*/ } diff --git a/app/src/androidTest/java/org/listenbrainz/android/feed/FeedFlowTest.kt b/app/src/androidTest/java/org/listenbrainz/android/feed/FeedFlowTest.kt new file mode 100644 index 00000000..893f17c8 --- /dev/null +++ b/app/src/androidTest/java/org/listenbrainz/android/feed/FeedFlowTest.kt @@ -0,0 +1,54 @@ +package org.listenbrainz.android.feed + +import androidx.activity.ComponentActivity +import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.LargeTest +import dagger.hilt.android.testing.HiltAndroidTest +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.listenbrainz.android.model.feed.FeedEvent +import org.listenbrainz.android.model.feed.FeedEventType +import org.listenbrainz.android.model.feed.ReviewEntityType +import org.listenbrainz.android.setExplicitContent +import org.listenbrainz.android.ui.screens.feed.FeedScreen +import org.listenbrainz.android.ui.screens.feed.FeedUiState + +@RunWith(AndroidJUnit4::class) +@LargeTest +@HiltAndroidTest +class FeedFlowTest { + + @get:Rule(order = 0) + val rule = createAndroidComposeRule() + + @Before + fun setup(){ + + rule.setExplicitContent { + + FeedScreen( + uiState = FeedUiState(), + scrollToTopState = false, + onScrollToTop = {}, + onDeleteOrHide = { feedEvent: FeedEvent, feedEventType: FeedEventType, s: String -> }, + onErrorShown = {}, + recommendTrack = {}, + personallyRecommendTrack = { feedEvent: FeedEvent, strings: List, s: String -> }, + review = { feedEvent: FeedEvent, reviewEntityType: ReviewEntityType, s: String, i: Int?, s1: String -> }, + pin = { feedEvent: FeedEvent, s: String? -> }, + searchFollower = {}, + isCritiqueBrainzLinked = { true }, + onPlay = {} + ) + } + } + + @Test + fun feedScreenTest() = runTest { + + } +} \ No newline at end of file diff --git a/app/src/main/java/org/listenbrainz/android/di/brainzplayer/ServiceModule.kt b/app/src/main/java/org/listenbrainz/android/di/brainzplayer/ServiceModule.kt index 446d946c..decd467e 100644 --- a/app/src/main/java/org/listenbrainz/android/di/brainzplayer/ServiceModule.kt +++ b/app/src/main/java/org/listenbrainz/android/di/brainzplayer/ServiceModule.kt @@ -14,8 +14,8 @@ import dagger.hilt.android.scopes.ServiceScoped import org.listenbrainz.android.repository.brainzplayer.AlbumRepository import org.listenbrainz.android.repository.brainzplayer.PlaylistRepository import org.listenbrainz.android.repository.brainzplayer.SongRepository -import org.listenbrainz.android.util.LocalMusicSource -import org.listenbrainz.android.util.MusicSource +import org.listenbrainz.android.util.brainzplayer.LocalMusicSource +import org.listenbrainz.android.util.brainzplayer.MusicSource @Module @InstallIn(ServiceComponent::class) diff --git a/app/src/main/java/org/listenbrainz/android/repository/brainzplayer/AlbumRepositoryImpl.kt b/app/src/main/java/org/listenbrainz/android/repository/brainzplayer/AlbumRepositoryImpl.kt index b9d48833..5283943d 100644 --- a/app/src/main/java/org/listenbrainz/android/repository/brainzplayer/AlbumRepositoryImpl.kt +++ b/app/src/main/java/org/listenbrainz/android/repository/brainzplayer/AlbumRepositoryImpl.kt @@ -12,8 +12,8 @@ import kotlinx.coroutines.withContext import org.listenbrainz.android.model.Album import org.listenbrainz.android.model.Song import org.listenbrainz.android.model.dao.AlbumDao -import org.listenbrainz.android.util.AlbumsData -import org.listenbrainz.android.util.SongsData +import org.listenbrainz.android.util.brainzplayer.AlbumsData +import org.listenbrainz.android.util.brainzplayer.SongsData import org.listenbrainz.android.util.Transformer.toAlbum import org.listenbrainz.android.util.Transformer.toAlbumEntity diff --git a/app/src/main/java/org/listenbrainz/android/repository/brainzplayer/ArtistRepositoryImpl.kt b/app/src/main/java/org/listenbrainz/android/repository/brainzplayer/ArtistRepositoryImpl.kt index 98cb14a6..f07da455 100644 --- a/app/src/main/java/org/listenbrainz/android/repository/brainzplayer/ArtistRepositoryImpl.kt +++ b/app/src/main/java/org/listenbrainz/android/repository/brainzplayer/ArtistRepositoryImpl.kt @@ -10,8 +10,8 @@ import org.listenbrainz.android.model.Album import org.listenbrainz.android.model.Artist import org.listenbrainz.android.model.Song import org.listenbrainz.android.model.dao.ArtistDao -import org.listenbrainz.android.util.AlbumsData -import org.listenbrainz.android.util.SongsData +import org.listenbrainz.android.util.brainzplayer.AlbumsData +import org.listenbrainz.android.util.brainzplayer.SongsData import org.listenbrainz.android.util.Transformer.toAlbumEntity import org.listenbrainz.android.util.Transformer.toArtist import org.listenbrainz.android.util.Transformer.toArtistEntity diff --git a/app/src/main/java/org/listenbrainz/android/repository/brainzplayer/SongRepositoryImpl.kt b/app/src/main/java/org/listenbrainz/android/repository/brainzplayer/SongRepositoryImpl.kt index b65813b0..6312ab5c 100644 --- a/app/src/main/java/org/listenbrainz/android/repository/brainzplayer/SongRepositoryImpl.kt +++ b/app/src/main/java/org/listenbrainz/android/repository/brainzplayer/SongRepositoryImpl.kt @@ -4,7 +4,7 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map import org.listenbrainz.android.model.Song import org.listenbrainz.android.model.dao.SongDao -import org.listenbrainz.android.util.SongsData +import org.listenbrainz.android.util.brainzplayer.SongsData import org.listenbrainz.android.util.Transformer.toSong import org.listenbrainz.android.util.Transformer.toSongEntity import javax.inject.Inject diff --git a/app/src/main/java/org/listenbrainz/android/repository/listenservicemanager/ListenServiceManagerImpl.kt b/app/src/main/java/org/listenbrainz/android/repository/listenservicemanager/ListenServiceManagerImpl.kt index 513382dd..301959b9 100644 --- a/app/src/main/java/org/listenbrainz/android/repository/listenservicemanager/ListenServiceManagerImpl.kt +++ b/app/src/main/java/org/listenbrainz/android/repository/listenservicemanager/ListenServiceManagerImpl.kt @@ -44,7 +44,9 @@ class ListenServiceManagerImpl @Inject constructor( /** Used to avoid repetitive submissions.*/ private var lastNotificationPostTs = System.currentTimeMillis() private lateinit var whitelist: List + private var isListeningAllowed: Boolean = true + init { with(scope) { diff --git a/app/src/main/java/org/listenbrainz/android/repository/preferences/AppPreferences.kt b/app/src/main/java/org/listenbrainz/android/repository/preferences/AppPreferences.kt index f1c4bf27..98b535e9 100644 --- a/app/src/main/java/org/listenbrainz/android/repository/preferences/AppPreferences.kt +++ b/app/src/main/java/org/listenbrainz/android/repository/preferences/AppPreferences.kt @@ -1,5 +1,7 @@ package org.listenbrainz.android.repository.preferences +import com.jasjeet.typesafe_datastore.preferences.ComplexPreference +import com.jasjeet.typesafe_datastore.preferences.PrimitivePreference import kotlinx.coroutines.flow.Flow import org.listenbrainz.android.model.Playable import org.listenbrainz.android.model.UiMode @@ -7,7 +9,7 @@ import org.listenbrainz.android.util.LinkedService interface AppPreferences { - val themePreference: DataStorePreference + val themePreference: ComplexPreference /** * @@ -21,10 +23,10 @@ interface AppPreferences { var permissionsPreference: String? /** Whitelist for ListenSubmissionService.*/ - val listeningWhitelist: DataStorePreference> + val listeningWhitelist: ComplexPreference> /** Music Apps in users device registered by listenService.*/ - val listeningApps: DataStorePreference> + val listeningApps: ComplexPreference> var onboardingCompleted: Boolean @@ -40,19 +42,19 @@ interface AppPreferences { suspend fun isUserLoggedIn() : Boolean /****ListenBrainz User Token:** User has to manually fill this token.*/ - val lbAccessToken: DataStorePreference + val lbAccessToken: PrimitivePreference - val username: DataStorePreference + val username: PrimitivePreference val refreshToken: String? var linkedServices: List /** Default is true. */ - val isListeningAllowed: DataStorePreference + val isListeningAllowed: PrimitivePreference /** Default is true. */ - val shouldListenNewPlayers: DataStorePreference + val shouldListenNewPlayers: PrimitivePreference val isNotificationServiceAllowed: Boolean diff --git a/app/src/main/java/org/listenbrainz/android/repository/preferences/AppPreferencesImpl.kt b/app/src/main/java/org/listenbrainz/android/repository/preferences/AppPreferencesImpl.kt index 274b4877..a1011422 100644 --- a/app/src/main/java/org/listenbrainz/android/repository/preferences/AppPreferencesImpl.kt +++ b/app/src/main/java/org/listenbrainz/android/repository/preferences/AppPreferencesImpl.kt @@ -3,17 +3,16 @@ package org.listenbrainz.android.repository.preferences import android.content.Context import android.content.pm.PackageManager import android.provider.Settings -import androidx.datastore.core.DataMigration import androidx.datastore.core.DataStore import androidx.datastore.preferences.SharedPreferencesMigration import androidx.datastore.preferences.core.Preferences import androidx.datastore.preferences.core.booleanPreferencesKey -import androidx.datastore.preferences.core.edit import androidx.datastore.preferences.core.stringPreferencesKey import androidx.datastore.preferences.preferencesDataStore import androidx.preference.PreferenceManager -import com.google.gson.Gson -import com.google.gson.reflect.TypeToken +import com.jasjeet.typesafe_datastore.preferences.ComplexPreference +import com.jasjeet.typesafe_datastore.preferences.PrimitivePreference +import com.jasjeet.typesafe_datastore_gson.AutoTypedDataStore import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.distinctUntilChanged @@ -22,10 +21,8 @@ import kotlinx.coroutines.withContext 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.repository.preferences.AppPreferencesImpl.Companion.PreferenceKeys.IS_LISTENING_ALLOWED import org.listenbrainz.android.repository.preferences.AppPreferencesImpl.Companion.PreferenceKeys.LISTENING_APPS -import org.listenbrainz.android.repository.preferences.AppPreferencesImpl.Companion.PreferenceKeys.LISTENING_BLACKLIST import org.listenbrainz.android.repository.preferences.AppPreferencesImpl.Companion.PreferenceKeys.LISTENING_WHITELIST import org.listenbrainz.android.repository.preferences.AppPreferencesImpl.Companion.PreferenceKeys.SHOULD_LISTEN_NEW_PLAYERS import org.listenbrainz.android.repository.preferences.AppPreferencesImpl.Companion.PreferenceKeys.THEME @@ -49,57 +46,31 @@ import org.listenbrainz.android.util.Constants.Strings.STATUS_LOGGED_OUT import org.listenbrainz.android.util.Constants.Strings.USERNAME import org.listenbrainz.android.util.LinkedService import org.listenbrainz.android.util.TypeConverter +import org.listenbrainz.android.util.datastore.DataStoreSerializers.themeSerializer +import org.listenbrainz.android.util.migrations.blacklistMigration -class AppPreferencesImpl(private val context: Context): AppPreferences { - companion object { - private val gson = Gson() - private val blacklistMigration: DataMigration = - object: DataMigration { - override suspend fun cleanUp() = Unit - - override suspend fun shouldMigrate(currentData: Preferences): Boolean { - // If blacklist is deleted, then we are sure that migration took place. - return currentData.contains(LISTENING_BLACKLIST) - } - - override suspend fun migrate(currentData: Preferences): Preferences { - val blacklist = currentData[LISTENING_BLACKLIST].asStringList() - val appList = currentData[LISTENING_APPS].asStringList() - - val whitelist = currentData[LISTENING_WHITELIST].asStringList().toMutableSet() - appList.forEach { pkg -> - if (!blacklist.contains(pkg)) { - whitelist.add(pkg) - } - } - - val mutablePreferences = currentData.toMutablePreferences() - mutablePreferences[LISTENING_WHITELIST] = Gson().toJson(whitelist.toList()) - mutablePreferences.remove(LISTENING_BLACKLIST) // Clear old stale data and key. - - return mutablePreferences.toPreferences() - } - } - - private val Context.dataStore: DataStore by preferencesDataStore( - name = "settings", - produceMigrations = { context -> - // Since we're migrating from SharedPreferences, add a migration based on the - // SharedPreferences name - listOf(SharedPreferencesMigration( - context, - context.packageName + "_preferences", - setOf( - LB_ACCESS_TOKEN, - USERNAME, - PREFERENCE_SYSTEM_THEME, - PREFERENCE_LISTENING_APPS - ) - ), blacklistMigration) - } +private val Context.dataStore: DataStore by preferencesDataStore( + name = "settings", + produceMigrations = { context -> + // Since we're migrating from SharedPreferences, add a migration based on the + // SharedPreferences name + listOf(SharedPreferencesMigration( + context, + context.packageName + "_preferences", + setOf( + LB_ACCESS_TOKEN, + USERNAME, + PREFERENCE_SYSTEM_THEME, + PREFERENCE_LISTENING_APPS + ) + ), blacklistMigration ) - - private object PreferenceKeys { + } +) + +class AppPreferencesImpl(private val context: Context): AutoTypedDataStore(context.dataStore), AppPreferences { + companion object { + object PreferenceKeys { val LB_ACCESS_TOKEN = stringPreferencesKey(Constants.Strings.LB_ACCESS_TOKEN) val USERNAME = stringPreferencesKey(Constants.Strings.USERNAME) val LISTENING_BLACKLIST = stringPreferencesKey(PREFERENCE_LISTENING_BLACKLIST) @@ -109,13 +80,6 @@ class AppPreferencesImpl(private val context: Context): AppPreferences { val IS_LISTENING_ALLOWED = booleanPreferencesKey(PREFERENCE_SUBMIT_LISTENS) val SHOULD_LISTEN_NEW_PLAYERS = booleanPreferencesKey(PREFERENCE_LISTEN_NEW_PLAYERS) } - - fun String?.asStringList(): List { - return gson.fromJson( - this, - object: TypeToken>() {}.type - ) ?: emptyList() - } } private val preferences = PreferenceManager.getDefaultSharedPreferences(context) @@ -142,47 +106,17 @@ class AppPreferencesImpl(private val context: Context): AppPreferences { editor.apply() } - private val datastore: Flow - get() = context.dataStore.data - // Preferences Implementation - override val themePreference: DataStorePreference - get() = object : DataStorePreference { - override fun getFlow(): Flow = - datastore.map { it[THEME].asUiMode() } - - override suspend fun set(value: UiMode) { - context.dataStore.edit { it[THEME] = value.name } - } - } + override val themePreference: ComplexPreference + get() = createComplexPreference(THEME, themeSerializer) override var permissionsPreference: String? get() = preferences.getString(PREFERENCE_PERMS, PermissionStatus.NOT_REQUESTED.name) set(value) = setString(PREFERENCE_PERMS, value) - override val listeningWhitelist: DataStorePreference> - get() = object: DataStorePreference> { - override fun getFlow(): Flow> = - datastore.map { prefs -> - prefs[LISTENING_WHITELIST].asStringList() - } - - override suspend fun set(value: List) { - context.dataStore.edit { prefs -> - prefs[LISTENING_WHITELIST] = gson.toJson(value) - } - } - - override suspend fun getAndUpdate(update: (List) -> List) { - context.dataStore.updateData { - val updatedValue = update(it[LISTENING_WHITELIST].asStringList()) - val mutablePrefs = it.toMutablePreferences() - mutablePrefs[LISTENING_WHITELIST] = gson.toJson(updatedValue) - return@updateData mutablePrefs - } - } - } + override val listeningWhitelist: ComplexPreference> + get() = listPreference(LISTENING_WHITELIST) override val isNotificationServiceAllowed: Boolean get() { @@ -190,56 +124,14 @@ class AppPreferencesImpl(private val context: Context): AppPreferences { return listeners != null && listeners.contains(context.packageName) } - override val isListeningAllowed: DataStorePreference - get() = object: DataStorePreference { - override fun getFlow(): Flow = - datastore.map { prefs -> - prefs[IS_LISTENING_ALLOWED] ?: true - } - - override suspend fun set(value: Boolean) { - context.dataStore.edit { prefs -> - prefs[IS_LISTENING_ALLOWED] = value - } - } - } - - override val shouldListenNewPlayers: DataStorePreference - get() = object : DataStorePreference { - override fun getFlow(): Flow = - datastore.map { prefs -> - prefs[SHOULD_LISTEN_NEW_PLAYERS] ?: true - } - - override suspend fun set(value: Boolean) { - context.dataStore.edit { prefs -> - prefs[SHOULD_LISTEN_NEW_PLAYERS] = value - } - } - } + override val isListeningAllowed: PrimitivePreference + get() = booleanPreference(IS_LISTENING_ALLOWED, true) - override val listeningApps: DataStorePreference> - get() = object: DataStorePreference> { - override fun getFlow(): Flow> = - datastore.map { prefs -> - prefs[LISTENING_APPS].asStringList() - } - - override suspend fun set(value: List) { - context.dataStore.edit { prefs -> - prefs[LISTENING_APPS] = gson.toJson(value) - } - } + override val shouldListenNewPlayers: PrimitivePreference + get() = booleanPreference(SHOULD_LISTEN_NEW_PLAYERS, true) - override suspend fun getAndUpdate(update: (List) -> List) { - context.dataStore.updateData { - val updatedValue = update(it[LISTENING_APPS].asStringList()) - val mutablePrefs = it.toMutablePreferences() - mutablePrefs[LISTENING_APPS] = gson.toJson(updatedValue) - return@updateData mutablePrefs - } - } - } + override val listeningApps: ComplexPreference> + get() = listPreference(LISTENING_APPS) override val version: String get() = try { @@ -253,7 +145,7 @@ class AppPreferencesImpl(private val context: Context): AppPreferences { get() = preferences.getBoolean(ONBOARDING, false) set(value) = setBoolean(ONBOARDING, value) - override suspend fun logoutUser() = withContext(Dispatchers.IO) { + override suspend fun logoutUser(): Unit = withContext(Dispatchers.IO) { val editor = preferences.edit() editor.remove(REFRESH_TOKEN) editor.remove(USERNAME) @@ -285,44 +177,19 @@ class AppPreferencesImpl(private val context: Context): AppPreferences { override suspend fun isUserLoggedIn() : Boolean = lbAccessToken.get().isNotEmpty() - override val lbAccessToken: DataStorePreference - get() = object : DataStorePreference { - override fun getFlow(): Flow = - datastore.map { prefs -> - prefs[PreferenceKeys.LB_ACCESS_TOKEN] ?: "" - } - - override suspend fun set(value: String) { - context.dataStore.edit { prefs -> - prefs[PreferenceKeys.LB_ACCESS_TOKEN] = value - } - } - - } - - override val username: DataStorePreference - get() = object: DataStorePreference { - override fun getFlow(): Flow = - datastore.map { prefs -> - prefs[PreferenceKeys.USERNAME] ?: "" - } - - override suspend fun set(value: String) { - context.dataStore.edit { prefs -> - prefs[PreferenceKeys.USERNAME] = value ?: "" - } - } + override val lbAccessToken: PrimitivePreference + get() = stringPreference(PreferenceKeys.LB_ACCESS_TOKEN) - } + override val username: PrimitivePreference + get() = stringPreference(PreferenceKeys.USERNAME) override var linkedServices: List get() { - val jsonString = preferences.getString(LINKED_SERVICES, "") - val type = object : TypeToken>() {}.type - return gson.fromJson(jsonString, type) ?: emptyList() + val jsonString = preferences.getString(LINKED_SERVICES, "") ?: "" + return listSerializer().from(jsonString) } set(value) { - val jsonString = gson.toJson(value) + val jsonString = listSerializer().to(value) setString(LINKED_SERVICES, jsonString) } diff --git a/app/src/main/java/org/listenbrainz/android/repository/preferences/DataStorePreference.kt b/app/src/main/java/org/listenbrainz/android/repository/preferences/DataStorePreference.kt deleted file mode 100644 index 2b79e4e7..00000000 --- a/app/src/main/java/org/listenbrainz/android/repository/preferences/DataStorePreference.kt +++ /dev/null @@ -1,16 +0,0 @@ -package org.listenbrainz.android.repository.preferences - -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.first - -interface DataStorePreference { - suspend fun get(): T = getFlow().first() - - fun getFlow(): Flow - - suspend fun set(value: T) - - /** Update the value of the preference in atomic read-modify-write manner.*/ - suspend fun getAndUpdate(update: (T) -> T): Unit = - throw NotImplementedError("getAndUpdate has not been implemented for this preference.") -} \ No newline at end of file diff --git a/app/src/main/java/org/listenbrainz/android/service/BrainzPlayerNotificationListener.kt b/app/src/main/java/org/listenbrainz/android/service/BrainzPlayerNotificationListener.kt index fac7a298..345b9355 100644 --- a/app/src/main/java/org/listenbrainz/android/service/BrainzPlayerNotificationListener.kt +++ b/app/src/main/java/org/listenbrainz/android/service/BrainzPlayerNotificationListener.kt @@ -6,7 +6,7 @@ import android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK import android.os.Build import androidx.core.content.ContextCompat import com.google.android.exoplayer2.ui.PlayerNotificationManager -import org.listenbrainz.android.util.BrainzPlayerUtils.NOTIFICATION_ID +import org.listenbrainz.android.util.brainzplayer.BrainzPlayerUtils.NOTIFICATION_ID class BrainzPlayerNotificationListener(private val brainzPlayerService: BrainzPlayerService) : PlayerNotificationManager.NotificationListener { diff --git a/app/src/main/java/org/listenbrainz/android/service/BrainzPlayerService.kt b/app/src/main/java/org/listenbrainz/android/service/BrainzPlayerService.kt index 5364f971..925c72ba 100644 --- a/app/src/main/java/org/listenbrainz/android/service/BrainzPlayerService.kt +++ b/app/src/main/java/org/listenbrainz/android/service/BrainzPlayerService.kt @@ -21,11 +21,11 @@ import org.listenbrainz.android.repository.brainzplayer.AlbumRepository import org.listenbrainz.android.repository.preferences.AppPreferences import org.listenbrainz.android.repository.brainzplayer.PlaylistRepository import org.listenbrainz.android.repository.brainzplayer.SongRepository -import org.listenbrainz.android.util.BrainzPlayerExtensions.toMediaMetadataCompat -import org.listenbrainz.android.util.BrainzPlayerNotificationManager -import org.listenbrainz.android.util.BrainzPlayerUtils.MEDIA_ROOT_ID -import org.listenbrainz.android.util.BrainzPlayerUtils.SERVICE_TAG -import org.listenbrainz.android.util.LocalMusicSource +import org.listenbrainz.android.util.brainzplayer.BrainzPlayerExtensions.toMediaMetadataCompat +import org.listenbrainz.android.util.brainzplayer.BrainzPlayerNotificationManager +import org.listenbrainz.android.util.brainzplayer.BrainzPlayerUtils.MEDIA_ROOT_ID +import org.listenbrainz.android.util.brainzplayer.BrainzPlayerUtils.SERVICE_TAG +import org.listenbrainz.android.util.brainzplayer.LocalMusicSource import javax.inject.Inject @AndroidEntryPoint diff --git a/app/src/main/java/org/listenbrainz/android/service/BrainzPlayerServiceConnection.kt b/app/src/main/java/org/listenbrainz/android/service/BrainzPlayerServiceConnection.kt index 942ea63c..2b020908 100644 --- a/app/src/main/java/org/listenbrainz/android/service/BrainzPlayerServiceConnection.kt +++ b/app/src/main/java/org/listenbrainz/android/service/BrainzPlayerServiceConnection.kt @@ -18,7 +18,7 @@ import org.listenbrainz.android.BuildConfig import org.listenbrainz.android.model.PlayingTrack.Companion.toPlayingTrack import org.listenbrainz.android.model.RepeatMode import org.listenbrainz.android.repository.preferences.AppPreferences -import org.listenbrainz.android.util.BrainzPlayerExtensions.isPlaying +import org.listenbrainz.android.util.brainzplayer.BrainzPlayerExtensions.isPlaying import org.listenbrainz.android.util.ListenSubmissionState import org.listenbrainz.android.util.Resource diff --git a/app/src/main/java/org/listenbrainz/android/service/MusicPlaybackPreparer.kt b/app/src/main/java/org/listenbrainz/android/service/MusicPlaybackPreparer.kt index 07dc4f10..96459d61 100644 --- a/app/src/main/java/org/listenbrainz/android/service/MusicPlaybackPreparer.kt +++ b/app/src/main/java/org/listenbrainz/android/service/MusicPlaybackPreparer.kt @@ -7,7 +7,7 @@ import android.support.v4.media.MediaMetadataCompat import android.support.v4.media.session.PlaybackStateCompat import com.google.android.exoplayer2.Player import com.google.android.exoplayer2.ext.mediasession.MediaSessionConnector -import org.listenbrainz.android.util.LocalMusicSource +import org.listenbrainz.android.util.brainzplayer.LocalMusicSource class MusicPlaybackPreparer(private val localMusicSource: LocalMusicSource, private val playerPrepared: (MediaMetadataCompat?) -> Unit) : MediaSessionConnector.PlaybackPreparer { diff --git a/app/src/main/java/org/listenbrainz/android/ui/components/Buttons.kt b/app/src/main/java/org/listenbrainz/android/ui/components/Buttons.kt index d6019841..929ff4f1 100644 --- a/app/src/main/java/org/listenbrainz/android/ui/components/Buttons.kt +++ b/app/src/main/java/org/listenbrainz/android/ui/components/Buttons.kt @@ -9,7 +9,7 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.vector.ImageVector -import org.listenbrainz.android.util.BrainzPlayerExtensions.toSong +import org.listenbrainz.android.util.brainzplayer.BrainzPlayerExtensions.toSong import org.listenbrainz.android.viewmodel.BrainzPlayerViewModel @OptIn(ExperimentalAnimationApi::class) diff --git a/app/src/main/java/org/listenbrainz/android/ui/screens/brainzplayer/AlbumScreen.kt b/app/src/main/java/org/listenbrainz/android/ui/screens/brainzplayer/AlbumScreen.kt index 0cd08efa..af20cc26 100644 --- a/app/src/main/java/org/listenbrainz/android/ui/screens/brainzplayer/AlbumScreen.kt +++ b/app/src/main/java/org/listenbrainz/android/ui/screens/brainzplayer/AlbumScreen.kt @@ -44,7 +44,7 @@ import org.listenbrainz.android.ui.components.BPLibraryEmptyMessage import org.listenbrainz.android.ui.components.ListenCardSmall import org.listenbrainz.android.ui.components.forwardingPainter import org.listenbrainz.android.ui.theme.ListenBrainzTheme -import org.listenbrainz.android.util.BrainzPlayerExtensions.toSong +import org.listenbrainz.android.util.brainzplayer.BrainzPlayerExtensions.toSong import org.listenbrainz.android.viewmodel.AlbumViewModel import org.listenbrainz.android.viewmodel.BrainzPlayerViewModel diff --git a/app/src/main/java/org/listenbrainz/android/ui/screens/brainzplayer/ArtistScreen.kt b/app/src/main/java/org/listenbrainz/android/ui/screens/brainzplayer/ArtistScreen.kt index eac62aef..990267f8 100644 --- a/app/src/main/java/org/listenbrainz/android/ui/screens/brainzplayer/ArtistScreen.kt +++ b/app/src/main/java/org/listenbrainz/android/ui/screens/brainzplayer/ArtistScreen.kt @@ -48,7 +48,7 @@ import org.listenbrainz.android.ui.components.BPLibraryEmptyMessage import org.listenbrainz.android.ui.components.ListenCardSmall import org.listenbrainz.android.ui.components.forwardingPainter import org.listenbrainz.android.ui.theme.ListenBrainzTheme -import org.listenbrainz.android.util.BrainzPlayerExtensions.toSong +import org.listenbrainz.android.util.brainzplayer.BrainzPlayerExtensions.toSong import org.listenbrainz.android.viewmodel.AlbumViewModel import org.listenbrainz.android.viewmodel.ArtistViewModel import org.listenbrainz.android.viewmodel.BrainzPlayerViewModel diff --git a/app/src/main/java/org/listenbrainz/android/ui/screens/brainzplayer/BrainzPlayerBackDropScreen.kt b/app/src/main/java/org/listenbrainz/android/ui/screens/brainzplayer/BrainzPlayerBackDropScreen.kt index aa42bf31..e6716d58 100644 --- a/app/src/main/java/org/listenbrainz/android/ui/screens/brainzplayer/BrainzPlayerBackDropScreen.kt +++ b/app/src/main/java/org/listenbrainz/android/ui/screens/brainzplayer/BrainzPlayerBackDropScreen.kt @@ -87,11 +87,10 @@ import org.listenbrainz.android.ui.components.ListenCardSmall import org.listenbrainz.android.ui.components.PlayPauseIcon import org.listenbrainz.android.ui.components.SeekBar import org.listenbrainz.android.ui.screens.brainzplayer.ui.components.basicMarquee +import org.listenbrainz.android.util.brainzplayer.BrainzPlayerExtensions.toSong import org.listenbrainz.android.ui.theme.ListenBrainzTheme -import org.listenbrainz.android.util.BrainzPlayerExtensions.toSong import org.listenbrainz.android.util.CacheService import org.listenbrainz.android.util.Constants.RECENTLY_PLAYED_KEY -import org.listenbrainz.android.util.SongViewPager import org.listenbrainz.android.viewmodel.BrainzPlayerViewModel import org.listenbrainz.android.viewmodel.PlaylistViewModel import kotlin.math.absoluteValue @@ -115,7 +114,9 @@ fun BrainzPlayerBackDropScreen( val repeatMode by brainzPlayerViewModel.repeatMode.collectAsState() /** 56.dp is default bottom navigation height. 80.dp is our mini player's height. */ - val headerHeight by animateDpAsState(targetValue = if (currentlyPlayingSong.title == "null" && currentlyPlayingSong.artist == "null") 56.dp else 136.dp) + val headerHeight by animateDpAsState(targetValue = if (currentlyPlayingSong.title == "null" && currentlyPlayingSong.artist == "null") 56.dp else 136.dp, + label = "header" + ) val isPlaying = brainzPlayerViewModel.isPlaying.collectAsState().value BackdropScaffold( diff --git a/app/src/main/java/org/listenbrainz/android/ui/screens/brainzplayer/PlaylistScreen.kt b/app/src/main/java/org/listenbrainz/android/ui/screens/brainzplayer/PlaylistScreen.kt index c877ecbd..f8840e0d 100644 --- a/app/src/main/java/org/listenbrainz/android/ui/screens/brainzplayer/PlaylistScreen.kt +++ b/app/src/main/java/org/listenbrainz/android/ui/screens/brainzplayer/PlaylistScreen.kt @@ -40,7 +40,7 @@ import org.listenbrainz.android.model.Playlist import org.listenbrainz.android.ui.components.ListenCardSmall import org.listenbrainz.android.ui.components.forwardingPainter import org.listenbrainz.android.ui.theme.ListenBrainzTheme -import org.listenbrainz.android.util.BrainzPlayerExtensions.toSong +import org.listenbrainz.android.util.brainzplayer.BrainzPlayerExtensions.toSong import org.listenbrainz.android.viewmodel.BrainzPlayerViewModel import org.listenbrainz.android.viewmodel.PlaylistViewModel diff --git a/app/src/main/java/org/listenbrainz/android/ui/screens/brainzplayer/SongScreen.kt b/app/src/main/java/org/listenbrainz/android/ui/screens/brainzplayer/SongScreen.kt index 1251750e..cc9d9088 100644 --- a/app/src/main/java/org/listenbrainz/android/ui/screens/brainzplayer/SongScreen.kt +++ b/app/src/main/java/org/listenbrainz/android/ui/screens/brainzplayer/SongScreen.kt @@ -39,7 +39,7 @@ import org.listenbrainz.android.R import org.listenbrainz.android.model.PlayableType import org.listenbrainz.android.ui.components.BPLibraryEmptyMessage import org.listenbrainz.android.ui.components.forwardingPainter -import org.listenbrainz.android.util.BrainzPlayerExtensions.toSong +import org.listenbrainz.android.util.brainzplayer.BrainzPlayerExtensions.toSong import org.listenbrainz.android.viewmodel.BrainzPlayerViewModel import org.listenbrainz.android.viewmodel.PlaylistViewModel import org.listenbrainz.android.viewmodel.SongViewModel diff --git a/app/src/main/java/org/listenbrainz/android/util/SongViewPager.kt b/app/src/main/java/org/listenbrainz/android/ui/screens/brainzplayer/SongViewPager.kt similarity index 99% rename from app/src/main/java/org/listenbrainz/android/util/SongViewPager.kt rename to app/src/main/java/org/listenbrainz/android/ui/screens/brainzplayer/SongViewPager.kt index 610e524b..142daa30 100644 --- a/app/src/main/java/org/listenbrainz/android/util/SongViewPager.kt +++ b/app/src/main/java/org/listenbrainz/android/ui/screens/brainzplayer/SongViewPager.kt @@ -1,4 +1,4 @@ -package org.listenbrainz.android.util +package org.listenbrainz.android.ui.screens.brainzplayer import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.background diff --git a/app/src/main/java/org/listenbrainz/android/ui/screens/main/MainActivity.kt b/app/src/main/java/org/listenbrainz/android/ui/screens/main/MainActivity.kt index 48890922..6081ac50 100644 --- a/app/src/main/java/org/listenbrainz/android/ui/screens/main/MainActivity.kt +++ b/app/src/main/java/org/listenbrainz/android/ui/screens/main/MainActivity.kt @@ -15,7 +15,12 @@ import androidx.compose.material3.Scaffold import androidx.compose.material3.Snackbar import androidx.compose.material3.SnackbarHost import androidx.compose.material3.SnackbarHostState -import androidx.compose.runtime.* +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.setValue import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.compose.LifecycleStartEffect @@ -51,7 +56,7 @@ class MainActivity : ComponentActivity() { dashBoardViewModel = ViewModelProvider(this)[DashBoardViewModel::class.java] setContent { - ListenBrainzTheme { + ListenBrainzTheme(appPreferences = remember { dashBoardViewModel.appPreferences }) { // TODO: Since this view-model will remain throughout the lifecycle of the app, // we can have tasks which require such lifecycle access or longevity. We can get this view-model's // instance anywhere when we initialize it as a hilt view-model. diff --git a/app/src/main/java/org/listenbrainz/android/ui/theme/Theme.kt b/app/src/main/java/org/listenbrainz/android/ui/theme/Theme.kt index 6b47dd33..5272f27d 100644 --- a/app/src/main/java/org/listenbrainz/android/ui/theme/Theme.kt +++ b/app/src/main/java/org/listenbrainz/android/ui/theme/Theme.kt @@ -201,7 +201,7 @@ fun ListenBrainzTheme( systemTheme: Boolean = isSystemInDarkTheme(), systemUiController: SystemUiController = rememberSystemUiController(), context: Context = LocalContext.current, - appPreferences: AppPreferences = AppPreferencesImpl(context), + appPreferences: AppPreferences = remember { AppPreferencesImpl(context) }, // Dynamic color is available on Android 12+ //dynamicColor: Boolean = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S, // dynamicColor: Boolean = false,//Build.VERSION.SDK_INT >= Build.VERSION_CODES.S, diff --git a/app/src/main/java/org/listenbrainz/android/util/AlbumsData.kt b/app/src/main/java/org/listenbrainz/android/util/brainzplayer/AlbumsData.kt similarity index 97% rename from app/src/main/java/org/listenbrainz/android/util/AlbumsData.kt rename to app/src/main/java/org/listenbrainz/android/util/brainzplayer/AlbumsData.kt index cf9fe6e7..fb7a690b 100644 --- a/app/src/main/java/org/listenbrainz/android/util/AlbumsData.kt +++ b/app/src/main/java/org/listenbrainz/android/util/brainzplayer/AlbumsData.kt @@ -1,4 +1,4 @@ -package org.listenbrainz.android.util +package org.listenbrainz.android.util.brainzplayer import android.os.Build import android.provider.MediaStore diff --git a/app/src/main/java/org/listenbrainz/android/util/BrainzPlayerExtensions.kt b/app/src/main/java/org/listenbrainz/android/util/brainzplayer/BrainzPlayerExtensions.kt similarity index 99% rename from app/src/main/java/org/listenbrainz/android/util/BrainzPlayerExtensions.kt rename to app/src/main/java/org/listenbrainz/android/util/brainzplayer/BrainzPlayerExtensions.kt index 9a5b4bd3..3d5a132d 100644 --- a/app/src/main/java/org/listenbrainz/android/util/BrainzPlayerExtensions.kt +++ b/app/src/main/java/org/listenbrainz/android/util/brainzplayer/BrainzPlayerExtensions.kt @@ -1,4 +1,4 @@ -package org.listenbrainz.android.util +package org.listenbrainz.android.util.brainzplayer import android.content.ContentResolver import android.content.Context diff --git a/app/src/main/java/org/listenbrainz/android/util/BrainzPlayerNotificationManager.kt b/app/src/main/java/org/listenbrainz/android/util/brainzplayer/BrainzPlayerNotificationManager.kt similarity index 92% rename from app/src/main/java/org/listenbrainz/android/util/BrainzPlayerNotificationManager.kt rename to app/src/main/java/org/listenbrainz/android/util/brainzplayer/BrainzPlayerNotificationManager.kt index af2d2a43..58ebc716 100644 --- a/app/src/main/java/org/listenbrainz/android/util/BrainzPlayerNotificationManager.kt +++ b/app/src/main/java/org/listenbrainz/android/util/brainzplayer/BrainzPlayerNotificationManager.kt @@ -1,4 +1,4 @@ -package org.listenbrainz.android.util +package org.listenbrainz.android.util.brainzplayer import android.app.PendingIntent import android.content.Context @@ -10,9 +10,9 @@ import com.google.android.exoplayer2.Player import com.google.android.exoplayer2.ui.PlayerNotificationManager import kotlinx.coroutines.* import org.listenbrainz.android.R -import org.listenbrainz.android.util.BrainzPlayerExtensions.bitmap -import org.listenbrainz.android.util.BrainzPlayerUtils.NOTIFICATION_CHANNEL_ID -import org.listenbrainz.android.util.BrainzPlayerUtils.NOTIFICATION_ID +import org.listenbrainz.android.util.brainzplayer.BrainzPlayerExtensions.bitmap +import org.listenbrainz.android.util.brainzplayer.BrainzPlayerUtils.NOTIFICATION_CHANNEL_ID +import org.listenbrainz.android.util.brainzplayer.BrainzPlayerUtils.NOTIFICATION_ID class BrainzPlayerNotificationManager( private val context : Context, diff --git a/app/src/main/java/org/listenbrainz/android/util/BrainzPlayerUtils.kt b/app/src/main/java/org/listenbrainz/android/util/brainzplayer/BrainzPlayerUtils.kt similarity index 82% rename from app/src/main/java/org/listenbrainz/android/util/BrainzPlayerUtils.kt rename to app/src/main/java/org/listenbrainz/android/util/brainzplayer/BrainzPlayerUtils.kt index 92cf30aa..5433de78 100644 --- a/app/src/main/java/org/listenbrainz/android/util/BrainzPlayerUtils.kt +++ b/app/src/main/java/org/listenbrainz/android/util/brainzplayer/BrainzPlayerUtils.kt @@ -1,4 +1,4 @@ -package org.listenbrainz.android.util +package org.listenbrainz.android.util.brainzplayer object BrainzPlayerUtils { const val SONG_COLLECTION = "songs" diff --git a/app/src/main/java/org/listenbrainz/android/util/LocalMusicSource.kt b/app/src/main/java/org/listenbrainz/android/util/brainzplayer/LocalMusicSource.kt similarity index 98% rename from app/src/main/java/org/listenbrainz/android/util/LocalMusicSource.kt rename to app/src/main/java/org/listenbrainz/android/util/brainzplayer/LocalMusicSource.kt index bd9c28da..5230a32e 100644 --- a/app/src/main/java/org/listenbrainz/android/util/LocalMusicSource.kt +++ b/app/src/main/java/org/listenbrainz/android/util/brainzplayer/LocalMusicSource.kt @@ -1,4 +1,4 @@ -package org.listenbrainz.android.util +package org.listenbrainz.android.util.brainzplayer import android.support.v4.media.MediaBrowserCompat import android.support.v4.media.MediaDescriptionCompat diff --git a/app/src/main/java/org/listenbrainz/android/util/MusicSource.kt b/app/src/main/java/org/listenbrainz/android/util/brainzplayer/MusicSource.kt similarity index 89% rename from app/src/main/java/org/listenbrainz/android/util/MusicSource.kt rename to app/src/main/java/org/listenbrainz/android/util/brainzplayer/MusicSource.kt index f82ca37f..0a5ed521 100644 --- a/app/src/main/java/org/listenbrainz/android/util/MusicSource.kt +++ b/app/src/main/java/org/listenbrainz/android/util/brainzplayer/MusicSource.kt @@ -1,4 +1,4 @@ -package org.listenbrainz.android.util +package org.listenbrainz.android.util.brainzplayer import android.support.v4.media.MediaBrowserCompat import android.support.v4.media.MediaMetadataCompat diff --git a/app/src/main/java/org/listenbrainz/android/util/SongsData.kt b/app/src/main/java/org/listenbrainz/android/util/brainzplayer/SongsData.kt similarity index 98% rename from app/src/main/java/org/listenbrainz/android/util/SongsData.kt rename to app/src/main/java/org/listenbrainz/android/util/brainzplayer/SongsData.kt index 3b646706..2ac83bd2 100644 --- a/app/src/main/java/org/listenbrainz/android/util/SongsData.kt +++ b/app/src/main/java/org/listenbrainz/android/util/brainzplayer/SongsData.kt @@ -1,4 +1,4 @@ -package org.listenbrainz.android.util +package org.listenbrainz.android.util.brainzplayer import android.content.ContentUris import android.os.Build diff --git a/app/src/main/java/org/listenbrainz/android/util/datastore/DataStoreSerializers.kt b/app/src/main/java/org/listenbrainz/android/util/datastore/DataStoreSerializers.kt new file mode 100644 index 00000000..93674956 --- /dev/null +++ b/app/src/main/java/org/listenbrainz/android/util/datastore/DataStoreSerializers.kt @@ -0,0 +1,16 @@ +package org.listenbrainz.android.util.datastore + +import com.jasjeet.typesafe_datastore.DataStoreSerializer +import org.listenbrainz.android.model.UiMode +import org.listenbrainz.android.model.UiMode.Companion.asUiMode + +object DataStoreSerializers { + val themeSerializer: DataStoreSerializer + get() = object: DataStoreSerializer { + override fun from(value: String): UiMode = value.asUiMode() + + override fun to(value: UiMode): String = value.name + + override fun default(): UiMode = UiMode.FOLLOW_SYSTEM + } +} \ No newline at end of file diff --git a/app/src/main/java/org/listenbrainz/android/util/migrations/BlacklistMigration.kt b/app/src/main/java/org/listenbrainz/android/util/migrations/BlacklistMigration.kt new file mode 100644 index 00000000..a9215c31 --- /dev/null +++ b/app/src/main/java/org/listenbrainz/android/util/migrations/BlacklistMigration.kt @@ -0,0 +1,31 @@ +package org.listenbrainz.android.util.migrations + +import androidx.datastore.core.DataMigration +import androidx.datastore.preferences.core.Preferences +import com.google.gson.Gson +import com.jasjeet.typesafe_datastore.migrations.CustomMigration +import com.jasjeet.typesafe_datastore_gson.AutoTypedDataStore.Companion.listSerializer +import org.listenbrainz.android.repository.preferences.AppPreferencesImpl.Companion.PreferenceKeys + +val blacklistMigration: DataMigration = + CustomMigration( + currentKey = PreferenceKeys.LISTENING_BLACKLIST, + newKey = PreferenceKeys.LISTENING_WHITELIST, + ) { currentData -> + val serializer = listSerializer() + val blacklist = serializer.from(currentData[PreferenceKeys.LISTENING_BLACKLIST] ?: "") + val appList = serializer.from(currentData[PreferenceKeys.LISTENING_APPS] ?: "") + + val whitelist = serializer.from(currentData[PreferenceKeys.LISTENING_WHITELIST] ?: "").toMutableSet() + appList.forEach { pkg -> + if (!blacklist.contains(pkg)) { + whitelist.add(pkg) + } + } + + val mutablePreferences = currentData.toMutablePreferences() + mutablePreferences[PreferenceKeys.LISTENING_WHITELIST] = Gson().toJson(whitelist.toList()) + mutablePreferences.remove(PreferenceKeys.LISTENING_BLACKLIST) // Clear old stale data and key. + + return@CustomMigration mutablePreferences.toPreferences() + } \ No newline at end of file diff --git a/app/src/main/java/org/listenbrainz/android/viewmodel/AboutViewModel.kt b/app/src/main/java/org/listenbrainz/android/viewmodel/AboutViewModel.kt index 92861eb7..556d2157 100644 --- a/app/src/main/java/org/listenbrainz/android/viewmodel/AboutViewModel.kt +++ b/app/src/main/java/org/listenbrainz/android/viewmodel/AboutViewModel.kt @@ -1,19 +1,14 @@ package org.listenbrainz.android.viewmodel -import android.app.Application -import androidx.lifecycle.AndroidViewModel -import androidx.lifecycle.viewModelScope +import androidx.lifecycle.ViewModel import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.launch -import org.listenbrainz.android.repository.listens.ListensRepository import org.listenbrainz.android.repository.preferences.AppPreferences import javax.inject.Inject @HiltViewModel class AboutViewModel @Inject constructor( - val appPreferences: AppPreferences, - application: Application, -) : AndroidViewModel(application) { + val appPreferences: AppPreferences +) : ViewModel() { fun version(): String { return appPreferences.version diff --git a/app/src/main/java/org/listenbrainz/android/viewmodel/BrainzPlayerViewModel.kt b/app/src/main/java/org/listenbrainz/android/viewmodel/BrainzPlayerViewModel.kt index 5f6208e3..3315d277 100644 --- a/app/src/main/java/org/listenbrainz/android/viewmodel/BrainzPlayerViewModel.kt +++ b/app/src/main/java/org/listenbrainz/android/viewmodel/BrainzPlayerViewModel.kt @@ -27,12 +27,12 @@ import org.listenbrainz.android.repository.brainzplayer.SongRepository import org.listenbrainz.android.repository.preferences.AppPreferences import org.listenbrainz.android.service.BrainzPlayerService import org.listenbrainz.android.service.BrainzPlayerServiceConnection -import org.listenbrainz.android.util.BrainzPlayerExtensions.currentPlaybackPosition -import org.listenbrainz.android.util.BrainzPlayerExtensions.isPlayEnabled -import org.listenbrainz.android.util.BrainzPlayerExtensions.isPlaying -import org.listenbrainz.android.util.BrainzPlayerExtensions.isPrepared -import org.listenbrainz.android.util.BrainzPlayerExtensions.toSong -import org.listenbrainz.android.util.BrainzPlayerUtils.MEDIA_ROOT_ID +import org.listenbrainz.android.util.brainzplayer.BrainzPlayerExtensions.currentPlaybackPosition +import org.listenbrainz.android.util.brainzplayer.BrainzPlayerExtensions.isPlayEnabled +import org.listenbrainz.android.util.brainzplayer.BrainzPlayerExtensions.isPlaying +import org.listenbrainz.android.util.brainzplayer.BrainzPlayerExtensions.isPrepared +import org.listenbrainz.android.util.brainzplayer.BrainzPlayerExtensions.toSong +import org.listenbrainz.android.util.brainzplayer.BrainzPlayerUtils.MEDIA_ROOT_ID import org.listenbrainz.android.util.Resource import javax.inject.Inject diff --git a/app/src/main/java/org/listenbrainz/android/viewmodel/DashBoardViewModel.kt b/app/src/main/java/org/listenbrainz/android/viewmodel/DashBoardViewModel.kt index ad259b05..8fc369e3 100644 --- a/app/src/main/java/org/listenbrainz/android/viewmodel/DashBoardViewModel.kt +++ b/app/src/main/java/org/listenbrainz/android/viewmodel/DashBoardViewModel.kt @@ -25,7 +25,7 @@ import javax.inject.Inject @HiltViewModel class DashBoardViewModel @Inject constructor( - private val appPreferences: AppPreferences, + val appPreferences: AppPreferences, private val application: Application, private val remotePlaybackHandler: RemotePlaybackHandler, @IoDispatcher private val ioDispatcher: CoroutineDispatcher diff --git a/app/src/test/java/org/listenbrainz/android/FeedPagingSourceTest.kt b/app/src/test/java/org/listenbrainz/android/FeedPagingSourceTest.kt new file mode 100644 index 00000000..a0be5ca3 --- /dev/null +++ b/app/src/test/java/org/listenbrainz/android/FeedPagingSourceTest.kt @@ -0,0 +1,28 @@ +package org.listenbrainz.android + +import androidx.paging.PagingSource +import kotlinx.coroutines.ExperimentalCoroutinesApi +import org.junit.Test +import org.junit.runner.RunWith +import org.listenbrainz.android.model.feed.FeedData +import org.listenbrainz.android.ui.screens.feed.FollowListensPagingSource +import org.listenbrainz.sharedtest.utils.ResourceString +import org.listenbrainz.sharedtest.utils.ResourceString.toClass +import org.mockito.junit.MockitoJUnitRunner + +@OptIn(ExperimentalCoroutinesApi::class) +@RunWith(MockitoJUnitRunner::class) +class FeedPagingSourceTest: BaseUnitTest() { + @Test + fun test() { + val expectedFeedData = + FollowListensPagingSource.processFeedEvents( + ResourceString.my_feed_page_1.toClass() + ) + val expectedResult = PagingSource.LoadResult.Page( + data = expectedFeedData, + prevKey = null, + nextKey = expectedFeedData.last().event.created + ) + } +} \ No newline at end of file diff --git a/app/src/test/java/org/listenbrainz/android/FeedRepositoryTest.kt b/app/src/test/java/org/listenbrainz/android/FeedRepositoryTest.kt new file mode 100644 index 00000000..e32aa849 --- /dev/null +++ b/app/src/test/java/org/listenbrainz/android/FeedRepositoryTest.kt @@ -0,0 +1,125 @@ +package org.listenbrainz.android + +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import junit.framework.TestCase.assertEquals +import kotlinx.coroutines.ExperimentalCoroutinesApi +import okhttp3.mockwebserver.Dispatcher +import okhttp3.mockwebserver.MockResponse +import okhttp3.mockwebserver.MockWebServer +import okhttp3.mockwebserver.RecordedRequest +import org.junit.After +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.listenbrainz.android.model.SocialResponse +import org.listenbrainz.android.model.feed.FeedData +import org.listenbrainz.android.model.feed.FeedEventDeletionData +import org.listenbrainz.android.model.feed.FeedEventVisibilityData +import org.listenbrainz.android.repository.feed.FeedRepository +import org.listenbrainz.android.repository.feed.FeedRepositoryImpl +import org.listenbrainz.android.service.FeedService +import org.listenbrainz.android.util.Resource +import org.listenbrainz.sharedtest.utils.EntityTestUtils.testUsername +import org.listenbrainz.sharedtest.utils.ResourceString.follow_listens_page_1 +import org.listenbrainz.sharedtest.utils.ResourceString.my_feed_page_1 +import org.listenbrainz.sharedtest.utils.ResourceString.similar_listens_page_1 +import org.listenbrainz.sharedtest.utils.ResourceString.status_ok +import org.listenbrainz.sharedtest.utils.RetrofitUtils +import org.mockito.junit.MockitoJUnitRunner + +@OptIn(ExperimentalCoroutinesApi::class) +@RunWith(MockitoJUnitRunner::class) +class FeedRepositoryTest : BaseUnitTest() { + + private lateinit var webServer: MockWebServer + private lateinit var repository: FeedRepository + @Before + fun setup() { + webServer = MockWebServer() + webServer.dispatcher = object : Dispatcher() { + override fun dispatch(request: RecordedRequest): MockResponse { + return when (request.path) { + + "/user/$testUsername/feed/events?count=${FeedRepository.FeedEventCount}" -> MockResponse().setResponseCode(200).setBody( + my_feed_page_1 + ) + + "/user/$testUsername/feed/events/listens/following?count=${FeedRepository.FeedListensCount}" -> MockResponse().setResponseCode(200).setBody( + follow_listens_page_1 + ) + + "/user/$testUsername/feed/events/listens/similar?count=${FeedRepository.FeedListensCount}" -> MockResponse().setResponseCode(200).setBody( + similar_listens_page_1 + ) + + "/user/$testUsername/feed/events/delete" -> MockResponse().setResponseCode(200).setBody( + status_ok + ) + + "/user/$testUsername/feed/events/hide" -> MockResponse().setResponseCode(200).setBody( + status_ok + ) + + "/user/$testUsername/feed/events/unhide" -> MockResponse().setResponseCode(200).setBody( + status_ok + ) + + else -> MockResponse().setResponseCode(400) + } + + } + } + webServer.start() + val service = RetrofitUtils.createTestService(FeedService::class.java, webServer.url("/")) + repository = FeedRepositoryImpl(service) + } + + @After + fun teardown() { + webServer.close() + } + + @Test + fun `test getFeedEvents`() = test { + val result = repository.getFeedEvents(testUsername) + assertEquals(Resource.Status.SUCCESS, result.status) + assertEquals(Gson().fromJson(my_feed_page_1, TypeToken.get(FeedData::class.java)), result.data) + } + + @Test + fun `test getFeedFollowListens`() = test { + val result = repository.getFeedFollowListens(testUsername) + assertEquals(Resource.Status.SUCCESS, result.status) + assertEquals(Gson().fromJson(follow_listens_page_1, TypeToken.get(FeedData::class.java)), result.data) + } + + @Test + fun `getFeedSimilarListens - success`() = test { + val result = repository.getFeedSimilarListens(testUsername) + assertEquals(Resource.Status.SUCCESS, result.status) + assertEquals(Gson().fromJson(similar_listens_page_1, TypeToken.get(FeedData::class.java)), result.data) + } + + @Test + fun `deleteEvent - success`() = test { + val result = repository.deleteEvent(testUsername, FeedEventDeletionData()) + assertEquals(Resource.Status.SUCCESS, result.status) + assertEquals(Gson().fromJson(status_ok, TypeToken.get(SocialResponse::class.java)), result.data) + } + + @Test + fun `hideEvent - success`() = test { + val result = repository.hideEvent(testUsername, FeedEventVisibilityData()) + assertEquals(Resource.Status.SUCCESS, result.status) + assertEquals(Gson().fromJson(status_ok, TypeToken.get(SocialResponse::class.java)), result.data) + } + + @Test + fun `unhideEvent - success`() = test { + val result = repository.hideEvent(testUsername, FeedEventVisibilityData()) + assertEquals(Resource.Status.SUCCESS, result.status) + assertEquals(Gson().fromJson(status_ok, TypeToken.get(SocialResponse::class.java)), result.data) + } + +} \ No newline at end of file diff --git a/app/src/test/java/org/listenbrainz/android/FeedViewModelTest.kt b/app/src/test/java/org/listenbrainz/android/FeedViewModelTest.kt new file mode 100644 index 00000000..aff28d75 --- /dev/null +++ b/app/src/test/java/org/listenbrainz/android/FeedViewModelTest.kt @@ -0,0 +1,139 @@ +package org.listenbrainz.android + +import com.jasjeet.typesafe_datastore_test.MockPrimitivePreference +import junit.framework.TestCase.assertEquals +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.advanceUntilIdle +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.listenbrainz.android.model.Metadata +import org.listenbrainz.android.model.SocialResponse +import org.listenbrainz.android.model.feed.FeedEvent +import org.listenbrainz.android.model.feed.FeedEventType +import org.listenbrainz.android.model.feed.FeedEventVisibilityData +import org.listenbrainz.android.repository.feed.FeedRepository +import org.listenbrainz.android.repository.listens.ListensRepository +import org.listenbrainz.android.repository.preferences.AppPreferences +import org.listenbrainz.android.repository.remoteplayer.RemotePlaybackHandler +import org.listenbrainz.android.repository.social.SocialRepository +import org.listenbrainz.android.util.Resource +import org.listenbrainz.android.viewmodel.FeedViewModel +import org.listenbrainz.sharedtest.utils.EntityTestUtils.testFamiliarUser +import org.listenbrainz.sharedtest.utils.EntityTestUtils.testUsername +import org.mockito.Mock +import org.mockito.junit.MockitoJUnitRunner +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.wheneverBlocking + +@OptIn(ExperimentalCoroutinesApi::class) +@RunWith(MockitoJUnitRunner::class) +class FeedViewModelTest: BaseUnitTest() +{ + private lateinit var viewModel : FeedViewModel + + @Mock + private lateinit var socialRepository: SocialRepository + + @Mock + private lateinit var feedRepository: FeedRepository + + @Mock + private lateinit var appPreferences: AppPreferences + + @Mock + private lateinit var listensRepository: ListensRepository + + @Mock + private lateinit var remotePlaybackHandler: RemotePlaybackHandler + + @Before + fun setup() { + /*wheneverBlocking { + feedRepository.getFeedEvents(username = testUsername, maxTs = null, count = FeedEventCount) + }.doReturn( + Resource.success(ResourceString.my_feed_page_1.toClass()) + ) + + wheneverBlocking { + feedRepository.getFeedEvents(username = testUsername, maxTs = 1692700512, count = FeedEventCount) + }.doReturn( + Resource.success(ResourceString.my_feed_page_2.toClass()) + ) + + wheneverBlocking { + feedRepository.getFeedSimilarListens(username = testUsername, maxTs = null, count = FeedEventCount) + }.doReturn( + Resource.success(ResourceString.similar_listens_page_1.toClass()) + ) + + wheneverBlocking { + feedRepository.getFeedSimilarListens(username = testUsername, maxTs = 1694881863, count = FeedEventCount) + }.doReturn( + Resource.success(ResourceString.similar_listens_page_2.toClass()) + ) + + wheneverBlocking { + feedRepository.getFeedFollowListens(username = testUsername, maxTs = null, count = FeedEventCount) + }.doReturn( + Resource.success(ResourceString.follow_listens_page_1.toClass()) + ) + + wheneverBlocking { + feedRepository.getFeedFollowListens(username = testUsername, maxTs = 1694878358, count = FeedEventCount) + }.doReturn( + Resource.success(ResourceString.follow_listens_page_2.toClass()) + )*/ + + viewModel = FeedViewModel( + feedRepository, + socialRepository, + listensRepository, + appPreferences, + remotePlaybackHandler, + testDispatcher(), + testDispatcher() + ) + } + + @Test + fun `test init`() = test { + val uiState = viewModel.uiState.value + assertEquals(true, uiState.myFeedState.isDeletedMap.isEmpty()) + assertEquals(true, uiState.myFeedState.isHiddenMap.isEmpty()) + } + + @Test + fun `test hide`() = test { + /** Condition for hide is that the event is not user's. + * Meanwhile for delete, user should be the one who created the event.*/ + val mockEventType = FeedEventType.RECORDING_RECOMMENDATION + val mockEvent = FeedEvent( + 0, + 0, + mockEventType.type, + metadata = Metadata(username = testFamiliarUser), + hidden = false, + username = testFamiliarUser + ) + wheneverBlocking { + appPreferences.username + }.doReturn(MockPrimitivePreference(testUsername)) + + wheneverBlocking { + feedRepository.hideEvent(testUsername, FeedEventVisibilityData(mockEventType.type, mockEvent.id.toString())) + }.doReturn(Resource.success(SocialResponse())) + + // Calling + viewModel.hideOrDeleteEvent( + mockEvent, + mockEventType, + testUsername + ) + + advanceUntilIdle() + + val uiState = viewModel.uiState.value + assertEquals(true, uiState.myFeedState.isHiddenMap[mockEvent.id]) + } +} \ No newline at end of file diff --git a/app/src/test/java/org/listenbrainz/android/yim/YimViewModelTest.kt b/app/src/test/java/org/listenbrainz/android/yim/YimViewModelTest.kt index 72cf9cf3..71e3c801 100644 --- a/app/src/test/java/org/listenbrainz/android/yim/YimViewModelTest.kt +++ b/app/src/test/java/org/listenbrainz/android/yim/YimViewModelTest.kt @@ -5,32 +5,43 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.launch import kotlinx.coroutines.test.StandardTestDispatcher import kotlinx.coroutines.test.resetMain -import kotlinx.coroutines.test.runTest import kotlinx.coroutines.test.setMain import org.junit.After import org.junit.Assert.assertEquals import org.junit.Before import org.junit.Test +import org.junit.runner.RunWith +import org.listenbrainz.android.BaseUnitTest +import org.listenbrainz.android.repository.preferences.AppPreferences import org.listenbrainz.android.util.Resource import org.listenbrainz.android.viewmodel.YimViewModel -import org.listenbrainz.sharedtest.mocks.MockAppPreferences import org.listenbrainz.sharedtest.mocks.MockYimRepository import org.listenbrainz.sharedtest.testdata.YimRepositoryTestData.testYimData import org.listenbrainz.sharedtest.utils.AssertionUtils.checkYimAssertions +import org.mockito.Mock +import org.mockito.junit.MockitoJUnitRunner -class YimViewModelTest{ +@OptIn(ExperimentalCoroutinesApi::class) +@RunWith(MockitoJUnitRunner::class) +class YimViewModelTest: BaseUnitTest() { private lateinit var viewModel : YimViewModel + @Mock + private lateinit var mockAppPreferences: AppPreferences + + @Mock + private lateinit var mockYimRepository: MockYimRepository + @OptIn(ExperimentalCoroutinesApi::class) @Before fun setup(){ Dispatchers.setMain(StandardTestDispatcher()) - viewModel = YimViewModel(MockYimRepository(), MockAppPreferences()) + viewModel = YimViewModel(mockYimRepository, mockAppPreferences) } @Test - fun getDataTest() = runTest { + fun getDataTest() = test { val expected = testYimData launch(Dispatchers.Main) { val resultResource = viewModel.yimData diff --git a/build.gradle b/build.gradle index 84bc209d..a5770334 100644 --- a/build.gradle +++ b/build.gradle @@ -11,6 +11,7 @@ buildscript { work_version = '2.9.0' exoplayer_version = '2.19.1' paging_version = "3.2.1" + typesafe_datastore_version = "1.0.3" } repositories { google() diff --git a/sharedTest/build.gradle b/sharedTest/build.gradle index e5cb41e0..f6cf4841 100644 --- a/sharedTest/build.gradle +++ b/sharedTest/build.gradle @@ -31,6 +31,7 @@ android { dependencies { implementation 'androidx.appcompat:appcompat:1.6.1' implementation 'com.google.android.material:material:1.11.0' + implementation "androidx.datastore:datastore-preferences:1.0.0" //Web Service Setup implementation 'com.google.code.gson:gson:2.10.1' @@ -45,6 +46,7 @@ dependencies { implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.8.0' implementation 'androidx.room:room-testing:2.6.1' debugImplementation "androidx.compose.ui:ui-test-manifest:$compose_version" + implementation "com.github.07jasjeet.typesafe-datastore:typesafe-datastore-test:$typesafe_datastore_version" implementation 'androidx.test:runner:1.5.2' implementation 'androidx.test.ext:junit:1.1.5' diff --git a/sharedTest/src/main/java/org/listenbrainz/sharedtest/mocks/MockAppPreferences.kt b/sharedTest/src/main/java/org/listenbrainz/sharedtest/mocks/MockAppPreferences.kt index d89af250..8bca62c1 100644 --- a/sharedTest/src/main/java/org/listenbrainz/sharedtest/mocks/MockAppPreferences.kt +++ b/sharedTest/src/main/java/org/listenbrainz/sharedtest/mocks/MockAppPreferences.kt @@ -1,21 +1,10 @@ package org.listenbrainz.sharedtest.mocks -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.flow -import org.listenbrainz.android.model.PermissionStatus -import org.listenbrainz.android.model.Playable -import org.listenbrainz.android.model.UiMode -import org.listenbrainz.android.repository.preferences.AppPreferences -import org.listenbrainz.android.repository.preferences.DataStorePreference -import org.listenbrainz.android.util.Constants.Strings.STATUS_LOGGED_IN -import org.listenbrainz.android.util.LinkedService -import org.listenbrainz.sharedtest.utils.EntityTestUtils.testAccessToken -import org.listenbrainz.sharedtest.utils.EntityTestUtils.testUsername - -/* +/** For every new preference, add default value of the concerned shared preference as default value here. */ +/* class MockAppPreferences( override var permissionsPreference: String? = PermissionStatus.NOT_REQUESTED.name, override var onboardingCompleted: Boolean = false, @@ -26,75 +15,28 @@ class MockAppPreferences( override val version: String = "", override val isNotificationServiceAllowed: Boolean = true, override var linkedServices: List = listOf() -) : AppPreferences { - - override val themePreference: DataStorePreference = - object : DataStorePreference { - override fun getFlow(): Flow = flow { emit(UiMode.FOLLOW_SYSTEM) } - - override suspend fun set(value: UiMode) { - TODO("Not yet implemented") - } - } +): AppPreferences { - override val listeningWhitelist: DataStorePreference> = - object : DataStorePreference> { - override fun getFlow(): Flow> = flow {} - - override suspend fun set(value: List) { - TODO("Not yet implemented") - } - } + override val themePreference: ComplexPreference = + mockComplexPreference(UiMode.FOLLOW_SYSTEM) - override val listeningApps: DataStorePreference> = - object : DataStorePreference> { - override fun getFlow(): Flow> = flow {} - - override suspend fun set(value: List) { - TODO("Not yet implemented") - } - } + override val listeningWhitelist: ComplexPreference> = + mockComplexPreference(emptyList()) - override val lbAccessToken: DataStorePreference = - object : DataStorePreference { - override fun getFlow(): Flow = flow { - emit(testAccessToken) - } + override val listeningApps: ComplexPreference> = + mockComplexPreference(emptyList()) - override suspend fun set(value: String) { - TODO("Not yet implemented") - } - } - - override val username: DataStorePreference = - object : DataStorePreference { - override fun getFlow(): Flow = flow { - emit(testUsername) - } - - override suspend fun set(value: String) { - TODO("Not yet implemented") - } - } + override val lbAccessToken: PrimitivePreference = + mockPrimitivePreference(testAccessToken) - override val isListeningAllowed: DataStorePreference = - object : DataStorePreference { - override fun getFlow(): Flow = flow {} - - override suspend fun set(value: Boolean) { - TODO("Not yet implemented") - } - } + override val username: PrimitivePreference = + mockPrimitivePreference(testUsername) - override val shouldListenNewPlayers: DataStorePreference = - object : DataStorePreference { - override fun getFlow(): Flow = flow {} - - override suspend fun set(value: Boolean) { - TODO("Not yet implemented") - } - } + override val isListeningAllowed: PrimitivePreference = + mockPrimitivePreference(true) + override val shouldListenNewPlayers: PrimitivePreference = + mockPrimitivePreference(true) override suspend fun logoutUser() { TODO("Not yet implemented") @@ -104,7 +46,5 @@ class MockAppPreferences( emit(STATUS_LOGGED_IN) } - override suspend fun isUserLoggedIn(): Boolean { - TODO("Not yet implemented") - } -} \ No newline at end of file + override suspend fun isUserLoggedIn(): Boolean = true +}*/