diff --git a/app/build.gradle b/app/build.gradle index 6dfab455..5fd94e45 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -5,7 +5,7 @@ plugins { id 'dagger.hilt.android.plugin' id 'kotlin-parcelize' id "org.jlleitschuh.gradle.ktlint" version "10.3.0" - id 'org.jetbrains.kotlin.plugin.serialization' version '1.7.20' + id 'org.jetbrains.kotlin.plugin.serialization' version '1.9.0' id 'com.google.gms.google-services' } @@ -59,10 +59,6 @@ android { dependencies { - // glide - implementation 'com.github.bumptech.glide:glide:4.13.2' - kapt 'com.github.bumptech.glide:compiler:4.13.2' - // hilt implementation 'com.google.dagger:hilt-android:2.48.1' kapt 'com.google.dagger:hilt-compiler:2.48.1' @@ -84,8 +80,7 @@ dependencies { implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3' // network - implementation 'com.squareup.okhttp3:logging-interceptor:4.11.0' - implementation(platform('com.squareup.okhttp3:okhttp-bom:4.11.0')) + implementation 'com.squareup.okhttp3:logging-interceptor:4.12.0' implementation 'com.squareup.retrofit2:retrofit:2.9.0' implementation 'com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter:1.0.0' diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index a521b84d..e19982d0 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -4,13 +4,15 @@ + android:required="true" /> - - - + android:screenOrientation="portrait" /> = kotlin.runCatching { authDataSource.postLogin(socialPlatform) }.map { response -> @@ -28,15 +27,10 @@ class AuthRepositoryImpl @Inject constructor( localTokenDataSource.refreshToken = refreshToken } - override fun setSplashState(splashState: SplashState) { - localSplashDataSource.splashState = splashState.toString() - } - - override fun getSplashState(): SplashState { - return SplashState.valueOf(localSplashDataSource.splashState) - } + override fun clearLocalPref() = localPrefDataSource.clearLocalPref() - override fun clearLocalPref() { - localPrefDataSource.clearLocalPref() + override fun getSignedUp(): Boolean = localSignedUpDataSource.isSignedUp + override fun setSignedUp() { + localSignedUpDataSource.isSignedUp = true } } diff --git a/app/src/main/java/com/sopt/peekabookaos/data/repository/ForceUpdateRepositoryImpl.kt b/app/src/main/java/com/sopt/peekabookaos/data/repository/ForceUpdateRepositoryImpl.kt deleted file mode 100644 index cb5989cd..00000000 --- a/app/src/main/java/com/sopt/peekabookaos/data/repository/ForceUpdateRepositoryImpl.kt +++ /dev/null @@ -1,15 +0,0 @@ -package com.sopt.peekabookaos.data.repository - -import com.sopt.peekabookaos.data.source.remote.ForceUpdateDataSource -import com.sopt.peekabookaos.domain.entity.Version -import com.sopt.peekabookaos.domain.repository.ForceUpdateRepository -import javax.inject.Inject - -class ForceUpdateRepositoryImpl @Inject constructor( - private val forceUpdateDataSource: ForceUpdateDataSource -) : ForceUpdateRepository { - override suspend fun getVersion(): Result = - kotlin.runCatching { forceUpdateDataSource.getVersion() }.map { response -> - requireNotNull(response.data).toVersion() - } -} diff --git a/app/src/main/java/com/sopt/peekabookaos/data/repository/ForcedUpdateRepositoryImpl.kt b/app/src/main/java/com/sopt/peekabookaos/data/repository/ForcedUpdateRepositoryImpl.kt new file mode 100644 index 00000000..f55f7e76 --- /dev/null +++ b/app/src/main/java/com/sopt/peekabookaos/data/repository/ForcedUpdateRepositoryImpl.kt @@ -0,0 +1,25 @@ +package com.sopt.peekabookaos.data.repository + +import com.sopt.peekabookaos.data.source.remote.ForcedUpdateDataSource +import com.sopt.peekabookaos.domain.entity.ForcedUpdate +import com.sopt.peekabookaos.domain.entity.UpdateInformation +import com.sopt.peekabookaos.domain.entity.Version +import com.sopt.peekabookaos.domain.repository.ForcedUpdateRepository +import javax.inject.Inject + +class ForcedUpdateRepositoryImpl @Inject constructor( + private val forcedUpdateDataSource: ForcedUpdateDataSource +) : ForcedUpdateRepository { + override suspend fun hasForceUpdateVersion( + currentVersion: String + ): Result = kotlin.runCatching { + forcedUpdateDataSource.getForcedUpdateVersion() + }.map { response -> + val (imageUrl, androidForceVersion, text) = requireNotNull(response.data) + if (Version.of(androidForceVersion).greaterThan(Version.of(currentVersion))) { + return@map ForcedUpdate.Need(UpdateInformation(imageUrl, text)) + } else { + return@map ForcedUpdate.None + } + } +} diff --git a/app/src/main/java/com/sopt/peekabookaos/data/service/ForceUpdateService.kt b/app/src/main/java/com/sopt/peekabookaos/data/service/ForceUpdateService.kt deleted file mode 100644 index 13b32c72..00000000 --- a/app/src/main/java/com/sopt/peekabookaos/data/service/ForceUpdateService.kt +++ /dev/null @@ -1,10 +0,0 @@ -package com.sopt.peekabookaos.data.service - -import com.sopt.peekabookaos.data.entity.BaseResponse -import com.sopt.peekabookaos.data.entity.response.VersionResponse -import retrofit2.http.GET - -interface ForceUpdateService { - @GET("user/v1/version") - suspend fun getVersion(): BaseResponse -} diff --git a/app/src/main/java/com/sopt/peekabookaos/data/service/ForcedUpdateService.kt b/app/src/main/java/com/sopt/peekabookaos/data/service/ForcedUpdateService.kt new file mode 100644 index 00000000..96850d71 --- /dev/null +++ b/app/src/main/java/com/sopt/peekabookaos/data/service/ForcedUpdateService.kt @@ -0,0 +1,10 @@ +package com.sopt.peekabookaos.data.service + +import com.sopt.peekabookaos.data.entity.BaseResponse +import com.sopt.peekabookaos.data.entity.response.ForcedUpdateResponse +import retrofit2.http.GET + +interface ForcedUpdateService { + @GET("user/v1/version") + suspend fun getVersion(): BaseResponse +} diff --git a/app/src/main/java/com/sopt/peekabookaos/data/source/local/LocalSignedUpDataSource.kt b/app/src/main/java/com/sopt/peekabookaos/data/source/local/LocalSignedUpDataSource.kt new file mode 100644 index 00000000..a11da331 --- /dev/null +++ b/app/src/main/java/com/sopt/peekabookaos/data/source/local/LocalSignedUpDataSource.kt @@ -0,0 +1,17 @@ +package com.sopt.peekabookaos.data.source.local + +import android.content.SharedPreferences +import androidx.core.content.edit +import javax.inject.Inject + +class LocalSignedUpDataSource @Inject constructor( + private val prefs: SharedPreferences +) { + var isSignedUp: Boolean + set(value) = prefs.edit { putBoolean(SIGNED_UP, value) } + get() = prefs.getBoolean(SIGNED_UP, false) + + companion object { + private const val SIGNED_UP = "signed up" + } +} diff --git a/app/src/main/java/com/sopt/peekabookaos/data/source/local/LocalSplashDataSource.kt b/app/src/main/java/com/sopt/peekabookaos/data/source/local/LocalSplashDataSource.kt deleted file mode 100644 index c5511091..00000000 --- a/app/src/main/java/com/sopt/peekabookaos/data/source/local/LocalSplashDataSource.kt +++ /dev/null @@ -1,18 +0,0 @@ -package com.sopt.peekabookaos.data.source.local - -import android.content.SharedPreferences -import androidx.core.content.edit -import javax.inject.Inject - -class LocalSplashDataSource @Inject constructor( - private val prefs: SharedPreferences -) { - var splashState: String - set(value) = prefs.edit { putString(SPLASH_STATE, value) } - get() = prefs.getString(SPLASH_STATE, DEFAULT_STATE) ?: DEFAULT_STATE - - companion object { - private const val SPLASH_STATE = "splash_state" - private const val DEFAULT_STATE = "ONBOARDING" - } -} diff --git a/app/src/main/java/com/sopt/peekabookaos/data/source/remote/ForceUpdateDataSource.kt b/app/src/main/java/com/sopt/peekabookaos/data/source/remote/ForceUpdateDataSource.kt deleted file mode 100644 index c21ee921..00000000 --- a/app/src/main/java/com/sopt/peekabookaos/data/source/remote/ForceUpdateDataSource.kt +++ /dev/null @@ -1,13 +0,0 @@ -package com.sopt.peekabookaos.data.source.remote - -import com.sopt.peekabookaos.data.entity.BaseResponse -import com.sopt.peekabookaos.data.entity.response.VersionResponse -import com.sopt.peekabookaos.data.service.ForceUpdateService -import javax.inject.Inject - -class ForceUpdateDataSource @Inject constructor( - private val forceUpdateService: ForceUpdateService -) { - suspend fun getVersion(): BaseResponse = - forceUpdateService.getVersion() -} diff --git a/app/src/main/java/com/sopt/peekabookaos/data/source/remote/ForcedUpdateDataSource.kt b/app/src/main/java/com/sopt/peekabookaos/data/source/remote/ForcedUpdateDataSource.kt new file mode 100644 index 00000000..37c49e76 --- /dev/null +++ b/app/src/main/java/com/sopt/peekabookaos/data/source/remote/ForcedUpdateDataSource.kt @@ -0,0 +1,13 @@ +package com.sopt.peekabookaos.data.source.remote + +import com.sopt.peekabookaos.data.entity.BaseResponse +import com.sopt.peekabookaos.data.entity.response.ForcedUpdateResponse +import com.sopt.peekabookaos.data.service.ForcedUpdateService +import javax.inject.Inject + +class ForcedUpdateDataSource @Inject constructor( + private val forcedUpdateService: ForcedUpdateService +) { + suspend fun getForcedUpdateVersion(): BaseResponse = + forcedUpdateService.getVersion() +} diff --git a/app/src/main/java/com/sopt/peekabookaos/di/RepositoryModule.kt b/app/src/main/java/com/sopt/peekabookaos/di/RepositoryModule.kt index 0b8c200f..676e504b 100644 --- a/app/src/main/java/com/sopt/peekabookaos/di/RepositoryModule.kt +++ b/app/src/main/java/com/sopt/peekabookaos/di/RepositoryModule.kt @@ -4,7 +4,7 @@ import com.sopt.peekabookaos.data.repository.AuthRepositoryImpl import com.sopt.peekabookaos.data.repository.BlockRepositoryImpl import com.sopt.peekabookaos.data.repository.BookRepositoryImpl import com.sopt.peekabookaos.data.repository.DetailRepositoryImpl -import com.sopt.peekabookaos.data.repository.ForceUpdateRepositoryImpl +import com.sopt.peekabookaos.data.repository.ForcedUpdateRepositoryImpl import com.sopt.peekabookaos.data.repository.MyPageRepositoryImpl import com.sopt.peekabookaos.data.repository.NaverRepositoryImpl import com.sopt.peekabookaos.data.repository.NotificationRepositoryImpl @@ -18,7 +18,7 @@ import com.sopt.peekabookaos.domain.repository.AuthRepository import com.sopt.peekabookaos.domain.repository.BlockRepository import com.sopt.peekabookaos.domain.repository.BookRepository import com.sopt.peekabookaos.domain.repository.DetailRepository -import com.sopt.peekabookaos.domain.repository.ForceUpdateRepository +import com.sopt.peekabookaos.domain.repository.ForcedUpdateRepository import com.sopt.peekabookaos.domain.repository.MyPageRepository import com.sopt.peekabookaos.domain.repository.NaverRepository import com.sopt.peekabookaos.domain.repository.NotificationRepository @@ -118,6 +118,6 @@ abstract class RepositoryModule { @Binds @Singleton abstract fun bindToForceUpdateRepository( - forceUpdateRepositoryImpl: ForceUpdateRepositoryImpl - ): ForceUpdateRepository + forceUpdateRepositoryImpl: ForcedUpdateRepositoryImpl + ): ForcedUpdateRepository } diff --git a/app/src/main/java/com/sopt/peekabookaos/di/RetrofitServiceModule.kt b/app/src/main/java/com/sopt/peekabookaos/di/RetrofitServiceModule.kt index 22f4b3fc..9814775b 100644 --- a/app/src/main/java/com/sopt/peekabookaos/di/RetrofitServiceModule.kt +++ b/app/src/main/java/com/sopt/peekabookaos/di/RetrofitServiceModule.kt @@ -4,7 +4,7 @@ import com.sopt.peekabookaos.data.service.AuthService import com.sopt.peekabookaos.data.service.BlockService import com.sopt.peekabookaos.data.service.BookService import com.sopt.peekabookaos.data.service.DetailService -import com.sopt.peekabookaos.data.service.ForceUpdateService +import com.sopt.peekabookaos.data.service.ForcedUpdateService import com.sopt.peekabookaos.data.service.MyPageService import com.sopt.peekabookaos.data.service.NaverService import com.sopt.peekabookaos.data.service.NotificationService @@ -22,7 +22,6 @@ import dagger.Provides import dagger.hilt.InstallIn import dagger.hilt.components.SingletonComponent import retrofit2.Retrofit -import retrofit2.create @Module @InstallIn(SingletonComponent::class) @@ -80,6 +79,6 @@ object RetrofitServiceModule { retrofit.create(ReportService::class.java) @Provides - fun providesForceUpdateService(@PeekaType retrofit: Retrofit): ForceUpdateService = - retrofit.create(ForceUpdateService::class.java) + fun providesForceUpdateService(@PeekaType retrofit: Retrofit): ForcedUpdateService = + retrofit.create(ForcedUpdateService::class.java) } diff --git a/app/src/main/java/com/sopt/peekabookaos/domain/entity/ForcedUpdate.kt b/app/src/main/java/com/sopt/peekabookaos/domain/entity/ForcedUpdate.kt new file mode 100644 index 00000000..797d8052 --- /dev/null +++ b/app/src/main/java/com/sopt/peekabookaos/domain/entity/ForcedUpdate.kt @@ -0,0 +1,6 @@ +package com.sopt.peekabookaos.domain.entity + +sealed class ForcedUpdate { + data object None : ForcedUpdate() + data class Need(val updateInformation: UpdateInformation) : ForcedUpdate() +} diff --git a/app/src/main/java/com/sopt/peekabookaos/domain/entity/SplashState.kt b/app/src/main/java/com/sopt/peekabookaos/domain/entity/SplashState.kt deleted file mode 100644 index 790865d4..00000000 --- a/app/src/main/java/com/sopt/peekabookaos/domain/entity/SplashState.kt +++ /dev/null @@ -1,5 +0,0 @@ -package com.sopt.peekabookaos.domain.entity - -enum class SplashState { - ONBOARDING, MAIN -} diff --git a/app/src/main/java/com/sopt/peekabookaos/domain/entity/SplashUiState.kt b/app/src/main/java/com/sopt/peekabookaos/domain/entity/SplashUiState.kt new file mode 100644 index 00000000..91ad4105 --- /dev/null +++ b/app/src/main/java/com/sopt/peekabookaos/domain/entity/SplashUiState.kt @@ -0,0 +1,10 @@ +package com.sopt.peekabookaos.domain.entity + +sealed class SplashUiState { + data object Idle : SplashUiState() + data object Error : SplashUiState() + data object ShowSplash : SplashUiState() + data object CanStartOnboarding : SplashUiState() + data object CanStartMain : SplashUiState() + data class ForceUpdate(val data: UpdateInformation) : SplashUiState() +} diff --git a/app/src/main/java/com/sopt/peekabookaos/domain/entity/UpdateInformation.kt b/app/src/main/java/com/sopt/peekabookaos/domain/entity/UpdateInformation.kt new file mode 100644 index 00000000..1f9f2bcc --- /dev/null +++ b/app/src/main/java/com/sopt/peekabookaos/domain/entity/UpdateInformation.kt @@ -0,0 +1,10 @@ +package com.sopt.peekabookaos.domain.entity + +import android.os.Parcelable +import kotlinx.parcelize.Parcelize + +@Parcelize +data class UpdateInformation( + val imageUrl: String = "", + val text: String = "" +) : Parcelable diff --git a/app/src/main/java/com/sopt/peekabookaos/domain/entity/Version.kt b/app/src/main/java/com/sopt/peekabookaos/domain/entity/Version.kt index c998453d..d0185321 100644 --- a/app/src/main/java/com/sopt/peekabookaos/domain/entity/Version.kt +++ b/app/src/main/java/com/sopt/peekabookaos/domain/entity/Version.kt @@ -1,11 +1,20 @@ package com.sopt.peekabookaos.domain.entity -import android.os.Parcelable -import kotlinx.parcelize.Parcelize +class Version private constructor( + private val major: Int, + private val minor: Int +) { -@Parcelize -data class Version( - val imageUrl: String = "", - val androidForceVersion: String = "", - val versionText: String = "" -) : Parcelable + fun greaterThan(other: Version): Boolean = other.major < major || other.minor < minor + + companion object { + private const val VERSION_SPLIT_SIGN = "." + private const val MAJOR_POSITION = 0 + private const val MINOR_POSITION = 1 + fun of(version: String) = version.split(VERSION_SPLIT_SIGN).map { + it.toInt() + }.let { + Version(it[MAJOR_POSITION], it[MINOR_POSITION]) + } + } +} diff --git a/app/src/main/java/com/sopt/peekabookaos/domain/entity/VersionDetail.kt b/app/src/main/java/com/sopt/peekabookaos/domain/entity/VersionDetail.kt deleted file mode 100644 index b904357a..00000000 --- a/app/src/main/java/com/sopt/peekabookaos/domain/entity/VersionDetail.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.sopt.peekabookaos.domain.entity - -data class VersionDetail( - val major: String, - val minor: String -) diff --git a/app/src/main/java/com/sopt/peekabookaos/domain/entity/VersionState.kt b/app/src/main/java/com/sopt/peekabookaos/domain/entity/VersionState.kt deleted file mode 100644 index 8bb1ca7b..00000000 --- a/app/src/main/java/com/sopt/peekabookaos/domain/entity/VersionState.kt +++ /dev/null @@ -1,5 +0,0 @@ -package com.sopt.peekabookaos.domain.entity - -enum class VersionState { - LATEST, OUTDATED -} diff --git a/app/src/main/java/com/sopt/peekabookaos/domain/repository/AuthRepository.kt b/app/src/main/java/com/sopt/peekabookaos/domain/repository/AuthRepository.kt index 2a60edcf..0a7d8c9e 100644 --- a/app/src/main/java/com/sopt/peekabookaos/domain/repository/AuthRepository.kt +++ b/app/src/main/java/com/sopt/peekabookaos/domain/repository/AuthRepository.kt @@ -1,6 +1,5 @@ package com.sopt.peekabookaos.domain.repository -import com.sopt.peekabookaos.domain.entity.SplashState import com.sopt.peekabookaos.domain.entity.Token interface AuthRepository { @@ -10,9 +9,9 @@ interface AuthRepository { fun initToken(accessToken: String, refreshToken: String) - fun setSplashState(splashState: SplashState) + fun clearLocalPref() - fun getSplashState(): SplashState + fun getSignedUp(): Boolean - fun clearLocalPref() + fun setSignedUp() } diff --git a/app/src/main/java/com/sopt/peekabookaos/domain/repository/ForceUpdateRepository.kt b/app/src/main/java/com/sopt/peekabookaos/domain/repository/ForceUpdateRepository.kt deleted file mode 100644 index 6050968a..00000000 --- a/app/src/main/java/com/sopt/peekabookaos/domain/repository/ForceUpdateRepository.kt +++ /dev/null @@ -1,7 +0,0 @@ -package com.sopt.peekabookaos.domain.repository - -import com.sopt.peekabookaos.domain.entity.Version - -interface ForceUpdateRepository { - suspend fun getVersion(): Result -} diff --git a/app/src/main/java/com/sopt/peekabookaos/domain/repository/ForcedUpdateRepository.kt b/app/src/main/java/com/sopt/peekabookaos/domain/repository/ForcedUpdateRepository.kt new file mode 100644 index 00000000..90d569ac --- /dev/null +++ b/app/src/main/java/com/sopt/peekabookaos/domain/repository/ForcedUpdateRepository.kt @@ -0,0 +1,9 @@ +package com.sopt.peekabookaos.domain.repository + +import com.sopt.peekabookaos.domain.entity.ForcedUpdate + +interface ForcedUpdateRepository { + suspend fun hasForceUpdateVersion( + currentVersion: String + ): Result +} diff --git a/app/src/main/java/com/sopt/peekabookaos/domain/usecase/GetSplashStateUseCase.kt b/app/src/main/java/com/sopt/peekabookaos/domain/usecase/GetSignedUpUseCase.kt similarity index 63% rename from app/src/main/java/com/sopt/peekabookaos/domain/usecase/GetSplashStateUseCase.kt rename to app/src/main/java/com/sopt/peekabookaos/domain/usecase/GetSignedUpUseCase.kt index bc51c746..a00b306d 100644 --- a/app/src/main/java/com/sopt/peekabookaos/domain/usecase/GetSplashStateUseCase.kt +++ b/app/src/main/java/com/sopt/peekabookaos/domain/usecase/GetSignedUpUseCase.kt @@ -3,8 +3,8 @@ package com.sopt.peekabookaos.domain.usecase import com.sopt.peekabookaos.domain.repository.AuthRepository import javax.inject.Inject -class GetSplashStateUseCase @Inject constructor( +class GetSignedUpUseCase @Inject constructor( private val authRepository: AuthRepository ) { - operator fun invoke() = authRepository.getSplashState() + operator fun invoke() = authRepository.getSignedUp() } diff --git a/app/src/main/java/com/sopt/peekabookaos/domain/usecase/GetVersionUseCase.kt b/app/src/main/java/com/sopt/peekabookaos/domain/usecase/GetVersionUseCase.kt deleted file mode 100644 index e4884600..00000000 --- a/app/src/main/java/com/sopt/peekabookaos/domain/usecase/GetVersionUseCase.kt +++ /dev/null @@ -1,10 +0,0 @@ -package com.sopt.peekabookaos.domain.usecase - -import com.sopt.peekabookaos.domain.repository.ForceUpdateRepository -import javax.inject.Inject - -class GetVersionUseCase @Inject constructor( - private val forceUpdateRepository: ForceUpdateRepository -) { - suspend operator fun invoke() = forceUpdateRepository.getVersion() -} diff --git a/app/src/main/java/com/sopt/peekabookaos/domain/usecase/HasUpdateVersionCheckUseCase.kt b/app/src/main/java/com/sopt/peekabookaos/domain/usecase/HasUpdateVersionCheckUseCase.kt new file mode 100644 index 00000000..ca3c7b3e --- /dev/null +++ b/app/src/main/java/com/sopt/peekabookaos/domain/usecase/HasUpdateVersionCheckUseCase.kt @@ -0,0 +1,12 @@ +package com.sopt.peekabookaos.domain.usecase + +import com.sopt.peekabookaos.domain.repository.ForcedUpdateRepository +import javax.inject.Inject + +class HasUpdateVersionCheckUseCase @Inject constructor( + private val forcedUpdateRepository: ForcedUpdateRepository +) { + suspend operator fun invoke( + currentVersion: String + ) = forcedUpdateRepository.hasForceUpdateVersion(currentVersion) +} diff --git a/app/src/main/java/com/sopt/peekabookaos/domain/usecase/SetAccessToMainUseCase.kt b/app/src/main/java/com/sopt/peekabookaos/domain/usecase/SetAccessToMainUseCase.kt new file mode 100644 index 00000000..0301685b --- /dev/null +++ b/app/src/main/java/com/sopt/peekabookaos/domain/usecase/SetAccessToMainUseCase.kt @@ -0,0 +1,10 @@ +package com.sopt.peekabookaos.domain.usecase + +import com.sopt.peekabookaos.domain.repository.AuthRepository +import javax.inject.Inject + +class SetAccessToMainUseCase @Inject constructor( + private val authRepository: AuthRepository +) { + operator fun invoke() = authRepository.setSignedUp() +} diff --git a/app/src/main/java/com/sopt/peekabookaos/domain/usecase/SetSplashStateUseCase.kt b/app/src/main/java/com/sopt/peekabookaos/domain/usecase/SetSplashStateUseCase.kt deleted file mode 100644 index 91608884..00000000 --- a/app/src/main/java/com/sopt/peekabookaos/domain/usecase/SetSplashStateUseCase.kt +++ /dev/null @@ -1,13 +0,0 @@ -package com.sopt.peekabookaos.domain.usecase - -import com.sopt.peekabookaos.domain.entity.SplashState -import com.sopt.peekabookaos.domain.repository.AuthRepository -import javax.inject.Inject - -class SetSplashStateUseCase @Inject constructor( - private val authRepository: AuthRepository -) { - operator fun invoke(splashState: SplashState) { - authRepository.setSplashState(splashState) - } -} diff --git a/app/src/main/java/com/sopt/peekabookaos/presentation/barcodeScanner/BarcodeAnalyser.kt b/app/src/main/java/com/sopt/peekabookaos/presentation/barcodeScanner/BarcodeAnalyzer.kt similarity index 88% rename from app/src/main/java/com/sopt/peekabookaos/presentation/barcodeScanner/BarcodeAnalyser.kt rename to app/src/main/java/com/sopt/peekabookaos/presentation/barcodeScanner/BarcodeAnalyzer.kt index e19b8545..29a4c6f5 100644 --- a/app/src/main/java/com/sopt/peekabookaos/presentation/barcodeScanner/BarcodeAnalyser.kt +++ b/app/src/main/java/com/sopt/peekabookaos/presentation/barcodeScanner/BarcodeAnalyzer.kt @@ -1,6 +1,7 @@ package com.sopt.peekabookaos.presentation.barcodeScanner -import android.annotation.SuppressLint +import androidx.annotation.OptIn +import androidx.camera.core.ExperimentalGetImage import androidx.camera.core.ImageAnalysis import androidx.camera.core.ImageProxy import com.google.mlkit.vision.barcode.BarcodeScannerOptions @@ -8,7 +9,7 @@ import com.google.mlkit.vision.barcode.BarcodeScanning import com.google.mlkit.vision.barcode.common.Barcode import com.google.mlkit.vision.common.InputImage -class BarcodeAnalyser(private val barcodeListener: BarcodeAnalyzerListener) : +class BarcodeAnalyzer(private val barcodeListener: BarcodeAnalyzerListener) : ImageAnalysis.Analyzer { private val options = BarcodeScannerOptions.Builder().setBarcodeFormats( Barcode.FORMAT_CODE_39, @@ -24,7 +25,7 @@ class BarcodeAnalyser(private val barcodeListener: BarcodeAnalyzerListener) : private val scanner by lazy { BarcodeScanning.getClient(options) } - @SuppressLint("UnsafeOptInUsageError") + @OptIn(ExperimentalGetImage::class) override fun analyze(imageProxy: ImageProxy) { val mediaImage = imageProxy.image if (mediaImage != null) { diff --git a/app/src/main/java/com/sopt/peekabookaos/presentation/barcodeScanner/BarcodeScannerFragment.kt b/app/src/main/java/com/sopt/peekabookaos/presentation/barcodeScanner/BarcodeScannerFragment.kt index c1339de6..4e8fb809 100644 --- a/app/src/main/java/com/sopt/peekabookaos/presentation/barcodeScanner/BarcodeScannerFragment.kt +++ b/app/src/main/java/com/sopt/peekabookaos/presentation/barcodeScanner/BarcodeScannerFragment.kt @@ -3,7 +3,6 @@ package com.sopt.peekabookaos.presentation.barcodeScanner import android.Manifest import android.content.pm.PackageManager import android.os.Bundle -import android.util.DisplayMetrics import android.view.View import androidx.activity.OnBackPressedCallback import androidx.camera.core.AspectRatio @@ -25,6 +24,7 @@ import com.sopt.peekabookaos.presentation.book.BookActivity.Companion.CREATE import com.sopt.peekabookaos.presentation.book.BookActivity.Companion.LOCATION import com.sopt.peekabookaos.util.ToastMessageUtil import com.sopt.peekabookaos.util.binding.BindingFragment +import com.sopt.peekabookaos.util.extensions.getScreenSize import com.sopt.peekabookaos.util.extensions.repeatOnStarted import com.sopt.peekabookaos.util.extensions.setSingleOnClickListener import dagger.hilt.android.AndroidEntryPoint @@ -34,7 +34,7 @@ import kotlin.math.abs import kotlin.math.max import kotlin.math.min -private val REQUIRED_PERMISSIONS = arrayOf(Manifest.permission.CAMERA) +private val requiredPermissions = arrayOf(Manifest.permission.CAMERA) private const val RATIO_4_3_VALUE = 4.0 / 3.0 private const val RATIO_16_9_VALUE = 16.0 / 9.0 typealias BarcodeAnalyzerListener = (barcode: MutableList) -> Unit @@ -77,10 +77,8 @@ class BarcodeScannerFragment : } private fun startCamera() { - @Suppress("DEPRECATION") - val metrics = - DisplayMetrics().also { binding.pvBarcode.display.getRealMetrics(it) } - val screenAspectRatio = aspectRatio(metrics.widthPixels, metrics.heightPixels) + val screenSize = requireContext().getScreenSize() + val screenAspectRatio = aspectRatio(screenSize.first, screenSize.second) val rotation = binding.pvBarcode.display.rotation val cameraSelector = @@ -90,8 +88,7 @@ class BarcodeScannerFragment : { val cameraProvider: ProcessCameraProvider = cameraProviderFuture.get() - val preview = Preview.Builder().setTargetAspectRatio(screenAspectRatio) - .setTargetRotation(rotation).build() + val preview = Preview.Builder().setTargetRotation(rotation).build() preview.setSurfaceProvider(binding.pvBarcode.surfaceProvider) @@ -116,7 +113,7 @@ class BarcodeScannerFragment : ) } - private fun allPermissionsGranted() = REQUIRED_PERMISSIONS.all { + private fun allPermissionsGranted() = requiredPermissions.all { ContextCompat.checkSelfPermission(requireContext(), it) == PackageManager.PERMISSION_GRANTED } @@ -129,12 +126,12 @@ class BarcodeScannerFragment : } private fun initAnalyzer(screenAspectRatio: Int, rotation: Int): UseCase { - return ImageAnalysis.Builder().setTargetAspectRatio(screenAspectRatio) + return ImageAnalysis.Builder().setTargetRotation(screenAspectRatio) .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST) .setTargetRotation(rotation).build().also { it.setAnalyzer( executor, - BarcodeAnalyser { barcode -> + BarcodeAnalyzer { barcode -> if (processingBarcode.compareAndSet(false, false)) { onBarcodeDetected(barcode) } @@ -177,9 +174,11 @@ class BarcodeScannerFragment : } ) } + BarcodeState.ERROR -> { BarcodeErrorDialog().show(childFragmentManager, BarcodeErrorDialog.TAG) } + BarcodeState.IDLE -> { return@collect } diff --git a/app/src/main/java/com/sopt/peekabookaos/presentation/book/BookActivity.kt b/app/src/main/java/com/sopt/peekabookaos/presentation/book/BookActivity.kt index ff92e08d..bca4df5f 100644 --- a/app/src/main/java/com/sopt/peekabookaos/presentation/book/BookActivity.kt +++ b/app/src/main/java/com/sopt/peekabookaos/presentation/book/BookActivity.kt @@ -1,12 +1,12 @@ package com.sopt.peekabookaos.presentation.book -import android.app.Activity import android.os.Bundle import androidx.navigation.fragment.NavHostFragment import com.sopt.peekabookaos.R import com.sopt.peekabookaos.databinding.ActivityBookBinding import com.sopt.peekabookaos.util.binding.BindingActivity -import com.sopt.peekabookaos.util.extensions.activityTransition +import com.sopt.peekabookaos.util.extensions.activityCloseTransition +import com.sopt.peekabookaos.util.extensions.activityOpenTransition import dagger.hilt.android.AndroidEntryPoint @AndroidEntryPoint @@ -24,31 +24,19 @@ class BookActivity : BindingActivity(R.layout.activity_book when (intent.getStringExtra(LOCATION) ?: CREATE) { RECOMMEND -> { navController.navigate(R.id.action_barcodeScannerFragment_to_searchBookFragment) - activityTransition( - Activity.OVERRIDE_TRANSITION_OPEN, - R.animator.anim_from_right, - R.animator.anim_to_left - ) + activityOpenTransition(R.animator.anim_from_right, R.animator.anim_to_left) } else -> { navController.navigate(R.id.barcodeScannerFragment) - activityTransition( - Activity.OVERRIDE_TRANSITION_OPEN, - R.animator.anim_from_bottom, - R.animator.anim_to_top - ) + activityOpenTransition(R.animator.anim_from_bottom, R.animator.anim_to_top) } } } override fun finish() { super.finish() - activityTransition( - Activity.OVERRIDE_TRANSITION_CLOSE, - R.animator.anim_from_left, - R.animator.anim_to_right - ) + activityCloseTransition(R.animator.anim_from_left, R.animator.anim_to_right) } companion object { diff --git a/app/src/main/java/com/sopt/peekabookaos/presentation/bookshelf/BlockDialog.kt b/app/src/main/java/com/sopt/peekabookaos/presentation/bookshelf/BlockDialog.kt index 25c56543..e2cc8b12 100644 --- a/app/src/main/java/com/sopt/peekabookaos/presentation/bookshelf/BlockDialog.kt +++ b/app/src/main/java/com/sopt/peekabookaos/presentation/bookshelf/BlockDialog.kt @@ -10,6 +10,7 @@ import com.sopt.peekabookaos.R import com.sopt.peekabookaos.databinding.DialogBlockBinding import com.sopt.peekabookaos.util.dialog.ConfirmClickListener import com.sopt.peekabookaos.util.dialog.WarningDialogFragment +import com.sopt.peekabookaos.util.extensions.getParcelableCompat import timber.log.Timber class BlockDialog : DialogFragment() { @@ -49,7 +50,7 @@ class BlockDialog : DialogFragment() { private fun initConfirmBtnClickListener() { binding.btnBlockDialogConfirm.setOnClickListener { - arguments?.getParcelable(WarningDialogFragment.CONFIRM_ACTION) + arguments?.getParcelableCompat(WarningDialogFragment.CONFIRM_ACTION) ?.onConfirmClick() ?: Timber.e(getString(R.string.null_point_exception_warning_dialog_argument)) dismiss() diff --git a/app/src/main/java/com/sopt/peekabookaos/presentation/createBook/CreateBookFragment.kt b/app/src/main/java/com/sopt/peekabookaos/presentation/createBook/CreateBookFragment.kt index 1fb6e731..314d970a 100644 --- a/app/src/main/java/com/sopt/peekabookaos/presentation/createBook/CreateBookFragment.kt +++ b/app/src/main/java/com/sopt/peekabookaos/presentation/createBook/CreateBookFragment.kt @@ -50,7 +50,7 @@ class CreateBookFragment : private fun initBookInfo() { createBookViewModel.initBookInfo( - arguments?.getParcelableCompat(BOOK_INFO, Book::class.java) ?: Book() + arguments?.getParcelableCompat(BOOK_INFO) ?: Book() ) } diff --git a/app/src/main/java/com/sopt/peekabookaos/presentation/forceUpdate/ForceUpdateActivity.kt b/app/src/main/java/com/sopt/peekabookaos/presentation/forceUpdate/ForceUpdateActivity.kt deleted file mode 100644 index c9e3eae1..00000000 --- a/app/src/main/java/com/sopt/peekabookaos/presentation/forceUpdate/ForceUpdateActivity.kt +++ /dev/null @@ -1,40 +0,0 @@ -package com.sopt.peekabookaos.presentation.forceUpdate - -import android.content.Intent -import android.net.Uri -import android.os.Bundle -import androidx.activity.viewModels -import com.sopt.peekabookaos.R -import com.sopt.peekabookaos.databinding.ActivityForceUpdateBinding -import com.sopt.peekabookaos.domain.entity.Version -import com.sopt.peekabookaos.presentation.splash.SplashActivity.Companion.LATEST_VERSION -import com.sopt.peekabookaos.util.binding.BindingActivity -import com.sopt.peekabookaos.util.extensions.getParcelable - -class ForceUpdateActivity : - BindingActivity(R.layout.activity_force_update) { - private lateinit var intentToPlayStore: Intent - private val viewModel by viewModels() - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - binding.viewModel = viewModel - getLatestVersion() - initUpdateBtnClickListener() - } - - private fun initUpdateBtnClickListener() { - binding.btnForceUpdate.setOnClickListener { - intentToPlayStore = Intent( - Intent.ACTION_VIEW, - Uri.parse(getString(R.string.force_update_store_link)) - ) - startActivity(intentToPlayStore) - } - } - - private fun getLatestVersion() { - intent.getParcelable(LATEST_VERSION, Version::class.java) - ?.let { viewModel.getLatestVersion(it) } - } -} diff --git a/app/src/main/java/com/sopt/peekabookaos/presentation/forceUpdate/ForceUpdateViewModel.kt b/app/src/main/java/com/sopt/peekabookaos/presentation/forceUpdate/ForceUpdateViewModel.kt deleted file mode 100644 index 992bb827..00000000 --- a/app/src/main/java/com/sopt/peekabookaos/presentation/forceUpdate/ForceUpdateViewModel.kt +++ /dev/null @@ -1,15 +0,0 @@ -package com.sopt.peekabookaos.presentation.forceUpdate - -import androidx.lifecycle.LiveData -import androidx.lifecycle.MutableLiveData -import androidx.lifecycle.ViewModel -import com.sopt.peekabookaos.domain.entity.Version - -class ForceUpdateViewModel : ViewModel() { - private val _latestVersion: MutableLiveData = MutableLiveData() - val latestVersion: LiveData = _latestVersion - - fun getLatestVersion(version: Version) { - _latestVersion.value = version - } -} diff --git a/app/src/main/java/com/sopt/peekabookaos/presentation/forceUpdate/ForcedUpdateActivity.kt b/app/src/main/java/com/sopt/peekabookaos/presentation/forceUpdate/ForcedUpdateActivity.kt new file mode 100644 index 00000000..ad5acfad --- /dev/null +++ b/app/src/main/java/com/sopt/peekabookaos/presentation/forceUpdate/ForcedUpdateActivity.kt @@ -0,0 +1,42 @@ +package com.sopt.peekabookaos.presentation.forceUpdate + +import android.content.Intent +import android.net.Uri +import android.os.Bundle +import androidx.activity.viewModels +import com.sopt.peekabookaos.R +import com.sopt.peekabookaos.databinding.ActivityForcedUpdateBinding +import com.sopt.peekabookaos.domain.entity.UpdateInformation +import com.sopt.peekabookaos.presentation.splash.SplashActivity.Companion.UPDATE_INFORMATION +import com.sopt.peekabookaos.util.binding.BindingActivity +import com.sopt.peekabookaos.util.extensions.getParcelable +import com.sopt.peekabookaos.util.extensions.initBackPressedCallback +import com.sopt.peekabookaos.util.extensions.setSingleOnClickListener + +class ForcedUpdateActivity : + BindingActivity(R.layout.activity_forced_update) { + private val viewModel by viewModels() + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding.viewModel = viewModel + initBackPressedCallback() + getUpdateInformation() + initUpdateBtnClickListener() + } + + private fun initUpdateBtnClickListener() { + binding.btnForceUpdate.setSingleOnClickListener { + val intentToPlayStore = Intent( + Intent.ACTION_VIEW, + Uri.parse(getString(R.string.force_update_store_link)) + ) + startActivity(intentToPlayStore) + } + } + + private fun getUpdateInformation() { + intent.getParcelable(UPDATE_INFORMATION, UpdateInformation::class.java) + ?.let { viewModel.setUpdateInformation(it) } + } +} diff --git a/app/src/main/java/com/sopt/peekabookaos/presentation/forceUpdate/ForcedUpdateViewModel.kt b/app/src/main/java/com/sopt/peekabookaos/presentation/forceUpdate/ForcedUpdateViewModel.kt new file mode 100644 index 00000000..23f17cfc --- /dev/null +++ b/app/src/main/java/com/sopt/peekabookaos/presentation/forceUpdate/ForcedUpdateViewModel.kt @@ -0,0 +1,15 @@ +package com.sopt.peekabookaos.presentation.forceUpdate + +import androidx.lifecycle.ViewModel +import com.sopt.peekabookaos.domain.entity.UpdateInformation +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.update + +class ForcedUpdateViewModel : ViewModel() { + private val _updateInformation = MutableStateFlow(UpdateInformation()) + val updateInformation = _updateInformation.asStateFlow() + + fun setUpdateInformation(updateInformation: UpdateInformation) = + _updateInformation.update { updateInformation } +} diff --git a/app/src/main/java/com/sopt/peekabookaos/presentation/main/MainActivity.kt b/app/src/main/java/com/sopt/peekabookaos/presentation/main/MainActivity.kt index 219418a2..689b8ebc 100644 --- a/app/src/main/java/com/sopt/peekabookaos/presentation/main/MainActivity.kt +++ b/app/src/main/java/com/sopt/peekabookaos/presentation/main/MainActivity.kt @@ -9,21 +9,20 @@ import androidx.navigation.fragment.NavHostFragment import androidx.navigation.ui.setupWithNavController import com.sopt.peekabookaos.R import com.sopt.peekabookaos.databinding.ActivityMainBinding -import com.sopt.peekabookaos.util.ToastMessageUtil import com.sopt.peekabookaos.util.binding.BindingActivity +import com.sopt.peekabookaos.util.extensions.initBackPressedCallback import dagger.hilt.android.AndroidEntryPoint import timber.log.Timber -import kotlin.system.exitProcess @AndroidEntryPoint class MainActivity : BindingActivity(R.layout.activity_main) { private val mainViewModel: MainViewModel by viewModels() - private var onBackPressedTime = 0L + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding.vm = mainViewModel initBottomNavigationView() - initBackPressedCallback() + clickBackPressedButton() } private fun initBottomNavigationView() { @@ -37,30 +36,14 @@ class MainActivity : BindingActivity(R.layout.activity_main return navHostFragment.navController } - private fun initBackPressedCallback() { + private fun clickBackPressedButton() { onBackPressedDispatcher.addCallback { val fragmentId: Int? = findNavController(R.id.fcv_main).currentDestination?.id fragmentId?.also { id -> if (id == R.id.bookshelfFragment) { - val curTime = System.currentTimeMillis() - val gap = curTime - onBackPressedTime - if (gap > WAITING_DEADLINE) { - onBackPressedTime = curTime - ToastMessageUtil.showToast( - this@MainActivity, - getString(R.string.finish_app_toast_msg) - ) - return@addCallback - } - finishAffinity() - System.runFinalization() - exitProcess(0) + initBackPressedCallback() } } ?: Timber.e(getString(R.string.null_point_exception)) } } - - companion object { - private const val WAITING_DEADLINE = 2000L - } } diff --git a/app/src/main/java/com/sopt/peekabookaos/presentation/main/MainViewModel.kt b/app/src/main/java/com/sopt/peekabookaos/presentation/main/MainViewModel.kt index a9cc701b..b5366404 100644 --- a/app/src/main/java/com/sopt/peekabookaos/presentation/main/MainViewModel.kt +++ b/app/src/main/java/com/sopt/peekabookaos/presentation/main/MainViewModel.kt @@ -1,16 +1,15 @@ package com.sopt.peekabookaos.presentation.main import androidx.lifecycle.ViewModel -import com.sopt.peekabookaos.domain.entity.SplashState -import com.sopt.peekabookaos.domain.usecase.SetSplashStateUseCase +import com.sopt.peekabookaos.domain.usecase.SetAccessToMainUseCase import dagger.hilt.android.lifecycle.HiltViewModel import javax.inject.Inject @HiltViewModel class MainViewModel @Inject constructor( - setSplashStateUseCase: SetSplashStateUseCase + setAccessToMainUseCase: SetAccessToMainUseCase ) : ViewModel() { init { - setSplashStateUseCase(SplashState.MAIN) + setAccessToMainUseCase() } } diff --git a/app/src/main/java/com/sopt/peekabookaos/presentation/networkError/NetworkErrorActivity.kt b/app/src/main/java/com/sopt/peekabookaos/presentation/networkError/NetworkErrorActivity.kt index 99f452fc..d159084e 100644 --- a/app/src/main/java/com/sopt/peekabookaos/presentation/networkError/NetworkErrorActivity.kt +++ b/app/src/main/java/com/sopt/peekabookaos/presentation/networkError/NetworkErrorActivity.kt @@ -2,24 +2,19 @@ package com.sopt.peekabookaos.presentation.networkError import android.content.Intent import android.os.Bundle -import androidx.activity.addCallback -import androidx.activity.viewModels import com.sopt.peekabookaos.R import com.sopt.peekabookaos.databinding.ActivityNetworkErrorBinding -import com.sopt.peekabookaos.domain.entity.SplashState -import com.sopt.peekabookaos.presentation.main.MainActivity -import com.sopt.peekabookaos.presentation.onboarding.OnboardingActivity +import com.sopt.peekabookaos.presentation.splash.SplashActivity import com.sopt.peekabookaos.util.ToastMessageUtil import com.sopt.peekabookaos.util.binding.BindingActivity +import com.sopt.peekabookaos.util.extensions.activityOpenTransition +import com.sopt.peekabookaos.util.extensions.initBackPressedCallback import com.sopt.peekabookaos.util.extensions.isNetworkConnected import dagger.hilt.android.AndroidEntryPoint -import kotlin.system.exitProcess @AndroidEntryPoint class NetworkErrorActivity : BindingActivity(R.layout.activity_network_error) { - private val networkErrorViewModel: NetworkErrorViewModel by viewModels() - private var onBackPressedTime = 0L override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -27,33 +22,15 @@ class NetworkErrorActivity : initBackPressedCallback() } - private fun initBackPressedCallback() { - onBackPressedDispatcher.addCallback { - if (System.currentTimeMillis() - onBackPressedTime >= WAITING_DEADLINE) { - onBackPressedTime = System.currentTimeMillis() - ToastMessageUtil.showToast( - this@NetworkErrorActivity, - getString(R.string.finish_app_toast_msg) - ) - } else { - finishAffinity() - System.runFinalization() - exitProcess(0) - } - } - } - private fun checkNetworkConnect() { binding.btnNetworkErrorRetry.setOnClickListener { if (isNetworkConnected()) { - when (networkErrorViewModel.getSplashState()) { - SplashState.ONBOARDING -> startActivity( - Intent(this, OnboardingActivity::class.java) - ) - SplashState.MAIN -> startActivity(Intent(this, MainActivity::class.java)) - } - overridePendingTransition(0, 0) - finish() + startActivity( + Intent(this, SplashActivity::class.java) + .putExtra(LOCATION, NETWORK_ERROR) + ) + activityOpenTransition(0, 0) + finishAffinity() } else { ToastMessageUtil.showToast( this@NetworkErrorActivity, @@ -64,6 +41,7 @@ class NetworkErrorActivity : } companion object { - private const val WAITING_DEADLINE = 2000L + const val LOCATION = "location" + const val NETWORK_ERROR = "network error" } } diff --git a/app/src/main/java/com/sopt/peekabookaos/presentation/networkError/NetworkErrorViewModel.kt b/app/src/main/java/com/sopt/peekabookaos/presentation/networkError/NetworkErrorViewModel.kt deleted file mode 100644 index 94163f7d..00000000 --- a/app/src/main/java/com/sopt/peekabookaos/presentation/networkError/NetworkErrorViewModel.kt +++ /dev/null @@ -1,14 +0,0 @@ -package com.sopt.peekabookaos.presentation.networkError - -import androidx.lifecycle.ViewModel -import com.sopt.peekabookaos.domain.entity.SplashState -import com.sopt.peekabookaos.domain.usecase.GetSplashStateUseCase -import dagger.hilt.android.lifecycle.HiltViewModel -import javax.inject.Inject - -@HiltViewModel -class NetworkErrorViewModel @Inject constructor( - private val getSplashStateUseCase: GetSplashStateUseCase -) : ViewModel() { - fun getSplashState(): SplashState = getSplashStateUseCase() -} diff --git a/app/src/main/java/com/sopt/peekabookaos/presentation/onboarding/OnboardingActivity.kt b/app/src/main/java/com/sopt/peekabookaos/presentation/onboarding/OnboardingActivity.kt index ca134360..8a2625b9 100644 --- a/app/src/main/java/com/sopt/peekabookaos/presentation/onboarding/OnboardingActivity.kt +++ b/app/src/main/java/com/sopt/peekabookaos/presentation/onboarding/OnboardingActivity.kt @@ -2,22 +2,19 @@ package com.sopt.peekabookaos.presentation.onboarding import android.content.Intent import android.os.Bundle -import androidx.activity.addCallback import com.google.android.material.tabs.TabLayoutMediator import com.sopt.peekabookaos.R import com.sopt.peekabookaos.databinding.ActivityOnboardingBinding import com.sopt.peekabookaos.domain.entity.Onboarding import com.sopt.peekabookaos.presentation.login.LoginActivity -import com.sopt.peekabookaos.util.ToastMessageUtil import com.sopt.peekabookaos.util.binding.BindingActivity +import com.sopt.peekabookaos.util.extensions.initBackPressedCallback import com.sopt.peekabookaos.util.extensions.setSingleOnClickListener -import kotlin.system.exitProcess class OnboardingActivity : BindingActivity(R.layout.activity_onboarding) { private val onboardingAdapter: OnboardingAdapter? get() = binding.vpOnboarding.adapter as? OnboardingAdapter - private var onBackPressedTime = 0L override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -45,22 +42,6 @@ class OnboardingActivity : } } - private fun initBackPressedCallback() { - onBackPressedDispatcher.addCallback { - if (System.currentTimeMillis() - onBackPressedTime >= WAITING_DEADLINE) { - onBackPressedTime = System.currentTimeMillis() - ToastMessageUtil.showToast( - this@OnboardingActivity, - getString(R.string.finish_app_toast_msg) - ) - } else { - finishAffinity() - System.runFinalization() - exitProcess(0) - } - } - } - companion object { private val onboardingList = listOf( Onboarding(R.drawable.ic_onboarding_1, R.drawable.ic_onboarding_content_1), @@ -68,6 +49,5 @@ class OnboardingActivity : Onboarding(R.drawable.ic_onboarding_3, R.drawable.ic_onboarding_content_3), Onboarding(R.drawable.ic_onboarding_4, R.drawable.ic_onboarding_content_4) ) - private const val WAITING_DEADLINE = 2000L } } diff --git a/app/src/main/java/com/sopt/peekabookaos/presentation/pickModify/PickModifyViewModel.kt b/app/src/main/java/com/sopt/peekabookaos/presentation/pickModify/PickModifyViewModel.kt index 8958f1b5..04687acc 100644 --- a/app/src/main/java/com/sopt/peekabookaos/presentation/pickModify/PickModifyViewModel.kt +++ b/app/src/main/java/com/sopt/peekabookaos/presentation/pickModify/PickModifyViewModel.kt @@ -30,7 +30,7 @@ class PickModifyViewModel @Inject constructor( private val _overListState: MutableLiveData = MutableLiveData() val overListState: LiveData = _overListState - var preListState = _overListState.value + private var preListState = _overListState.value private val _isGetPickServerStatus = MutableLiveData() val isGetPickServerStatus: LiveData = _isGetPickServerStatus @@ -38,7 +38,7 @@ class PickModifyViewModel @Inject constructor( private val _isPatchPickServerStatus = MutableLiveData() val isPatchPickServerStatus: LiveData = _isPatchPickServerStatus - var selectItemIdList = arrayOfNulls(3) + private var selectItemIdList = arrayOfNulls(3) init { getPick() diff --git a/app/src/main/java/com/sopt/peekabookaos/presentation/recommendation/RecommendationFragment.kt b/app/src/main/java/com/sopt/peekabookaos/presentation/recommendation/RecommendationFragment.kt index 4b2e2564..7677891c 100644 --- a/app/src/main/java/com/sopt/peekabookaos/presentation/recommendation/RecommendationFragment.kt +++ b/app/src/main/java/com/sopt/peekabookaos/presentation/recommendation/RecommendationFragment.kt @@ -51,16 +51,10 @@ class RecommendationFragment : private fun initView() { recommendationViewModel.initRecommendData( requireNotNull( - arguments?.getParcelableCompat( - BookActivity.BOOK_INFO, - Book::class.java - ) + arguments?.getParcelableCompat(BookActivity.BOOK_INFO) ?: Book() ), requireNotNull( - arguments?.getParcelableCompat( - BookActivity.FRIEND_INFO, - User::class.java - ) + arguments?.getParcelableCompat(BookActivity.FRIEND_INFO) ?: User() ) ) } diff --git a/app/src/main/java/com/sopt/peekabookaos/presentation/search/user/SearchUserActivity.kt b/app/src/main/java/com/sopt/peekabookaos/presentation/search/user/SearchUserActivity.kt index a84fbe0d..2381296e 100644 --- a/app/src/main/java/com/sopt/peekabookaos/presentation/search/user/SearchUserActivity.kt +++ b/app/src/main/java/com/sopt/peekabookaos/presentation/search/user/SearchUserActivity.kt @@ -26,7 +26,7 @@ class SearchUserActivity : initEditTextClearFocus() initKeyboardDoneClickListener() initCloseBtnClickListener() - collectSearchState() + collectUiEvent() } private fun initSearchFocus() { @@ -65,7 +65,7 @@ class SearchUserActivity : } } - private fun collectSearchState() { + private fun collectUiEvent() { repeatOnStarted { searchUserViewModel.uiEvent.collect { uiEvent -> when (uiEvent) { diff --git a/app/src/main/java/com/sopt/peekabookaos/presentation/socialLogin/SocialLoginFragment.kt b/app/src/main/java/com/sopt/peekabookaos/presentation/socialLogin/SocialLoginFragment.kt index 27a51162..dc2d72f2 100644 --- a/app/src/main/java/com/sopt/peekabookaos/presentation/socialLogin/SocialLoginFragment.kt +++ b/app/src/main/java/com/sopt/peekabookaos/presentation/socialLogin/SocialLoginFragment.kt @@ -5,29 +5,25 @@ import android.content.Intent.ACTION_VIEW import android.net.Uri import android.os.Bundle import android.view.View -import androidx.activity.OnBackPressedCallback -import androidx.core.app.ActivityCompat.finishAffinity import androidx.fragment.app.viewModels import androidx.navigation.fragment.findNavController import com.sopt.peekabookaos.R import com.sopt.peekabookaos.data.service.KakaoLoginService import com.sopt.peekabookaos.databinding.FragmentSocialLoginBinding import com.sopt.peekabookaos.presentation.main.MainActivity -import com.sopt.peekabookaos.util.ToastMessageUtil import com.sopt.peekabookaos.util.binding.BindingFragment +import com.sopt.peekabookaos.util.extensions.initBackPressedCallback import com.sopt.peekabookaos.util.extensions.repeatOnStarted import com.sopt.peekabookaos.util.extensions.setSingleOnClickListener import dagger.hilt.android.AndroidEntryPoint import timber.log.Timber import javax.inject.Inject -import kotlin.system.exitProcess @AndroidEntryPoint class SocialLoginFragment : BindingFragment(R.layout.fragment_social_login) { @Inject lateinit var kakaoLoginService: KakaoLoginService - private var onBackPressedTime = 0L private val socialLoginViewModel: SocialLoginViewModel by viewModels() override fun onViewCreated(view: View, savedInstanceState: Bundle?) { @@ -40,29 +36,6 @@ class SocialLoginFragment : collectIsSignedUp() } - private fun initBackPressedCallback() { - requireActivity().onBackPressedDispatcher.addCallback( - viewLifecycleOwner, - object : OnBackPressedCallback(true) { - override fun handleOnBackPressed() { - val curTime = System.currentTimeMillis() - val gap = curTime - onBackPressedTime - if (gap > WAITING_DEADLINE) { - onBackPressedTime = curTime - ToastMessageUtil.showToast( - requireContext(), - getString(R.string.finish_app_toast_msg) - ) - return - } - finishAffinity(requireActivity()) - System.runFinalization() - exitProcess(0) - } - } - ) - } - private fun initKakaoLoginBtnClickListener() { binding.btnSocialLoginKakao.setSingleOnClickListener { startKakaoLogin() @@ -119,8 +92,4 @@ class SocialLoginFragment : } } } - - companion object { - private const val WAITING_DEADLINE = 2000L - } } diff --git a/app/src/main/java/com/sopt/peekabookaos/presentation/splash/SplashActivity.kt b/app/src/main/java/com/sopt/peekabookaos/presentation/splash/SplashActivity.kt index e28ccd88..3d3b7706 100644 --- a/app/src/main/java/com/sopt/peekabookaos/presentation/splash/SplashActivity.kt +++ b/app/src/main/java/com/sopt/peekabookaos/presentation/splash/SplashActivity.kt @@ -2,20 +2,22 @@ package com.sopt.peekabookaos.presentation.splash import android.annotation.SuppressLint import android.content.Intent -import android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK -import android.content.Intent.FLAG_ACTIVITY_NEW_TASK import android.os.Bundle import android.os.Handler import android.os.Looper import androidx.activity.viewModels import com.sopt.peekabookaos.R import com.sopt.peekabookaos.databinding.ActivitySplashBinding -import com.sopt.peekabookaos.domain.entity.SplashState -import com.sopt.peekabookaos.domain.entity.VersionState -import com.sopt.peekabookaos.presentation.forceUpdate.ForceUpdateActivity +import com.sopt.peekabookaos.domain.entity.SplashUiState +import com.sopt.peekabookaos.domain.entity.UpdateInformation +import com.sopt.peekabookaos.presentation.forceUpdate.ForcedUpdateActivity import com.sopt.peekabookaos.presentation.main.MainActivity +import com.sopt.peekabookaos.presentation.networkError.NetworkErrorActivity.Companion.LOCATION +import com.sopt.peekabookaos.presentation.networkError.NetworkErrorActivity.Companion.NETWORK_ERROR import com.sopt.peekabookaos.presentation.onboarding.OnboardingActivity import com.sopt.peekabookaos.util.binding.BindingActivity +import com.sopt.peekabookaos.util.extensions.activityOpenTransition +import com.sopt.peekabookaos.util.extensions.repeatOnStarted import dagger.hilt.android.AndroidEntryPoint @AndroidEntryPoint @@ -25,42 +27,63 @@ class SplashActivity : BindingActivity(R.layout.activity_ override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - binding.lottieSplash.playAnimation() - Handler(Looper.getMainLooper()).postDelayed({ initIsForceUpdateObserver() }, DURATION) + checkLocation() } - private fun initIsForceUpdateObserver() { - splashViewModel.latestVersion.observe(this) { - splashViewModel.checkUpdateVersion() - checkVersionUpdate() + private fun checkLocation() { + when (intent.getStringExtra(LOCATION)) { + NETWORK_ERROR -> { + collectUiState() + } + + else -> { + binding.lottieSplash.playAnimation() + Handler(Looper.getMainLooper()).postDelayed({ collectUiState() }, DURATION) + } } } - private fun checkVersionUpdate() { - when (splashViewModel.checkUpdateVersion()) { - VersionState.LATEST -> checkSplashState() - VersionState.OUTDATED -> { - val intentToForceUpdate = Intent(this, ForceUpdateActivity::class.java).apply { - putExtra(LATEST_VERSION, splashViewModel.latestVersion.value) - addFlags(FLAG_ACTIVITY_CLEAR_TASK or FLAG_ACTIVITY_NEW_TASK) + private fun collectUiState() { + repeatOnStarted { + splashViewModel.uiState.collect { uiState -> + when (uiState) { + is SplashUiState.Idle -> {} + is SplashUiState.Error -> {} + is SplashUiState.ShowSplash -> {} + is SplashUiState.CanStartOnboarding -> startOnboardingActivity() + is SplashUiState.CanStartMain -> startMainActivity() + is SplashUiState.ForceUpdate -> { + startForcedUpdateActivity(uiState.data) + } } - startActivity(Intent(intentToForceUpdate)) - finish() } } } - private fun checkSplashState() { - when (splashViewModel.getSplashState()) { - SplashState.ONBOARDING -> startActivity(Intent(this, OnboardingActivity::class.java)) - SplashState.MAIN -> startActivity(Intent(this, MainActivity::class.java)) + private fun startForcedUpdateActivity(updateInformation: UpdateInformation) { + Intent(this, ForcedUpdateActivity::class.java).apply { + putExtra(UPDATE_INFORMATION, updateInformation) + }.also { + startActivityWithAnimation(it) } - overridePendingTransition(0, 0) - finish() + } + + private fun startOnboardingActivity() { + Intent(this, OnboardingActivity::class.java).also { startActivityWithAnimation(it) } + } + + private fun startMainActivity() { + Intent(this, MainActivity::class.java).also { startActivityWithAnimation(it) } + } + + private fun startActivityWithAnimation(intent: Intent) { + startActivity(intent) + activityOpenTransition(0, 0) + finishAffinity() } companion object { - private const val DURATION: Long = 2000 - const val LATEST_VERSION = "latest version" + private const val DURATION: Long = 2000L + const val UPDATE_INFORMATION = "update information" } } diff --git a/app/src/main/java/com/sopt/peekabookaos/presentation/splash/SplashViewModel.kt b/app/src/main/java/com/sopt/peekabookaos/presentation/splash/SplashViewModel.kt index 268ae523..c7a86a13 100644 --- a/app/src/main/java/com/sopt/peekabookaos/presentation/splash/SplashViewModel.kt +++ b/app/src/main/java/com/sopt/peekabookaos/presentation/splash/SplashViewModel.kt @@ -1,65 +1,53 @@ package com.sopt.peekabookaos.presentation.splash -import androidx.lifecycle.LiveData -import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.sopt.peekabookaos.BuildConfig -import com.sopt.peekabookaos.domain.entity.SplashState -import com.sopt.peekabookaos.domain.entity.Version -import com.sopt.peekabookaos.domain.entity.VersionDetail -import com.sopt.peekabookaos.domain.entity.VersionState -import com.sopt.peekabookaos.domain.usecase.GetSplashStateUseCase -import com.sopt.peekabookaos.domain.usecase.GetVersionUseCase +import com.sopt.peekabookaos.domain.entity.ForcedUpdate +import com.sopt.peekabookaos.domain.entity.SplashUiState +import com.sopt.peekabookaos.domain.usecase.GetSignedUpUseCase +import com.sopt.peekabookaos.domain.usecase.HasUpdateVersionCheckUseCase import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.launch import timber.log.Timber import javax.inject.Inject @HiltViewModel class SplashViewModel @Inject constructor( - private val getSplashStateUseCase: GetSplashStateUseCase, - private val getVersionUseCase: GetVersionUseCase + private val hasUpdateVersionCheckUseCase: HasUpdateVersionCheckUseCase, + private val getSignedUpUseCase: GetSignedUpUseCase ) : ViewModel() { - private val _latestVersion: MutableLiveData = MutableLiveData() - val latestVersion: LiveData = _latestVersion - private lateinit var latestVersionDetail: VersionDetail - private lateinit var appVersionDetail: VersionDetail + private val _uiState = MutableStateFlow(SplashUiState.Idle) + val uiState = _uiState.asSharedFlow() init { - getVersion() + checkHasUpdate() } - fun getSplashState(): SplashState = getSplashStateUseCase() + private fun isSignedUp(): Boolean = getSignedUpUseCase() - fun checkUpdateVersion(): VersionState { - latestVersionDetail = - spiltVersionToMajorMinor(requireNotNull(latestVersion.value?.androidForceVersion) { "version is null" }) - appVersionDetail = spiltVersionToMajorMinor(BuildConfig.VERSION_NAME) - return if (appVersionDetail.major != latestVersionDetail.major || appVersionDetail.minor != latestVersionDetail.minor) VersionState.OUTDATED - else VersionState.LATEST - } - - private fun spiltVersionToMajorMinor(versionName: String): VersionDetail { - val versionSpiltList = versionName.split(".") - val major = versionSpiltList[0] - val minor = versionSpiltList[1] - return VersionDetail(major, minor) - } + private fun checkHasUpdate() = viewModelScope.launch { + _uiState.emit(SplashUiState.Idle) + hasUpdateVersionCheckUseCase(BuildConfig.VERSION_NAME) + .onSuccess { update -> + when (update) { + is ForcedUpdate.None -> { + if (isSignedUp()) { + _uiState.emit(SplashUiState.CanStartMain) + } else { + _uiState.emit(SplashUiState.CanStartOnboarding) + } + } - private fun getVersion() { - viewModelScope.launch { - getVersionUseCase() - .onSuccess { response -> - _latestVersion.value = Version( - response.imageUrl, - response.androidForceVersion, - response.versionText - ) - } - .onFailure { throwable -> - Timber.e("$throwable") + is ForcedUpdate.Need -> { + _uiState.emit(SplashUiState.ForceUpdate(update.updateInformation)) + } } - } + }.onFailure { throwable -> + _uiState.emit(SplashUiState.Error) + Timber.e("$throwable") + } } } diff --git a/app/src/main/java/com/sopt/peekabookaos/presentation/userInput/UserInputViewModel.kt b/app/src/main/java/com/sopt/peekabookaos/presentation/userInput/UserInputViewModel.kt index a61ddd68..ea446ca5 100644 --- a/app/src/main/java/com/sopt/peekabookaos/presentation/userInput/UserInputViewModel.kt +++ b/app/src/main/java/com/sopt/peekabookaos/presentation/userInput/UserInputViewModel.kt @@ -68,13 +68,14 @@ class UserInputViewModel @Inject constructor( fun getNickNameState() { viewModelScope.launch { - postDuplicateUseCase(requireNotNull(nickname.value)).onSuccess { check -> - _isNickname.value = (check == 1) - updateNicknameMessage(true) - updateDuplicateButtonState(requireNotNull(_isNickname.value)) - }.onFailure { throwable -> - Timber.e("$throwable") - } + postDuplicateUseCase(requireNotNull(nickname.value)) + .onSuccess { check -> + _isNickname.value = (check == 1) + updateNicknameMessage(true) + updateDuplicateButtonState(requireNotNull(_isNickname.value)) + }.onFailure { throwable -> + Timber.e("$throwable") + } } } diff --git a/app/src/main/java/com/sopt/peekabookaos/presentation/withdraw/WithdrawViewModel.kt b/app/src/main/java/com/sopt/peekabookaos/presentation/withdraw/WithdrawViewModel.kt index 6397e0de..46c4cc96 100644 --- a/app/src/main/java/com/sopt/peekabookaos/presentation/withdraw/WithdrawViewModel.kt +++ b/app/src/main/java/com/sopt/peekabookaos/presentation/withdraw/WithdrawViewModel.kt @@ -2,10 +2,8 @@ package com.sopt.peekabookaos.presentation.withdraw import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import com.sopt.peekabookaos.domain.entity.SplashState import com.sopt.peekabookaos.domain.usecase.ClearLocalPrefUseCase import com.sopt.peekabookaos.domain.usecase.DeleteUserUseCase -import com.sopt.peekabookaos.domain.usecase.SetSplashStateUseCase import com.sopt.peekabookaos.util.UiEvent import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableSharedFlow @@ -17,8 +15,7 @@ import javax.inject.Inject @HiltViewModel class WithdrawViewModel @Inject constructor( private val deleteUserUseCase: DeleteUserUseCase, - private val clearLocalPrefUseCase: ClearLocalPrefUseCase, - private val setSplashStateUseCase: SetSplashStateUseCase + private val clearLocalPrefUseCase: ClearLocalPrefUseCase ) : ViewModel() { private val _uiEvent = MutableSharedFlow() val uiEvent = _uiEvent.asSharedFlow() @@ -30,7 +27,6 @@ class WithdrawViewModel @Inject constructor( .onSuccess { _uiEvent.emit(UiEvent.SUCCESS) clearLocalPrefUseCase() - setSplashStateUseCase(SplashState.ONBOARDING) }.onFailure { throwable -> _uiEvent.emit(UiEvent.ERROR) Timber.e("$throwable") diff --git a/app/src/main/java/com/sopt/peekabookaos/util/ImageUtil.kt b/app/src/main/java/com/sopt/peekabookaos/util/ImageUtil.kt index 6359f8df..9eaf5dc2 100644 --- a/app/src/main/java/com/sopt/peekabookaos/util/ImageUtil.kt +++ b/app/src/main/java/com/sopt/peekabookaos/util/ImageUtil.kt @@ -7,9 +7,7 @@ import android.graphics.BitmapFactory import android.graphics.Matrix import android.media.ExifInterface import android.net.Uri -import android.os.Build import android.provider.MediaStore -import androidx.annotation.RequiresApi import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import java.io.ByteArrayOutputStream @@ -38,7 +36,6 @@ object ImageUtil { return Uri.parse(path) } - @RequiresApi(Build.VERSION_CODES.N) fun getOrientationOfImage(uri: Uri, bitmap: Bitmap, cr: ContentResolver): Bitmap { val inputStream = cr.openInputStream(uri) val matrix = Matrix() diff --git a/app/src/main/java/com/sopt/peekabookaos/util/binding/BindingAdapter.kt b/app/src/main/java/com/sopt/peekabookaos/util/binding/BindingAdapter.kt index 5e4d672e..828c0001 100644 --- a/app/src/main/java/com/sopt/peekabookaos/util/binding/BindingAdapter.kt +++ b/app/src/main/java/com/sopt/peekabookaos/util/binding/BindingAdapter.kt @@ -4,7 +4,9 @@ import android.widget.ImageView import android.widget.TextView import androidx.core.content.ContextCompat import androidx.databinding.BindingAdapter -import com.bumptech.glide.Glide +import coil.imageLoader +import coil.request.ImageRequest +import coil.transform.CircleCropTransformation import com.sopt.peekabookaos.R object BindingAdapter { @@ -12,9 +14,11 @@ object BindingAdapter { @BindingAdapter("setImage") fun ImageView.setImage(imgUrl: String?) { this.let { - Glide.with(context) - .load(imgUrl) - .into(this) + val request = ImageRequest.Builder(context) + .data(imgUrl) + .target(this) + .build() + context.imageLoader.enqueue(request) } } @@ -22,15 +26,13 @@ object BindingAdapter { @BindingAdapter("setCircleImage") fun ImageView.setCircleImage(imgUrl: String?) { this.let { - Glide.with(context) - .load(imgUrl) - .apply { - if (imgUrl == null) { - placeholder(R.drawable.ic_profile_default) - } - } - .circleCrop() - .into(this) + val request = ImageRequest.Builder(context) + .data(imgUrl) + .target(this) + .transformations(CircleCropTransformation()) + .fallback(R.drawable.ic_profile_default) + .build() + context.imageLoader.enqueue(request) } } @@ -66,8 +68,8 @@ object BindingAdapter { } @JvmStatic - @BindingAdapter("setImageResource") - fun ImageView.setImageResource(resId: Int) { + @BindingAdapter("imageResource") + fun ImageView.setAppImageResource(resId: Int) { setImageResource(resId) } } diff --git a/app/src/main/java/com/sopt/peekabookaos/util/dialog/WarningDialogFragment.kt b/app/src/main/java/com/sopt/peekabookaos/util/dialog/WarningDialogFragment.kt index 0e58c2d9..f86e107b 100644 --- a/app/src/main/java/com/sopt/peekabookaos/util/dialog/WarningDialogFragment.kt +++ b/app/src/main/java/com/sopt/peekabookaos/util/dialog/WarningDialogFragment.kt @@ -7,6 +7,8 @@ import android.view.ViewGroup import androidx.fragment.app.DialogFragment import com.sopt.peekabookaos.R import com.sopt.peekabookaos.databinding.DialogWarningBinding +import com.sopt.peekabookaos.util.extensions.getParcelableCompat +import com.sopt.peekabookaos.util.extensions.getSerializableCompat import com.sopt.peekabookaos.util.extensions.initLayout import com.sopt.peekabookaos.util.extensions.setSingleOnClickListener import timber.log.Timber @@ -34,17 +36,20 @@ class WarningDialogFragment : DialogFragment() { } private fun initWarningDialogContent() { - val warningType = arguments?.get(WARNING_TYPE) + val warningType = arguments?.getSerializableCompat(WARNING_TYPE) as? WarningType ?: Timber.e(getString(R.string.null_point_exception_warning_dialog_argument)) val follower = arguments?.getString(FOLLOWER) ?: DEFAULT with(binding) { warning = when (warningType as WarningType) { WarningType.WARNING_RECOMMEND -> WarningDialogContent().getWarningRecommendBook(requireContext(), follower) + WarningType.WARNING_DELETE_BOOK -> WarningDialogContent().getWarningDeleteBook(requireContext()) + WarningType.WARNING_DELETE_FOLLOWER -> WarningDialogContent().getWarningDeleteFollow(requireContext(), follower) + WarningType.WARNING_UNFOLLOW -> WarningDialogContent().getWarningUnfollow(requireContext(), follower) } @@ -59,7 +64,7 @@ class WarningDialogFragment : DialogFragment() { private fun initConfirmClickListener() { binding.btnWarningDialogConfirm.setSingleOnClickListener { - arguments?.getParcelable(CONFIRM_ACTION) + arguments?.getParcelableCompat(CONFIRM_ACTION) ?.onConfirmClick() ?: Timber.e(getString(R.string.null_point_exception_warning_dialog_argument)) dismiss() diff --git a/app/src/main/java/com/sopt/peekabookaos/util/extensions/ActivityExt.kt b/app/src/main/java/com/sopt/peekabookaos/util/extensions/ActivityExt.kt index 8e44f653..058c43f1 100644 --- a/app/src/main/java/com/sopt/peekabookaos/util/extensions/ActivityExt.kt +++ b/app/src/main/java/com/sopt/peekabookaos/util/extensions/ActivityExt.kt @@ -1,12 +1,44 @@ package com.sopt.peekabookaos.util.extensions import android.app.Activity +import android.app.Activity.OVERRIDE_TRANSITION_CLOSE +import android.app.Activity.OVERRIDE_TRANSITION_OPEN import android.os.Build +import androidx.activity.addCallback +import androidx.appcompat.app.AppCompatActivity +import com.sopt.peekabookaos.R +import com.sopt.peekabookaos.util.ToastMessageUtil.showToast +import kotlin.system.exitProcess -fun Activity.activityTransition(transitionType: Int, enterAnim: Int, exitAnim: Int) { +fun Activity.activityOpenTransition(enterAnim: Int, exitAnim: Int) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { - overrideActivityTransition(transitionType, enterAnim, exitAnim) + overrideActivityTransition(OVERRIDE_TRANSITION_OPEN, enterAnim, exitAnim) } else { + @Suppress("DEPRECATION") overridePendingTransition(enterAnim, exitAnim) } } + +fun Activity.activityCloseTransition(enterAnim: Int, exitAnim: Int) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { + overrideActivityTransition(OVERRIDE_TRANSITION_CLOSE, enterAnim, exitAnim) + } else { + @Suppress("DEPRECATION") + overridePendingTransition(enterAnim, exitAnim) + } +} + +fun AppCompatActivity.initBackPressedCallback() { + var onBackPressedTime = 0L + val deadline = 2000L + onBackPressedDispatcher.addCallback(this) { + if (System.currentTimeMillis() - onBackPressedTime >= deadline) { + onBackPressedTime = System.currentTimeMillis() + showToast(this@initBackPressedCallback, getString(R.string.finish_app_toast_msg)) + } else { + this@initBackPressedCallback.finishAffinity() + System.runFinalization() + exitProcess(0) + } + } +} diff --git a/app/src/main/java/com/sopt/peekabookaos/util/extensions/BundleExt.kt b/app/src/main/java/com/sopt/peekabookaos/util/extensions/BundleExt.kt index 68ef973b..e827ca5c 100644 --- a/app/src/main/java/com/sopt/peekabookaos/util/extensions/BundleExt.kt +++ b/app/src/main/java/com/sopt/peekabookaos/util/extensions/BundleExt.kt @@ -2,11 +2,23 @@ package com.sopt.peekabookaos.util.extensions import android.os.Build import android.os.Bundle +import android.os.Parcelable +import java.io.Serializable -fun Bundle.getParcelableCompat(name: String, clazz: Class): T? { +inline fun Bundle.getParcelableCompat(key: String): T? { return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - getParcelable(name, clazz) + getParcelable(key, T::class.java) } else { - getParcelable(name) + @Suppress("DEPRECATION") + getParcelable(key) as? T + } +} + +inline fun Bundle.getSerializableCompat(key: String): T? { + return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + getSerializable(key, T::class.java) + } else { + @Suppress("DEPRECATION") + getSerializable(key) as? T } } diff --git a/app/src/main/java/com/sopt/peekabookaos/util/extensions/ContextExt.kt b/app/src/main/java/com/sopt/peekabookaos/util/extensions/ContextExt.kt index 916bfc52..7acaa065 100644 --- a/app/src/main/java/com/sopt/peekabookaos/util/extensions/ContextExt.kt +++ b/app/src/main/java/com/sopt/peekabookaos/util/extensions/ContextExt.kt @@ -3,7 +3,12 @@ package com.sopt.peekabookaos.util.extensions import android.content.Context import android.net.ConnectivityManager import android.net.NetworkCapabilities +import android.os.Build +import android.util.DisplayMetrics +import android.util.Size import android.view.View +import android.view.WindowInsets +import android.view.WindowManager import com.sopt.peekabookaos.util.OnSingleClickListener fun View.setSingleOnClickListener(onSingleClick: (View) -> Unit) { @@ -15,10 +20,39 @@ fun Context.isNetworkConnected(): Boolean { val cm = this.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager val capabilities = cm.getNetworkCapabilities(cm.activeNetwork) if (capabilities != null) { - isConnected = - capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) || capabilities.hasTransport( - NetworkCapabilities.TRANSPORT_CELLULAR - ) + isConnected = isConnectedToWiFi(capabilities) || isConnectedToCellular(capabilities) } return isConnected } + +private fun isConnectedToWiFi(capabilities: NetworkCapabilities): Boolean { + return capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) +} + +private fun isConnectedToCellular(capabilities: NetworkCapabilities): Boolean { + return capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) +} + +fun Context.getScreenSize(): Pair { + return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + val metrics = this.getSystemService(WindowManager::class.java).currentWindowMetrics + val windowInsets = metrics.windowInsets + + val insets = windowInsets.getInsetsIgnoringVisibility( + WindowInsets.Type.systemBars() or WindowInsets.Type.displayCutout() + ) + val insetsWidth = insets.right + insets.left + val insetsHeight = insets.top + insets.bottom + + val legacySize = Size( + metrics.bounds.width() - insetsWidth, + metrics.bounds.height() - insetsHeight + ) + Pair(legacySize.width, legacySize.height) + } else { + val metrics = DisplayMetrics() + @Suppress("DEPRECATION") + this.getSystemService(WindowManager::class.java).defaultDisplay.getRealMetrics(metrics) + Pair(metrics.widthPixels, metrics.heightPixels) + } +} diff --git a/app/src/main/java/com/sopt/peekabookaos/util/extensions/FragmentExt.kt b/app/src/main/java/com/sopt/peekabookaos/util/extensions/FragmentExt.kt index bac732bb..ac182e69 100644 --- a/app/src/main/java/com/sopt/peekabookaos/util/extensions/FragmentExt.kt +++ b/app/src/main/java/com/sopt/peekabookaos/util/extensions/FragmentExt.kt @@ -1,10 +1,39 @@ package com.sopt.peekabookaos.util.extensions import android.os.Bundle +import androidx.activity.OnBackPressedCallback import androidx.fragment.app.Fragment +import com.sopt.peekabookaos.R +import com.sopt.peekabookaos.util.ToastMessageUtil +import kotlin.system.exitProcess inline fun T.withArgs(argsBuilder: Bundle.() -> Unit): T { return this.apply { arguments = Bundle().apply(argsBuilder) } } + +fun Fragment.initBackPressedCallback() { + var onBackPressedTime = 0L + val deadline = 2000L + requireActivity().onBackPressedDispatcher.addCallback( + viewLifecycleOwner, + object : OnBackPressedCallback(true) { + override fun handleOnBackPressed() { + val curTime = System.currentTimeMillis() + val gap = curTime - onBackPressedTime + if (gap > deadline) { + onBackPressedTime = curTime + ToastMessageUtil.showToast( + requireContext(), + getString(R.string.finish_app_toast_msg) + ) + } else { + requireActivity().finishAffinity() + System.runFinalization() + exitProcess(0) + } + } + } + ) +} diff --git a/app/src/main/java/com/sopt/peekabookaos/util/extensions/IntentExt.kt b/app/src/main/java/com/sopt/peekabookaos/util/extensions/IntentExt.kt index ea7e0232..ec9bbbe9 100644 --- a/app/src/main/java/com/sopt/peekabookaos/util/extensions/IntentExt.kt +++ b/app/src/main/java/com/sopt/peekabookaos/util/extensions/IntentExt.kt @@ -7,6 +7,7 @@ fun Intent.getParcelable(name: String, clazz: Class): T? { return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { getParcelableExtra(name, clazz) } else { + @Suppress("DEPRECATION") getParcelableExtra(name) } } diff --git a/app/src/main/res/layout/activity_force_update.xml b/app/src/main/res/layout/activity_forced_update.xml similarity index 95% rename from app/src/main/res/layout/activity_force_update.xml rename to app/src/main/res/layout/activity_forced_update.xml index 666f325e..baead1c9 100644 --- a/app/src/main/res/layout/activity_force_update.xml +++ b/app/src/main/res/layout/activity_forced_update.xml @@ -7,14 +7,14 @@ + type="com.sopt.peekabookaos.presentation.forceUpdate.ForcedUpdateViewModel" /> + tools:context=".presentation.forceUpdate.ForcedUpdateActivity">