Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>

<application
android:name=".OrbitApplication"
Expand Down
18 changes: 18 additions & 0 deletions app/src/main/java/com/yapp/orbit/di/AppVersionModule.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.yapp.orbit.di

import com.yapp.orbit.BuildConfig
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import javax.inject.Named
import javax.inject.Singleton

@Module
@InstallIn(SingletonComponent::class)
object AppVersionModule {
@Provides
@Singleton
@Named("appVersion")
fun provideAppVersion(): String = BuildConfig.VERSION_NAME
}
89 changes: 49 additions & 40 deletions core/datastore/src/main/java/com/yapp/datastore/UserPreferences.kt
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
import java.time.LocalDate
import java.time.format.DateTimeFormatter
import javax.inject.Inject
import javax.inject.Singleton

Expand All @@ -27,7 +26,7 @@ class UserPreferences @Inject constructor(
val ONBOARDING_COMPLETED = booleanPreferencesKey("onboarding_completed")

val FORTUNE_ID = longPreferencesKey("fortune_id")
val FORTUNE_DATE = stringPreferencesKey("fortune_date")
val FORTUNE_DATE_EPOCH = longPreferencesKey("fortune_date_epoch")
val FORTUNE_IMAGE_ID = intPreferencesKey("fortune_image_id")
val FORTUNE_SCORE = intPreferencesKey("fortune_score")
val FORTUNE_SEEN = booleanPreferencesKey("fortune_seen")
Expand All @@ -36,10 +35,13 @@ class UserPreferences @Inject constructor(
val FORTUNE_FAILED = booleanPreferencesKey("fortune_failed")

val FIRST_ALARM_DISMISSED_TODAY = booleanPreferencesKey("first_alarm_dismissed_today")
val FIRST_ALARM_DISMISSED_DATE = stringPreferencesKey("first_alarm_dismissed_date")
val FIRST_ALARM_DISMISSED_DATE_EPOCH = longPreferencesKey("first_alarm_dismissed_date_epoch")

val UPDATE_NOTICE_DONT_SHOW_VERSION = stringPreferencesKey("update_notice_dont_show_version")
val UPDATE_NOTICE_LAST_SHOWN_DATE_EPOCH = longPreferencesKey("update_notice_last_shown_date_epoch")
}

private fun today(): String = LocalDate.now().format(DateTimeFormatter.ISO_DATE)
private fun todayEpoch(): Long = LocalDate.now().toEpochDay()

val userIdFlow: Flow<Long?> = dataStore.data
.catch { emit(emptyPreferences()) }
Expand All @@ -61,9 +63,9 @@ class UserPreferences @Inject constructor(
.map { it[Keys.FORTUNE_ID] }
.distinctUntilChanged()

val fortuneDateFlow: Flow<String?> = dataStore.data
val fortuneDateEpochFlow: Flow<Long?> = dataStore.data
.catch { emit(emptyPreferences()) }
.map { it[Keys.FORTUNE_DATE] }
.map { it[Keys.FORTUNE_DATE_EPOCH] }
.distinctUntilChanged()

val fortuneImageIdFlow: Flow<Int?> = dataStore.data
Expand All @@ -79,18 +81,17 @@ class UserPreferences @Inject constructor(
val hasUnseenFortuneFlow: Flow<Boolean> = dataStore.data
.catch { emit(emptyPreferences()) }
.map { pref ->
pref[Keys.FORTUNE_DATE] == today() &&
pref[Keys.FORTUNE_ID] != null &&
(pref[Keys.FORTUNE_SEEN] != true)
val isToday = pref[Keys.FORTUNE_DATE_EPOCH] == todayEpoch()
isToday && (pref[Keys.FORTUNE_ID] != null) && (pref[Keys.FORTUNE_SEEN] != true)
}
.distinctUntilChanged()

val shouldShowFortuneToolTipFlow: Flow<Boolean> = dataStore.data
.catch { emit(emptyPreferences()) }
.map { pref ->
val hasTodayFortune = pref[Keys.FORTUNE_DATE] == today() && pref[Keys.FORTUNE_ID] != null
val tooltipNotShown = pref[Keys.FORTUNE_TOOLTIP_SHOWN] ?: false
hasTodayFortune && !tooltipNotShown
val hasTodayFortune = (pref[Keys.FORTUNE_DATE_EPOCH] == todayEpoch()) && (pref[Keys.FORTUNE_ID] != null)
val tooltipShown = pref[Keys.FORTUNE_TOOLTIP_SHOWN] ?: false
hasTodayFortune && !tooltipShown
}
.distinctUntilChanged()

Expand All @@ -108,27 +109,31 @@ class UserPreferences @Inject constructor(
.catch { emit(emptyPreferences()) }
.map { pref ->
val flag = pref[Keys.FIRST_ALARM_DISMISSED_TODAY] ?: false
val date = pref[Keys.FIRST_ALARM_DISMISSED_DATE]
flag && date == today()
val isToday = pref[Keys.FIRST_ALARM_DISMISSED_DATE_EPOCH] == todayEpoch()
flag && isToday
}
.distinctUntilChanged()

val updateNoticeDontShowVersionFlow: Flow<String?> = dataStore.data
.catch { emit(emptyPreferences()) }
.map { it[Keys.UPDATE_NOTICE_DONT_SHOW_VERSION] }
.distinctUntilChanged()

val updateNoticeLastShownDateEpochFlow: Flow<Long?> = dataStore.data
.catch { emit(emptyPreferences()) }
.map { it[Keys.UPDATE_NOTICE_LAST_SHOWN_DATE_EPOCH] }
.distinctUntilChanged()

suspend fun saveUserId(userId: Long) {
dataStore.edit { pref ->
pref[Keys.USER_ID] = userId
}
dataStore.edit { it[Keys.USER_ID] = userId }
}

suspend fun saveUserName(userName: String) {
dataStore.edit { pref ->
pref[Keys.USER_NAME] = userName
}
dataStore.edit { it[Keys.USER_NAME] = userName }
}

suspend fun setOnboardingCompleted() {
dataStore.edit { pref ->
pref[Keys.ONBOARDING_COMPLETED] = true
}
dataStore.edit { it[Keys.ONBOARDING_COMPLETED] = true }
}

suspend fun markFortuneCreating() {
Expand All @@ -140,10 +145,12 @@ class UserPreferences @Inject constructor(

suspend fun markFortuneCreated(fortuneId: Long) {
dataStore.edit { pref ->
val isNewForToday = pref[Keys.FORTUNE_ID] != fortuneId || pref[Keys.FORTUNE_DATE] != today()
val today = todayEpoch()
val prevDate = pref[Keys.FORTUNE_DATE_EPOCH]
val isNewForToday = (pref[Keys.FORTUNE_ID] != fortuneId) || (prevDate != today)

pref[Keys.FORTUNE_ID] = fortuneId
pref[Keys.FORTUNE_DATE] = today()
pref[Keys.FORTUNE_DATE_EPOCH] = today
pref[Keys.FORTUNE_CREATING] = false
pref[Keys.FORTUNE_FAILED] = false

Expand All @@ -162,44 +169,46 @@ class UserPreferences @Inject constructor(
}

suspend fun markFortuneSeen() {
dataStore.edit { pref ->
pref[Keys.FORTUNE_SEEN] = true
}
dataStore.edit { it[Keys.FORTUNE_SEEN] = true }
}

suspend fun markFortuneTooltipShown() {
dataStore.edit { pref ->
pref[Keys.FORTUNE_TOOLTIP_SHOWN] = true
}
dataStore.edit { it[Keys.FORTUNE_TOOLTIP_SHOWN] = true }
}

suspend fun saveFortuneImageId(imageResId: Int) {
dataStore.edit { pref ->
pref[Keys.FORTUNE_IMAGE_ID] = imageResId
}
dataStore.edit { it[Keys.FORTUNE_IMAGE_ID] = imageResId }
}

suspend fun saveFortuneScore(score: Int) {
dataStore.edit { pref ->
pref[Keys.FORTUNE_SCORE] = score
}
dataStore.edit { it[Keys.FORTUNE_SCORE] = score }
}

suspend fun markFirstAlarmDismissedToday() {
dataStore.edit { pref ->
pref[Keys.FIRST_ALARM_DISMISSED_TODAY] = true
pref[Keys.FIRST_ALARM_DISMISSED_DATE] = today()
pref[Keys.FIRST_ALARM_DISMISSED_DATE_EPOCH] = todayEpoch()
}
}

suspend fun markUpdateNoticeDontShow(version: String) {
dataStore.edit { it[Keys.UPDATE_NOTICE_DONT_SHOW_VERSION] = version }
}

suspend fun markUpdateNoticeShownToday() {
dataStore.edit { pref ->
pref[Keys.UPDATE_NOTICE_LAST_SHOWN_DATE_EPOCH] = todayEpoch()
}
}

suspend fun clearUserData() {
dataStore.edit { pref -> pref.clear() }
dataStore.edit { it.clear() }
}

suspend fun clearFortuneData() {
dataStore.edit { pref ->
pref.remove(Keys.FORTUNE_ID)
pref.remove(Keys.FORTUNE_DATE)
pref.remove(Keys.FORTUNE_DATE_EPOCH)
pref.remove(Keys.FORTUNE_IMAGE_ID)
pref.remove(Keys.FORTUNE_SCORE)
pref.remove(Keys.FORTUNE_SEEN)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import kotlinx.coroutines.flow.Flow

interface FortuneLocalDataSource {
val fortuneIdFlow: Flow<Long?>
val fortuneDateFlow: Flow<String?>
val fortuneDateEpochFlow: Flow<Long?>
val fortuneImageIdFlow: Flow<Int?>
val fortuneScoreFlow: Flow<Int?>
val hasUnseenFortuneFlow: Flow<Boolean>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,14 @@ import com.yapp.domain.model.FortuneCreateStatus
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import java.time.LocalDate
import java.time.format.DateTimeFormatter
import javax.inject.Inject

class FortuneLocalDataSourceImpl @Inject constructor(
private val userPreferences: UserPreferences,
) : FortuneLocalDataSource {

override val fortuneIdFlow = userPreferences.fortuneIdFlow
override val fortuneDateFlow = userPreferences.fortuneDateFlow
override val fortuneDateEpochFlow = userPreferences.fortuneDateEpochFlow
override val fortuneImageIdFlow = userPreferences.fortuneImageIdFlow
override val fortuneScoreFlow = userPreferences.fortuneScoreFlow
override val hasUnseenFortuneFlow = userPreferences.hasUnseenFortuneFlow
Expand All @@ -22,19 +21,19 @@ class FortuneLocalDataSourceImpl @Inject constructor(

override val fortuneCreateStatusFlow = combine(
userPreferences.fortuneIdFlow,
userPreferences.fortuneDateFlow,
userPreferences.fortuneDateEpochFlow,
userPreferences.isFortuneCreatingFlow,
userPreferences.isFortuneFailedFlow,
) { fortuneId, fortuneDate, isCreating, isFailed ->
when {
isFailed -> FortuneCreateStatus.Failure
isCreating -> FortuneCreateStatus.Creating
fortuneId != null && fortuneDate == today() -> FortuneCreateStatus.Success(fortuneId)
fortuneId != null && fortuneDate == todayEpoch() -> FortuneCreateStatus.Success(fortuneId)
else -> FortuneCreateStatus.Idle
}
}.distinctUntilChanged()

private fun today(): String = LocalDate.now().format(DateTimeFormatter.ISO_DATE)
private fun todayEpoch(): Long = LocalDate.now().toEpochDay()

override suspend fun markFortuneCreating() {
userPreferences.markFortuneCreating()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,13 @@ interface UserLocalDataSource {
val userIdFlow: Flow<Long?>
val userNameFlow: Flow<String?>
val onboardingCompletedFlow: Flow<Boolean>
val updateNoticeDontShowVersionFlow: Flow<String?>
val updateNoticeLastShownDateEpochFlow: Flow<Long?>

suspend fun saveUserId(userId: Long)
suspend fun saveUserName(userName: String)
suspend fun setOnboardingCompleted()
suspend fun markUpdateNoticeDontShow(version: String)
suspend fun markUpdateNoticeShownToday()
suspend fun clearUserData()
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ class UserLocalDataSourceImpl @Inject constructor(
override val userIdFlow: Flow<Long?> = userPreferences.userIdFlow
override val userNameFlow: Flow<String?> = userPreferences.userNameFlow
override val onboardingCompletedFlow: Flow<Boolean> = userPreferences.onboardingCompletedFlow
override val updateNoticeDontShowVersionFlow: Flow<String?> = userPreferences.updateNoticeDontShowVersionFlow
override val updateNoticeLastShownDateEpochFlow: Flow<Long?> = userPreferences.updateNoticeLastShownDateEpochFlow

override suspend fun saveUserId(userId: Long) {
userPreferences.saveUserId(userId)
Expand All @@ -24,6 +26,14 @@ class UserLocalDataSourceImpl @Inject constructor(
userPreferences.setOnboardingCompleted()
}

override suspend fun markUpdateNoticeDontShow(version: String) {
userPreferences.markUpdateNoticeDontShow(version)
}

override suspend fun markUpdateNoticeShownToday() {
userPreferences.markUpdateNoticeShownToday()
}

override suspend fun clearUserData() {
userPreferences.clearUserData()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ class FortuneRepositoryImpl @Inject constructor(
) : FortuneRepository {

override val fortuneIdFlow: Flow<Long?> = fortuneLocalDataSource.fortuneIdFlow
override val fortuneDateFlow: Flow<String?> = fortuneLocalDataSource.fortuneDateFlow
override val fortuneDateEpochFlow: Flow<Long?> = fortuneLocalDataSource.fortuneDateEpochFlow
override val fortuneImageIdFlow: Flow<Int?> = fortuneLocalDataSource.fortuneImageIdFlow
override val fortuneScoreFlow: Flow<Int?> = fortuneLocalDataSource.fortuneScoreFlow
override val hasUnseenFortuneFlow: Flow<Boolean> = fortuneLocalDataSource.hasUnseenFortuneFlow
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,14 @@ class UserInfoRepositoryImpl @Inject constructor(
override val userIdFlow: Flow<Long?> = userLocalDataSource.userIdFlow
override val userNameFlow: Flow<String?> = userLocalDataSource.userNameFlow
override val onboardingCompletedFlow: Flow<Boolean> = userLocalDataSource.onboardingCompletedFlow
override val updateNoticeDontShowVersionFlow: Flow<String?> = userLocalDataSource.updateNoticeDontShowVersionFlow
override val updateNoticeLastShownDateEpochFlow: Flow<Long?> = userLocalDataSource.updateNoticeLastShownDateEpochFlow

override suspend fun saveUserId(userId: Long) = userLocalDataSource.saveUserId(userId)
override suspend fun saveUserName(userName: String) = userLocalDataSource.saveUserName(userName)
override suspend fun setOnboardingCompleted() = userLocalDataSource.setOnboardingCompleted()
override suspend fun markUpdateNoticeDontShow(version: String) = userLocalDataSource.markUpdateNoticeDontShow(version)
override suspend fun markUpdateNoticeShownToday() = userLocalDataSource.markUpdateNoticeShownToday()
override suspend fun clearUserData() = userLocalDataSource.clearUserData()

override suspend fun getUserInfo(userId: Long): Result<User> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import kotlinx.coroutines.flow.Flow

interface FortuneRepository {
val fortuneIdFlow: Flow<Long?>
val fortuneDateFlow: Flow<String?>
val fortuneDateEpochFlow: Flow<Long?>
val fortuneImageIdFlow: Flow<Int?>
val fortuneScoreFlow: Flow<Int?>
val hasUnseenFortuneFlow: Flow<Boolean>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,14 @@ interface UserInfoRepository {
val userIdFlow: Flow<Long?>
val userNameFlow: Flow<String?>
val onboardingCompletedFlow: Flow<Boolean>
val updateNoticeDontShowVersionFlow: Flow<String?>
val updateNoticeLastShownDateEpochFlow: Flow<Long?>

suspend fun saveUserId(userId: Long)
suspend fun saveUserName(userName: String)
suspend fun setOnboardingCompleted()
suspend fun markUpdateNoticeDontShow(version: String)
suspend fun markUpdateNoticeShownToday()
suspend fun clearUserData()

suspend fun getUserInfo(userId: Long): Result<User>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import org.orbitmvi.orbit.viewmodel.container
import java.time.LocalDate
import java.time.LocalDateTime
import java.time.ZoneId
import java.time.format.DateTimeFormatter
import javax.inject.Inject
import kotlin.math.max

Expand Down Expand Up @@ -45,8 +44,8 @@ class AlarmSnoozeTimerViewModel @Inject constructor(
}

private fun fetchIsFirstMission() = intent {
val fortuneDate = fortuneRepository.fortuneDateFlow.firstOrNull()
val todayDate = LocalDate.now().format(DateTimeFormatter.ISO_DATE)
val fortuneDate = fortuneRepository.fortuneDateEpochFlow.firstOrNull()
val todayDate = LocalDate.now().toEpochDay()
val isFirstMission = fortuneDate != todayDate

reduce {
Expand Down
1 change: 1 addition & 0 deletions feature/home/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,5 @@ dependencies {
implementation(libs.orbit.viewmodel)
implementation(libs.androidx.material.android)
implementation(libs.androidx.annotation)
implementation(libs.coil.compose)
}
2 changes: 1 addition & 1 deletion feature/home/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">

<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
</manifest>
3 changes: 3 additions & 0 deletions feature/home/src/main/java/com/yapp/home/HomeContract.kt
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ sealed class HomeContract {
val isDeleteDialogVisible: Boolean = false,
val isNoActivatedAlarmDialogVisible: Boolean = false,
val isNoDailyFortuneDialogVisible: Boolean = false,
val isUpdateNoticeVisible: Boolean = false,
val hasNewFortune: Boolean = false,
val isToolTipVisible: Boolean = false,
val pendingAlarmToggle: Pair<Long, Boolean>? = null,
Expand Down Expand Up @@ -58,6 +59,8 @@ sealed class HomeContract {
data object ShowNoDailyFortuneDialog : Action()
data object HideNoDailyFortuneDialog : Action()
data object HideToolTip : Action()
data object OnClickDontShowAgain : Action()
data object HideUpdateNotice : Action()
data object RollbackPendingAlarmToggle : Action()
data object ConfirmDeletion : Action()
data class DeleteSingleAlarm(val alarmId: Long) : Action()
Expand Down
Loading