diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..5adef55c9 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +public/** linguist-vendored \ No newline at end of file diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml index e805548aa..8d81632f8 100644 --- a/.idea/kotlinc.xml +++ b/.idea/kotlinc.xml @@ -1,6 +1,6 @@ - \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml index a33bb3181..49d13d9e4 100644 --- a/.idea/vcs.xml +++ b/.idea/vcs.xml @@ -2,7 +2,6 @@ - \ No newline at end of file diff --git a/app/build.gradle.kts b/app/build.gradle.kts index cd1991864..7cf318518 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -1,4 +1,4 @@ -import com.android.build.gradle.internal.cxx.configure.gradleLocalProperties +import org.jetbrains.kotlin.konan.properties.Properties plugins { alias(libs.plugins.android.application) @@ -13,19 +13,26 @@ plugins { } android { + buildFeatures.buildConfig = true + defaultConfig { applicationId = "com.kafka.user" - versionCode = 52 + versionCode = 53 versionName = libs.versions.versionname.toString() - val root = gradleLocalProperties(rootDir) - val googleServerClientId: String = (root.getProperty("GOOGLE_SERVER_CLIENT_ID") - ?: System.getenv("GOOGLE_SERVER_CLIENT_ID")) as String - val pipelessAuthToken: String = (root.getProperty("PIPELESS_AUTH_TOKEN") - ?: System.getenv("PIPELESS_AUTH_TOKEN")) as String + val properties = Properties() + properties.load(project.rootProject.file("local.properties").inputStream()) - buildConfigField("String", "GOOGLE_SERVER_CLIENT_ID", googleServerClientId) - buildConfigField("String", "PIPELESS_AUTH_TOKEN", pipelessAuthToken) + buildConfigField( + "String", + "GOOGLE_SERVER_CLIENT_ID", + properties["PIPELESS_AUTH_TOKEN"]?.toString() ?: System.getenv("PIPELESS_AUTH_TOKEN") + ) + buildConfigField( + "String", + "PIPELESS_AUTH_TOKEN", + properties["PIPELESS_AUTH_TOKEN"]?.toString() ?: System.getenv("PIPELESS_AUTH_TOKEN") + ) } compileOptions { @@ -107,8 +114,6 @@ android { warning += "AutoboxingStateCreation" } - buildFeatures.buildConfig = true - } dependencies { diff --git a/app/src/main/java/com/kafka/user/MainActivity.kt b/app/src/main/java/com/kafka/user/MainActivity.kt index 197a29d50..98d434c5c 100644 --- a/app/src/main/java/com/kafka/user/MainActivity.kt +++ b/app/src/main/java/com/kafka/user/MainActivity.kt @@ -45,10 +45,10 @@ class MainActivity : ComponentActivity() { override fun onNewIntent(intent: Intent?) { super.onNewIntent(intent) - if (::navController.isInitialized) { + if (::navController.isInitialized && intent != null) { navController.handleDeepLink(intent) } else { - Timber.e(Error("navController is not initialized. isFinishing $isFinishing")) + Timber.e(Error("navController is not initialized or intent is null. isFinishing = $isFinishing, intent = $intent")) } } } diff --git a/app/src/main/java/com/kafka/user/fcm/FirebaseMessageService.kt b/app/src/main/java/com/kafka/user/fcm/FirebaseMessageService.kt index 788ef116a..3db944412 100644 --- a/app/src/main/java/com/kafka/user/fcm/FirebaseMessageService.kt +++ b/app/src/main/java/com/kafka/user/fcm/FirebaseMessageService.kt @@ -42,14 +42,20 @@ class FirebaseMessageService : FirebaseMessagingService() { debug { "Received FCM message: $message" } - when { - message.data.isNotEmpty() -> notificationManager.buildNotification( - push = pushMapper.map(message), - pendingIntent = mainActivityIntent() - ) + if (message.data.isNotEmpty()) { + val contentId = message.data["itemId"] - else -> super.onMessageReceived(message) + debug { "FCM contentId $contentId" } } + +// when { +// message.data.isNotEmpty() -> notificationManager.buildNotification( +// push = pushMapper.map(message), +// pendingIntent = mainActivityIntent() +// ) +// +// else -> super.onMessageReceived(message) +// } } override fun onNewToken(token: String) { diff --git a/app/src/main/java/com/kafka/user/injection/PlayerModule.kt b/app/src/main/java/com/kafka/user/injection/PlayerModule.kt index 9272dc60e..c30bb513c 100644 --- a/app/src/main/java/com/kafka/user/injection/PlayerModule.kt +++ b/app/src/main/java/com/kafka/user/injection/PlayerModule.kt @@ -1,13 +1,17 @@ package com.kafka.user.injection +import com.kafka.remote.config.RemoteConfig +import com.kafka.remote.config.isExactAlarmEnabled import com.kafka.user.playback.KafkaPlayerEventLogger import com.kafka.user.playback.PlayerAudioDataSource +import com.sarahang.playback.core.PlayerRemoteConfig import com.sarahang.playback.core.apis.AudioDataSource import com.sarahang.playback.core.apis.PlayerEventLogger import dagger.Binds import dagger.Module import dagger.hilt.InstallIn import dagger.hilt.components.SingletonComponent +import javax.inject.Inject @InstallIn(SingletonComponent::class) @Module @@ -18,4 +22,15 @@ abstract class PlayerModule { @Binds abstract fun playerEventLogger(kafkaPlayerEventLogger: KafkaPlayerEventLogger): PlayerEventLogger + + @Binds + abstract fun PlayerRemoteConfig(playerRemoteConfig: PlayerRemoteConfigImpl): PlayerRemoteConfig +} + +class PlayerRemoteConfigImpl @Inject constructor( + private val remoteConfig: RemoteConfig +) : PlayerRemoteConfig { + override fun isExactAlarmEnabled(): Boolean { + return remoteConfig.isExactAlarmEnabled() + } } diff --git a/core/remote-config/src/main/java/com/kafka/remote/config/RemoteConfigExtensions.kt b/core/remote-config/src/main/java/com/kafka/remote/config/RemoteConfigExtensions.kt index e5060d7b5..195eef202 100644 --- a/core/remote-config/src/main/java/com/kafka/remote/config/RemoteConfigExtensions.kt +++ b/core/remote-config/src/main/java/com/kafka/remote/config/RemoteConfigExtensions.kt @@ -6,6 +6,7 @@ const val DOWNLOADER_TYPE = "downloader_type" const val GOOGLE_LOGIN_ENABLED = "google_login_enabled" const val RECOMMENDATION_ENABLED = "recommendation_data_enabled" const val ONLINE_READER_ENABLED = "online_reader_enabled" +const val EXACT_ALARM_ENABLED = "exact_alarm_enabled" fun RemoteConfig.getPlayerTheme() = get(REMOTE_CONFIG_PLAYER_THEME_KEY) @@ -18,3 +19,5 @@ fun RemoteConfig.isGoogleLoginEnabled() = getBoolean(GOOGLE_LOGIN_ENABLED) fun RemoteConfig.isRecommendationEnabled() = getBoolean(RECOMMENDATION_ENABLED) fun RemoteConfig.isOnlineReaderEnabled() = getBoolean(ONLINE_READER_ENABLED) + +fun RemoteConfig.isExactAlarmEnabled() = getBoolean(EXACT_ALARM_ENABLED) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 76417fc2c..32160df46 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,20 +1,20 @@ [versions] accompanist = "0.34.0" -agp = "8.2.2" -androidxhilt = "1.1.0" +agp = "8.3.0" +androidxhilt = "1.2.0" androidxlifecycle = "2.7.0" -navigation = "2.7.6" -coil = "2.5.0" +navigation = "2.7.7" +coil = "2.6.0" compose-alpha = "2024.01.00-alpha01" -compose-bom = "2024.01.00" +compose-bom = "2024.02.02" constraintlayout = "1.1.0-alpha13" -composecompiler = "1.5.4" -coroutines = "1.7.3" -dagger = "2.50" +composecompiler = "1.5.10" +coroutines = "1.8.0" +dagger = "2.51" icons = "1.0.0" -kotlin = "1.9.20" +kotlin = "1.9.22" kotlin-immutable = "0.3.7" -material3 = "1.2.0-rc01" +material3 = "1.2.1" mixpanel = "7.0.0" okhttp = "4.12.0" paging = "3.2.1" @@ -30,25 +30,25 @@ targetSdk = "33" core-ktx = "1.12.0" androidx-test-ext-junit = "1.1.5" espresso-core = "3.5.1" -uiautomator = "2.2.0" +uiautomator = "2.3.0" benchmark-macro-junit4 = "1.2.3" androidx-baselineprofile = "1.2.3" profileinstaller = "1.3.1" review = "2.0.1" -versionname = "0.12.0" +versionname = "0.13.0" [plugins] android-application = { id = "com.android.application", version.ref = "agp" } android-library = { id = "com.android.library", version.ref = "agp" } android-lint = { id = "com.android.lint", version.ref = "agp" } -cacheFixPlugin = { id = "org.gradle.android.cache-fix", version = "3.0" } +cacheFixPlugin = { id = "org.gradle.android.cache-fix", version = "3.0.1" } kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } kotlin-kapt = { id = "org.jetbrains.kotlin.kapt", version.ref = "kotlin" } -ksp = "com.google.devtools.ksp:1.9.22-1.0.16" -gms-googleServices = "com.google.gms.google-services:4.4.0" +ksp = "com.google.devtools.ksp:1.9.23-1.0.19" +gms-googleServices = "com.google.gms.google-services:4.4.1" firebase-crashlytics = "com.google.firebase.crashlytics:2.9.9" -spotless = "com.diffplug.spotless:6.24.0" +spotless = "com.diffplug.spotless:6.25.0" kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" } hilt = { id = "com.google.dagger.hilt.android", version.ref = "dagger" } androidTest = { id = "com.android.test", version.ref = "agp" } @@ -76,7 +76,7 @@ androidx-collection = "androidx.collection:collection-ktx:1.4.0" androidx-core = "androidx.core:core-ktx:1.12.0" androidx-hilt-compiler = { module = "androidx.hilt:hilt-compiler", version.ref = "androidxhilt" } -androidx-hilt-compose = "androidx.hilt:hilt-navigation-compose:1.1.0" +androidx-hilt-compose = "androidx.hilt:hilt-navigation-compose:1.2.0" androidx-hilt-navigation = { module = "androidx.hilt:hilt-navigation", version.ref = "androidxhilt" } hilt-compiler = { module = "com.google.dagger:hilt-android-compiler", version.ref = "dagger" } @@ -129,16 +129,16 @@ fetch-okhttp = "androidx.tonyodev.fetch2okhttp:xfetch2okhttp:3.1.6" dagger-dagger = { module = "com.google.dagger:dagger", version.ref = "dagger" } -google-bom = "com.google.firebase:firebase-bom:32.7.1" +google-bom = "com.google.firebase:firebase-bom:32.7.4" google-analytics = { module = "com.google.firebase:firebase-analytics" } google-crashlytics = { module = "com.google.firebase:firebase-crashlytics" } google-dynamic_links = { module = "com.google.firebase:firebase-dynamic-links" } google-auth = { module = "com.google.firebase:firebase-auth" } -google-coroutines = "org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.7.3" +google-coroutines = "org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.8.0" google-firestore = { module = "com.google.firebase:firebase-firestore" } google-messaging = { module = "com.google.firebase:firebase-messaging" } google-performance = { module = "com.google.firebase:firebase-perf" } -google-playservices-auth = "com.google.android.gms:play-services-auth:20.7.0" +google-playservices-auth = "com.google.android.gms:play-services-auth:21.0.0" google-remoteConfig = { module = "com.google.firebase:firebase-config" } google-storage = { module = "com.google.firebase:firebase-storage-ktx" } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index e57a6b765..92c1ac1bd 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,7 @@ -#Wed Sep 15 09:55:12 CEST 2021 distributionBase=GRADLE_USER_HOME -distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-bin.zip distributionPath=wrapper/dists -zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip +networkTimeout=10000 +validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists \ No newline at end of file diff --git a/ui/common/src/main/java/org/kafka/common/LazyList.kt b/ui/common/src/main/java/org/kafka/common/LazyList.kt new file mode 100644 index 000000000..f5d091e4b --- /dev/null +++ b/ui/common/src/main/java/org/kafka/common/LazyList.kt @@ -0,0 +1,15 @@ +package org.kafka.common + +import androidx.compose.foundation.lazy.LazyListState +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp + +val LazyListState.elevation: Dp + get() = if (firstVisibleItemIndex == 0) { + // For the first element, use the minimum of scroll offset and default elevation + // i.e. a value between 0 and 4.dp + minOf(firstVisibleItemScrollOffset.toFloat().dp, 24.dp) + } else { + // If not the first element, always set elevation and show the shadow + 24.dp + } diff --git a/ui/common/src/main/java/org/kafka/common/extensions/Elevation.kt b/ui/common/src/main/java/org/kafka/common/extensions/Elevation.kt deleted file mode 100644 index 53085b167..000000000 --- a/ui/common/src/main/java/org/kafka/common/extensions/Elevation.kt +++ /dev/null @@ -1,41 +0,0 @@ -package org.kafka.common.extensions - -import androidx.compose.foundation.ScrollState -import androidx.compose.foundation.lazy.LazyListState -import androidx.compose.foundation.lazy.grid.LazyGridState -import androidx.compose.runtime.derivedStateOf -import androidx.compose.ui.unit.Dp -import androidx.compose.ui.unit.dp -import ui.common.theme.theme.Dimens - -val ScrollState.elevation - get() = derivedStateOf { - this.run { ((value / 100) * 2).coerceAtMost(MaxElevation).dp } ?: 0.dp - } - -val LazyListState.elevation: Dp - get() = if (firstVisibleItemIndex == 0) { - // For the first element, use the minimum of scroll offset and default elevation - // i.e. a value between 0 and 4.dp - minOf(firstVisibleItemScrollOffset.toFloat().dp, 24.dp) - } else { - // If not the first element, always set elevation and show the shadow - 24.dp - } - - -val LazyListState?.elevation - get() = derivedStateOf { - if (this?.firstVisibleItemIndex == 0) { - minOf(firstVisibleItemScrollOffset.toFloat().dp, MaxElevation.dp) - } else { - Dimens.Spacing20 - } - } - - -fun LazyGridState.elevation(maxElevation: Int = 40) = derivedStateOf { - this.run { firstVisibleItemScrollOffset.coerceAtMost(maxElevation) }.dp -} - -private const val MaxElevation = 20 diff --git a/ui/common/src/main/java/org/kafka/common/extensions/elevation.kt b/ui/common/src/main/java/org/kafka/common/extensions/elevation.kt deleted file mode 100644 index 53085b167..000000000 --- a/ui/common/src/main/java/org/kafka/common/extensions/elevation.kt +++ /dev/null @@ -1,41 +0,0 @@ -package org.kafka.common.extensions - -import androidx.compose.foundation.ScrollState -import androidx.compose.foundation.lazy.LazyListState -import androidx.compose.foundation.lazy.grid.LazyGridState -import androidx.compose.runtime.derivedStateOf -import androidx.compose.ui.unit.Dp -import androidx.compose.ui.unit.dp -import ui.common.theme.theme.Dimens - -val ScrollState.elevation - get() = derivedStateOf { - this.run { ((value / 100) * 2).coerceAtMost(MaxElevation).dp } ?: 0.dp - } - -val LazyListState.elevation: Dp - get() = if (firstVisibleItemIndex == 0) { - // For the first element, use the minimum of scroll offset and default elevation - // i.e. a value between 0 and 4.dp - minOf(firstVisibleItemScrollOffset.toFloat().dp, 24.dp) - } else { - // If not the first element, always set elevation and show the shadow - 24.dp - } - - -val LazyListState?.elevation - get() = derivedStateOf { - if (this?.firstVisibleItemIndex == 0) { - minOf(firstVisibleItemScrollOffset.toFloat().dp, MaxElevation.dp) - } else { - Dimens.Spacing20 - } - } - - -fun LazyGridState.elevation(maxElevation: Int = 40) = derivedStateOf { - this.run { firstVisibleItemScrollOffset.coerceAtMost(maxElevation) }.dp -} - -private const val MaxElevation = 20 diff --git a/ui/homepage/src/main/java/org/kafka/homepage/recent/RecentScreen.kt b/ui/homepage/src/main/java/org/kafka/homepage/recent/RecentScreen.kt index db45f7edc..908be05ae 100644 --- a/ui/homepage/src/main/java/org/kafka/homepage/recent/RecentScreen.kt +++ b/ui/homepage/src/main/java/org/kafka/homepage/recent/RecentScreen.kt @@ -16,7 +16,7 @@ import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.kafka.data.entities.RecentItem import kotlinx.collections.immutable.ImmutableList -import org.kafka.common.extensions.elevation +import org.kafka.common.elevation import org.kafka.common.widgets.shadowMaterial import org.kafka.homepage.R import org.kafka.navigation.LocalNavigator diff --git a/ui/item/src/main/java/org/kafka/item/files/Files.kt b/ui/item/src/main/java/org/kafka/item/files/Files.kt index 820deec33..d59d0b0e2 100644 --- a/ui/item/src/main/java/org/kafka/item/files/Files.kt +++ b/ui/item/src/main/java/org/kafka/item/files/Files.kt @@ -16,7 +16,7 @@ import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.kafka.data.entities.File import kotlinx.coroutines.CoroutineScope -import org.kafka.common.extensions.elevation +import org.kafka.common.elevation import org.kafka.common.test.testTagUi import org.kafka.common.widgets.shadowMaterial import org.kafka.navigation.LocalNavigator