Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Seminar/7 #10

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
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 README/README_seminar_7.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
7주차 README입니다 - https://velog.io/@gxstxdgxs/THE-SOPT-Android-7차-세미나-과제
20 changes: 13 additions & 7 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,10 @@ android {
}

dependencies {
implementation 'androidx.core:core-ktx:1.7.0'
implementation 'androidx.appcompat:appcompat:1.4.1'
implementation 'com.google.android.material:material:1.6.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
implementation 'androidx.core:core-ktx:1.8.0'
implementation 'androidx.appcompat:appcompat:1.4.2'
implementation 'com.google.android.material:material:1.6.1'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
Expand All @@ -61,8 +61,8 @@ dependencies {
kapt 'androidx.room:room-compiler:2.4.2'

// Hilt
implementation "com.google.dagger:hilt-android:2.40"
kapt "com.google.dagger:hilt-android-compiler:2.40"
implementation "com.google.dagger:hilt-android:2.41"
kapt "com.google.dagger:hilt-android-compiler:2.41"

// ktx
implementation 'androidx.activity:activity-ktx:1.4.0'
Expand Down Expand Up @@ -90,5 +90,11 @@ dependencies {

// OkHttp3
implementation 'com.squareup.okhttp3:okhttp:5.0.0-alpha.2'
implementation 'com.squareup.okhttp3:logging-interceptor:4.9.1'
implementation 'com.squareup.okhttp3:logging-interceptor:4.9.3'

// Coroutines
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.0'

// lifecycle
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.4.1'
}
17 changes: 10 additions & 7 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,15 @@
android:supportsRtl="true"
android:theme="@style/Theme.SOPT_Seminar_30th"
android:usesCleartextTraffic="true">
<activity
android:name=".presentation.ui.onboarding.OnBoardingActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".presentation.ui.detail.DetailActivity"
android:exported="false" />
Expand All @@ -27,13 +36,7 @@
<activity
android:name=".presentation.ui.auth.SignInActivity"
android:exported="true"
android:windowSoftInputMode="adjustResize">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
android:windowSoftInputMode="adjustResize" />
</application>

</manifest>
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,21 @@ import android.content.Context
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
import co.kr.sopt_seminar_30th.data.datasource.local.AuthorizationDao
import co.kr.sopt_seminar_30th.data.datasource.local.FollowerDao
import co.kr.sopt_seminar_30th.data.datasource.local.RepositoryDao
import co.kr.sopt_seminar_30th.data.datasource.local.UserDao
import co.kr.sopt_seminar_30th.data.model.dto.AuthorizationDto
import co.kr.sopt_seminar_30th.data.model.dto.FollowerDto
import co.kr.sopt_seminar_30th.data.model.dto.RepositoryDto
import co.kr.sopt_seminar_30th.data.model.dto.UserDto

@Database(entities = [UserDto::class, FollowerDto::class, RepositoryDto::class], version = 1)
@Database(entities = [UserDto::class, FollowerDto::class, RepositoryDto::class, AuthorizationDto::class], version = 1)
abstract class AppDatabase : RoomDatabase() {
abstract fun userDao(): UserDao
abstract fun followerDao(): FollowerDao
abstract fun repositoryDao(): RepositoryDao
abstract fun authorizationDao(): AuthorizationDao

companion object {
fun getInstance(context: Context): AppDatabase = Room
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package co.kr.sopt_seminar_30th.data.datasource.local

import androidx.room.*
import co.kr.sopt_seminar_30th.data.model.dto.AuthorizationDto

@Dao
interface AuthorizationDao {
@Insert
suspend fun insertAuthorization(authorizationDto: AuthorizationDto)

@Delete
suspend fun deleteAuthorization(authorizationDto: AuthorizationDto)

@Query("SELECT * FROM Authorization WHERE userId = :id")
suspend fun getAuthorization(id: String): AuthorizationDto
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,8 @@ class SopthubDataStore @Inject constructor(
var autoLogin: Boolean
set(value) = dataStore.edit { putBoolean("AUTO_LOGIN", value) }
get() = dataStore.getBoolean("AUTO_LOGIN", false)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sharedPreference보다 dataStore를 사용하였을 때의 이점이 무엇이 있나요?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DataStore를 공부해보려고 하긴 했는데, 결과적으로 보면 저 dataStore라는 애는 SharedPreferences입니다. 왜냐면 context.getSharedPreferences() 메서드로 가져오기 때문이죠.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

제가 공부해보려고 한 이유는 이런 3가지 특징이 있어서라고 합니다.

  • DataStore는 코루틴과 Flow를 통해 읽고 쓰기에 대한 비동기 API를 제공한다.
  • DataStore는 UI 스레드를 호출해도 안전하다.
  • runtime exception으로부터 안전하다.


var onBoardingEnabled: Boolean
set(value) = dataStore.edit { putBoolean("ON_BOARDING_ENABLED", value) }
get() = dataStore.getBoolean("ON_BOARDING_ENABLED", false)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package co.kr.sopt_seminar_30th.data.model.dto

import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey

@Entity(tableName = "Authorization")
data class AuthorizationDto(
@PrimaryKey val userId: String,
@ColumnInfo(name = "autoLogin") val autoLogin: Boolean
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package co.kr.sopt_seminar_30th.data.repositoryimpl.local

import co.kr.sopt_seminar_30th.data.datasource.local.AuthorizationDao
import co.kr.sopt_seminar_30th.data.model.dto.AuthorizationDto
import co.kr.sopt_seminar_30th.domain.repository.local.AuthorizationRepository
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.withContext
import javax.inject.Inject

class AuthorizationRepositoryImpl @Inject constructor(
private val dao: AuthorizationDao,
private val coroutineDispatcher: CoroutineDispatcher
) : AuthorizationRepository {
override suspend fun insertAuthorization(userId: String, autoLogin: Boolean) =
withContext(coroutineDispatcher) {
dao.insertAuthorization(AuthorizationDto(userId, autoLogin))
}

override suspend fun deleteAuthorization(userId: String, autoLogin: Boolean) =
withContext(coroutineDispatcher) {
dao.deleteAuthorization(AuthorizationDto(userId, autoLogin))
}

override suspend fun getAuthorization(userId: String): Boolean =
withContext(coroutineDispatcher) {
dao.getAuthorization(userId).autoLogin
}
}
5 changes: 5 additions & 0 deletions app/src/main/java/co/kr/sopt_seminar_30th/di/DbModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package co.kr.sopt_seminar_30th.di

import android.content.Context
import co.kr.sopt_seminar_30th.data.database.AppDatabase
import co.kr.sopt_seminar_30th.data.datasource.local.AuthorizationDao
import co.kr.sopt_seminar_30th.data.datasource.local.FollowerDao
import co.kr.sopt_seminar_30th.data.datasource.local.RepositoryDao
import co.kr.sopt_seminar_30th.data.datasource.local.UserDao
Expand Down Expand Up @@ -33,4 +34,8 @@ object DbModule {
@Singleton
@Provides
fun provideRepositoryDao(appDatabase: AppDatabase): RepositoryDao = appDatabase.repositoryDao()

@Singleton
@Provides
fun provideAuthorizationDao(appDatabase: AppDatabase): AuthorizationDao = appDatabase.authorizationDao()
}
15 changes: 11 additions & 4 deletions app/src/main/java/co/kr/sopt_seminar_30th/di/RepositoryModule.kt
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
package co.kr.sopt_seminar_30th.di

import co.kr.sopt_seminar_30th.data.datasource.local.FollowerDao
import co.kr.sopt_seminar_30th.data.datasource.local.RepositoryDao
import co.kr.sopt_seminar_30th.data.datasource.local.SopthubDataStore
import co.kr.sopt_seminar_30th.data.datasource.local.UserDao
import co.kr.sopt_seminar_30th.data.datasource.local.*
import co.kr.sopt_seminar_30th.data.repositoryimpl.local.AuthorizationRepositoryImpl
import co.kr.sopt_seminar_30th.data.repositoryimpl.local.FollowerRepositoryImpl
import co.kr.sopt_seminar_30th.data.repositoryimpl.local.RepositoryRepositoryImpl
import co.kr.sopt_seminar_30th.data.repositoryimpl.local.UserRepositoryImpl
Expand All @@ -13,6 +11,7 @@ import co.kr.sopt_seminar_30th.data.repositoryimpl.remote.SignUpRepositoryImpl
import co.kr.sopt_seminar_30th.data.service.auth.SignInService
import co.kr.sopt_seminar_30th.data.service.auth.SignUpService
import co.kr.sopt_seminar_30th.data.service.home.HomeService
import co.kr.sopt_seminar_30th.domain.repository.local.AuthorizationRepository
import co.kr.sopt_seminar_30th.domain.repository.local.FollowerRepository
import co.kr.sopt_seminar_30th.domain.repository.local.RepositoryRepository
import co.kr.sopt_seminar_30th.domain.repository.local.UserRepository
Expand Down Expand Up @@ -68,4 +67,12 @@ object RepositoryModule {
homeService: HomeService,
@IoDispatcher coroutineDispatcher: CoroutineDispatcher
): HomeRepository = HomeRepositoryImpl(homeService, coroutineDispatcher)

@Singleton
@Provides
fun provideAuthorizationRepository(
authorizationDao: AuthorizationDao,
@IoDispatcher coroutineDispatcher: CoroutineDispatcher
): AuthorizationRepository =
AuthorizationRepositoryImpl(authorizationDao, coroutineDispatcher)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package co.kr.sopt_seminar_30th.domain.repository.local

interface AuthorizationRepository {
suspend fun insertAuthorization(userId: String, autoLogin: Boolean)
suspend fun deleteAuthorization(userId: String, autoLogin: Boolean)
suspend fun getAuthorization(userId: String): Boolean
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,18 @@ import android.os.Bundle
import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
import androidx.activity.viewModels
import androidx.lifecycle.flowWithLifecycle
import androidx.lifecycle.lifecycleScope
import co.kr.sopt_seminar_30th.R
import co.kr.sopt_seminar_30th.data.datasource.local.SopthubDataStore
import co.kr.sopt_seminar_30th.databinding.ActivitySignInBinding
import co.kr.sopt_seminar_30th.presentation.ui.base.BaseActivity
import co.kr.sopt_seminar_30th.presentation.ui.home.HomeActivity
import co.kr.sopt_seminar_30th.presentation.viewmodel.SignInViewModel
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import javax.inject.Inject

@AndroidEntryPoint
class SignInActivity : BaseActivity<ActivitySignInBinding>() {
Expand All @@ -19,6 +25,9 @@ class SignInActivity : BaseActivity<ActivitySignInBinding>() {

private val signInViewModel by viewModels<SignInViewModel>()

@Inject
lateinit var dataStore: SopthubDataStore

private val activityResultLauncher =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
if (it.resultCode == RESULT_OK) {
Expand All @@ -34,11 +43,18 @@ class SignInActivity : BaseActivity<ActivitySignInBinding>() {
binding.viewmodel = signInViewModel
binding.lifecycleOwner = this

loginWithAutoLogin()
signUp()
login()
observeLogin()
}

private fun loginWithAutoLogin() {
if (dataStore.autoLogin) {
signInViewModel.checkAuthorization(dataStore.userId)
}
}

private fun signUp() {
binding.btnSignUp.setOnClickListener {
val intent = Intent(this, SignUpActivity::class.java)
Expand All @@ -65,34 +81,30 @@ class SignInActivity : BaseActivity<ActivitySignInBinding>() {
}
}
}

signInViewModel.isEmailIncorrect.observe(this) {
if(it) {
if (it) {
Toast.makeText(this, "존재하지 않는 계정입니다", Toast.LENGTH_SHORT).show()
binding.etUserPassword.text.clear()
}
}

signInViewModel.isPasswordIncorrect.observe(this) {
if(it) {
if (it) {
Toast.makeText(this, "비밀번호가 일치하지 않습니다", Toast.LENGTH_SHORT).show()
binding.etUserPassword.text.clear()
}
}

signInViewModel.isEmpty.observe(this) {
if (it) {
Toast.makeText(this, "아이디/비밀번호를 확인해주세요", Toast.LENGTH_SHORT).show()
}
}

signInViewModel.autoLogin.observe(this) {
if (it) {
Toast.makeText(this, "자동로그인 성공", Toast.LENGTH_SHORT).show()
val intent = Intent(this, HomeActivity::class.java)
startActivity(intent)
finish()
}
}
signInViewModel.autoLoginState
.flowWithLifecycle(this.lifecycle)
.onEach {
if(it) {
startActivity(Intent(this, HomeActivity::class.java))
finish()
}
}.launchIn(this.lifecycleScope)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package co.kr.sopt_seminar_30th.presentation.ui.onboarding

import android.content.Intent
import android.os.Bundle
import androidx.activity.viewModels
import androidx.lifecycle.flowWithLifecycle
import androidx.lifecycle.lifecycleScope
import co.kr.sopt_seminar_30th.R
import co.kr.sopt_seminar_30th.data.datasource.local.SopthubDataStore
import co.kr.sopt_seminar_30th.databinding.ActivityOnBoardingBinding
import co.kr.sopt_seminar_30th.presentation.ui.auth.SignInActivity
import co.kr.sopt_seminar_30th.presentation.ui.base.BaseActivity
import co.kr.sopt_seminar_30th.presentation.ui.home.HomeActivity
import co.kr.sopt_seminar_30th.presentation.viewmodel.OnBoardingViewModel
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import javax.inject.Inject

@AndroidEntryPoint
class OnBoardingActivity : BaseActivity<ActivityOnBoardingBinding>() {
override val layoutRes: Int
get() = R.layout.activity_on_boarding

private val viewModel by viewModels<OnBoardingViewModel>()

@Inject
lateinit var dataStore: SopthubDataStore

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
checkAutoLoginEnabled()
checkOnBoardingEnabled()
observeData()
}

private fun checkAutoLoginEnabled() {
if (dataStore.autoLogin) {
viewModel.checkAuthorization(dataStore.userId)
}
}

private fun checkOnBoardingEnabled() {
if (dataStore.onBoardingEnabled) {
if (!isFinishing) {
startActivity(Intent(this, SignInActivity::class.java))
finish()
}
}
}

private fun observeData() {
viewModel.autoLoginState
.flowWithLifecycle(this.lifecycle)
.onEach {
if (it) {
startActivity(Intent(this, HomeActivity::class.java))
finish()
}
}.launchIn(this.lifecycleScope)
}
}
Loading