diff --git a/.idea/deploymentTargetDropDown.xml b/.idea/deploymentTargetDropDown.xml new file mode 100644 index 00000000..7047cf08 --- /dev/null +++ b/.idea/deploymentTargetDropDown.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index d2bd69d9..a27ad946 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -15,8 +15,8 @@ android { applicationId "com.teamsparker.android" minSdk 26 targetSdk 31 - versionCode 4 - versionName "1.0.2" + versionCode 5 + versionName "1.1.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } diff --git a/app/src/main/java/com/teamsparker/android/data/local/datasource/LocalPreferencesDataSourceImpl.kt b/app/src/main/java/com/teamsparker/android/data/local/datasource/LocalPreferencesDataSourceImpl.kt index 080aa41e..31bb5a77 100644 --- a/app/src/main/java/com/teamsparker/android/data/local/datasource/LocalPreferencesDataSourceImpl.kt +++ b/app/src/main/java/com/teamsparker/android/data/local/datasource/LocalPreferencesDataSourceImpl.kt @@ -105,10 +105,10 @@ class LocalPreferencesDataSourceImpl @Inject constructor( } companion object { - private const val ACCESS_TOKEN = "ACCESS_TOKEN" - private const val USER_KAKAO_USER_ID = "USER_KAKAO_USER_ID" - private const val USER_NICKNAME = "USER_NAME" - private const val ALARM_LOCAL_SAVED = "ALARM_LOCAL_SAVED" + const val ACCESS_TOKEN = "ACCESS_TOKEN" + const val USER_KAKAO_USER_ID = "USER_KAKAO_USER_ID" + const val USER_NICKNAME = "USER_NAME" + const val ALARM_LOCAL_SAVED = "ALARM_LOCAL_SAVED" const val DEFAULT_STRING_VALUE = "" } } diff --git a/app/src/main/java/com/teamsparker/android/data/remote/RetrofitBuilder.kt b/app/src/main/java/com/teamsparker/android/data/remote/RetrofitBuilder.kt index 410e778e..b41134b5 100644 --- a/app/src/main/java/com/teamsparker/android/data/remote/RetrofitBuilder.kt +++ b/app/src/main/java/com/teamsparker/android/data/remote/RetrofitBuilder.kt @@ -1,5 +1,6 @@ package com.teamsparker.android.data.remote +import com.teamsparker.android.data.remote.calladapter.CustomCallAdapterFactory import com.teamsparker.android.data.remote.service.* import okhttp3.Interceptor import okhttp3.OkHttpClient @@ -28,19 +29,20 @@ object RetrofitBuilder { .addInterceptor(headerInterceptor) .build() - private val retrofit: Retrofit = Retrofit.Builder() .baseUrl(BASE_URL) .client(okHttpClient) + .addCallAdapterFactory(CustomCallAdapterFactory()) .addConverterFactory(GsonConverterFactory.create()) .build() // 이 밑에다가 이런식으로 서비스 객체 생성하기 // val sampleService: SampleService = retrofit.create(SampleService::class.java) - val habitService : HabitService = retrofit.create(HabitService::class.java) + val habitService: HabitService = retrofit.create(HabitService::class.java) val storageService: StorageService = retrofit.create(StorageService::class.java) - val photoCollectionService: PhotoCollectionService = retrofit.create(PhotoCollectionService::class.java) + val photoCollectionService: PhotoCollectionService = + retrofit.create(PhotoCollectionService::class.java) val photoMainService: PhotoMainService = retrofit.create(PhotoMainService::class.java) val setStatusService: SetStatusService = retrofit.create(SetStatusService::class.java) val sendSparkService: SendSparkService = retrofit.create(SendSparkService::class.java) diff --git a/app/src/main/java/com/teamsparker/android/data/remote/calladapter/CustomCall.kt b/app/src/main/java/com/teamsparker/android/data/remote/calladapter/CustomCall.kt new file mode 100644 index 00000000..75b66486 --- /dev/null +++ b/app/src/main/java/com/teamsparker/android/data/remote/calladapter/CustomCall.kt @@ -0,0 +1,69 @@ +package com.teamsparker.android.data.remote.calladapter + +import okhttp3.Request +import okio.Timeout +import retrofit2.Call +import retrofit2.Callback +import retrofit2.Response +import java.io.IOException + +class CustomCall(private val call: Call) : Call> { + + override fun enqueue(callback: Callback>) { + call.enqueue(object : Callback { + override fun onResponse(call: Call, response: Response) { + val body = response.body() + val code = response.code() + val error = response.errorBody()?.string() + + if (response.isSuccessful) { + if (body != null) { + callback.onResponse( + this@CustomCall, + Response.success(NetworkState.Success(body)) + ) + } else { + callback.onResponse( + this@CustomCall, + Response.success( + NetworkState.UnknownError( + IllegalStateException("body값이 null로 넘어옴"), + "body값이 null로 넘어옴" + ) + ) + ) + } + } else { + callback.onResponse( + this@CustomCall, + Response.success(NetworkState.Failure(code, error)) + ) + } + } + + override fun onFailure(call: Call, t: Throwable) { + val errorResponse = when (t) { + is IOException -> NetworkState.NetworkError(t) + else -> NetworkState.UnknownError(t, "onFailure에 진입,IoException 이외의 에러") + } + callback.onResponse(this@CustomCall, Response.success(errorResponse)) + } + }) + } + + override fun clone(): Call> = CustomCall(call.clone()) + + override fun execute(): Response> { + throw UnsupportedOperationException("커스텀한 callAdapter에서는 execute를 사용하지 않습니다 ") + } + + override fun isExecuted(): Boolean = call.isExecuted + + override fun cancel() = call.cancel() + + override fun isCanceled(): Boolean = call.isCanceled + + override fun request(): Request = call.request() + + override fun timeout(): Timeout = call.timeout() +} diff --git a/app/src/main/java/com/teamsparker/android/data/remote/calladapter/CustomCallAdapter.kt b/app/src/main/java/com/teamsparker/android/data/remote/calladapter/CustomCallAdapter.kt new file mode 100644 index 00000000..171e9a27 --- /dev/null +++ b/app/src/main/java/com/teamsparker/android/data/remote/calladapter/CustomCallAdapter.kt @@ -0,0 +1,12 @@ +package com.teamsparker.android.data.remote.calladapter + +import retrofit2.Call +import retrofit2.CallAdapter +import java.lang.reflect.Type + +class CustomCallAdapter(private val responseType: Type) : + CallAdapter>> { + override fun responseType(): Type = responseType + + override fun adapt(call: Call): Call> = CustomCall(call) +} diff --git a/app/src/main/java/com/teamsparker/android/data/remote/calladapter/CustomCallAdapterFactory.kt b/app/src/main/java/com/teamsparker/android/data/remote/calladapter/CustomCallAdapterFactory.kt new file mode 100644 index 00000000..f9127f75 --- /dev/null +++ b/app/src/main/java/com/teamsparker/android/data/remote/calladapter/CustomCallAdapterFactory.kt @@ -0,0 +1,38 @@ +package com.teamsparker.android.data.remote.calladapter + +import retrofit2.Call +import retrofit2.CallAdapter +import retrofit2.Retrofit +import java.lang.reflect.ParameterizedType +import java.lang.reflect.Type + +class CustomCallAdapterFactory : CallAdapter.Factory() { + + override fun get( + returnType: Type, + annotations: Array, + retrofit: Retrofit + ): CallAdapter<*, *>? { + if (Call::class.java != getRawType(returnType)) { + return null + } + + check(returnType is ParameterizedType) { + "return type must be parameterized as Call> or Call>" + } + + val responseType = getParameterUpperBound(0, returnType) + + if (getRawType(responseType) != NetworkState::class.java) { + return null + } + + check(responseType is ParameterizedType) { + "Response must be parameterized as NetworkState or NetworkState" + } + + val bodyType = getParameterUpperBound(0, responseType) + + return CustomCallAdapter(bodyType) + } +} diff --git a/app/src/main/java/com/teamsparker/android/data/remote/calladapter/NetworkState.kt b/app/src/main/java/com/teamsparker/android/data/remote/calladapter/NetworkState.kt new file mode 100644 index 00000000..ab91c64b --- /dev/null +++ b/app/src/main/java/com/teamsparker/android/data/remote/calladapter/NetworkState.kt @@ -0,0 +1,18 @@ +package com.teamsparker.android.data.remote.calladapter + +import java.io.IOException + +sealed class NetworkState { + + // 200대 응답 성공한것 + data class Success(val body: T) : NetworkState() + + // isSuccessful 이 false인 경우(200~300대 응답이 아닌경우) + data class Failure(val code: Int, val error: String?) : NetworkState() + + // onFailure로 넘어간경우(네트워크 오류,timeout 같은거) + data class NetworkError(val error: IOException) : NetworkState() + + // 예상 못한에러(기타 모든 에러처리) + data class UnknownError(val t: Throwable?, val errorState: String) : NetworkState() +} diff --git a/app/src/main/java/com/teamsparker/android/data/remote/calladapter/RetrofitFaliureStateException.kt b/app/src/main/java/com/teamsparker/android/data/remote/calladapter/RetrofitFaliureStateException.kt new file mode 100644 index 00000000..1837f886 --- /dev/null +++ b/app/src/main/java/com/teamsparker/android/data/remote/calladapter/RetrofitFaliureStateException.kt @@ -0,0 +1,3 @@ +package com.teamsparker.android.data.remote.calladapter + +class RetrofitFailureStateException(error: String?, val code: Int) : Exception(error) diff --git a/app/src/main/java/com/teamsparker/android/data/remote/datasource/HabitRoomTImeLineDataSourceImpl.kt b/app/src/main/java/com/teamsparker/android/data/remote/datasource/HabitRoomTImeLineDataSourceImpl.kt new file mode 100644 index 00000000..e27f7819 --- /dev/null +++ b/app/src/main/java/com/teamsparker/android/data/remote/datasource/HabitRoomTImeLineDataSourceImpl.kt @@ -0,0 +1,14 @@ +package com.teamsparker.android.data.remote.datasource + +import com.teamsparker.android.data.remote.calladapter.NetworkState +import com.teamsparker.android.data.remote.entity.response.BaseResponse +import com.teamsparker.android.data.remote.entity.response.HabitRoomTimeLine +import com.teamsparker.android.data.remote.service.HabitRoomTimeLineService +import javax.inject.Inject + +class HabitRoomTImeLineDataSourceImpl @Inject constructor( + private val habitRoomTimeLineService: HabitRoomTimeLineService +) : HabitRoomTimeLineDataSource { + override suspend fun getHabitRoomTimeLine(roomId: Int): NetworkState> = + habitRoomTimeLineService.getHabitRoomRimeLine(roomId) +} diff --git a/app/src/main/java/com/teamsparker/android/data/remote/datasource/HabitRoomTimeLineDataSource.kt b/app/src/main/java/com/teamsparker/android/data/remote/datasource/HabitRoomTimeLineDataSource.kt new file mode 100644 index 00000000..f5e17aac --- /dev/null +++ b/app/src/main/java/com/teamsparker/android/data/remote/datasource/HabitRoomTimeLineDataSource.kt @@ -0,0 +1,10 @@ +package com.teamsparker.android.data.remote.datasource + +import com.teamsparker.android.data.remote.calladapter.NetworkState +import com.teamsparker.android.data.remote.entity.response.BaseResponse +import com.teamsparker.android.data.remote.entity.response.HabitRoomTimeLine + +interface HabitRoomTimeLineDataSource { + + suspend fun getHabitRoomTimeLine(roomId: Int): NetworkState> +} diff --git a/app/src/main/java/com/teamsparker/android/data/remote/entity/response/HabitResponse.kt b/app/src/main/java/com/teamsparker/android/data/remote/entity/response/HabitResponse.kt index e4a54784..e914cf70 100644 --- a/app/src/main/java/com/teamsparker/android/data/remote/entity/response/HabitResponse.kt +++ b/app/src/main/java/com/teamsparker/android/data/remote/entity/response/HabitResponse.kt @@ -5,7 +5,6 @@ data class HabitResponse( val roomName: String, val leftDay: Int, val life: Int, - val lifeDeductionCount : Int, val startDate: String, val endDate: String, val fromStart: Boolean, @@ -13,6 +12,8 @@ data class HabitResponse( val purpose: String, val myRecord: HabitRecord, val otherRecords: List, + val isTimelineNew: Boolean, + val isTermNew: Boolean ) data class HabitRecord( @@ -22,7 +23,7 @@ data class HabitRecord( val recordId: Int, val rest: Int = -1, val status: String, - val userId: Int, + val userId: Int ) data class OtherRecord( @@ -30,5 +31,5 @@ data class OtherRecord( val profileImg: String, val recordId: Int, val status: String, - val userId: Int, + val userId: Int ) diff --git a/app/src/main/java/com/teamsparker/android/data/remote/entity/response/HabitRoomTimeLineResponse.kt b/app/src/main/java/com/teamsparker/android/data/remote/entity/response/HabitRoomTimeLineResponse.kt new file mode 100644 index 00000000..96bb3ba0 --- /dev/null +++ b/app/src/main/java/com/teamsparker/android/data/remote/entity/response/HabitRoomTimeLineResponse.kt @@ -0,0 +1,21 @@ +package com.teamsparker.android.data.remote.entity.response + +import com.google.gson.annotations.SerializedName + +data class HabitRoomTimeLine( + @SerializedName("timelines") + val timelines: List +) + +data class Timeline( + @SerializedName("content") + val content: String, + @SerializedName("day") + val day: String, + @SerializedName("isNew") + val isNew: Boolean, + @SerializedName("profiles") + val profiles: List, + @SerializedName("title") + val title: String +) diff --git a/app/src/main/java/com/teamsparker/android/data/remote/repository/HabitRepository.kt b/app/src/main/java/com/teamsparker/android/data/remote/repository/HabitRepository.kt index 157cf409..7f822c75 100644 --- a/app/src/main/java/com/teamsparker/android/data/remote/repository/HabitRepository.kt +++ b/app/src/main/java/com/teamsparker/android/data/remote/repository/HabitRepository.kt @@ -1,8 +1,12 @@ package com.teamsparker.android.data.remote.repository +import com.teamsparker.android.data.remote.entity.response.HabitRoomTimeLine + interface HabitRepository { fun setHabitUserGuideState(state: Boolean) fun getHabitUserGuideState(): Boolean + + suspend fun getHabitRoomTimeLine(roomId: Int): Result } diff --git a/app/src/main/java/com/teamsparker/android/data/remote/repository/HabitRepositoryImpl.kt b/app/src/main/java/com/teamsparker/android/data/remote/repository/HabitRepositoryImpl.kt index 238fb60a..9fd19c8d 100644 --- a/app/src/main/java/com/teamsparker/android/data/remote/repository/HabitRepositoryImpl.kt +++ b/app/src/main/java/com/teamsparker/android/data/remote/repository/HabitRepositoryImpl.kt @@ -1,10 +1,17 @@ package com.teamsparker.android.data.remote.repository import com.teamsparker.android.data.local.datasource.LocalPreferencesHabitDataSource +import com.teamsparker.android.data.remote.calladapter.NetworkState +import com.teamsparker.android.data.remote.calladapter.RetrofitFailureStateException +import com.teamsparker.android.data.remote.datasource.HabitRoomTimeLineDataSource +import com.teamsparker.android.data.remote.entity.response.HabitRoomTimeLine +import timber.log.Timber.Forest.tag +import java.security.cert.CertificateException import javax.inject.Inject class HabitRepositoryImpl @Inject constructor( - private val localPreferencesHabitDataSource: LocalPreferencesHabitDataSource + private val localPreferencesHabitDataSource: LocalPreferencesHabitDataSource, + private val habitRoomTimeLineDataSource: HabitRoomTimeLineDataSource ) : HabitRepository { override fun setHabitUserGuideState(state: Boolean) { @@ -13,4 +20,22 @@ class HabitRepositoryImpl @Inject constructor( override fun getHabitUserGuideState(): Boolean = localPreferencesHabitDataSource.getHabitUserGuideState() + + override suspend fun getHabitRoomTimeLine(roomId: Int): Result { + when (val response = habitRoomTimeLineDataSource.getHabitRoomTimeLine(roomId)) { + is NetworkState.Success -> return Result.success( + response.body.data + ) + is NetworkState.Failure -> + if (response.code == 401) throw CertificateException("토큰 만료 오류") + else return Result.failure( + RetrofitFailureStateException(response.error, response.code) + ) + is NetworkState.NetworkError -> tag("${this.javaClass.name}_getHabitRoomTimeLine") + .d(response.error) + is NetworkState.UnknownError -> tag("${this.javaClass.name}_getHabitRoomTimeLine") + .d(response.t) + } + return Result.failure(IllegalStateException("NetworkError or UnKnownError please check timber")) + } } diff --git a/app/src/main/java/com/teamsparker/android/data/remote/service/HabitRoomTimeLineService.kt b/app/src/main/java/com/teamsparker/android/data/remote/service/HabitRoomTimeLineService.kt new file mode 100644 index 00000000..f28860a2 --- /dev/null +++ b/app/src/main/java/com/teamsparker/android/data/remote/service/HabitRoomTimeLineService.kt @@ -0,0 +1,14 @@ +package com.teamsparker.android.data.remote.service + +import com.teamsparker.android.data.remote.calladapter.NetworkState +import com.teamsparker.android.data.remote.entity.response.BaseResponse +import com.teamsparker.android.data.remote.entity.response.HabitRoomTimeLine +import retrofit2.http.GET +import retrofit2.http.Path + +interface HabitRoomTimeLineService { + @GET("room/{roomId}/timeline") + suspend fun getHabitRoomRimeLine( + @Path("roomId") roomId: Int + ): NetworkState> +} diff --git a/app/src/main/java/com/teamsparker/android/di/RemoteDataSourceModule.kt b/app/src/main/java/com/teamsparker/android/di/RemoteDataSourceModule.kt index 332f68c0..513777c0 100644 --- a/app/src/main/java/com/teamsparker/android/di/RemoteDataSourceModule.kt +++ b/app/src/main/java/com/teamsparker/android/di/RemoteDataSourceModule.kt @@ -2,6 +2,7 @@ package com.teamsparker.android.di import com.teamsparker.android.data.remote.datasource.* import com.teamsparker.android.data.remote.service.* +import dagger.Binds import dagger.Module import dagger.Provides import dagger.hilt.InstallIn @@ -57,4 +58,11 @@ object RemoteDataSourceModule { alarmCenterService: AlarmCenterService ): AlarmCenterDataSource = AlarmCenterDataSourceImpl(alarmCenterService) + + @Provides + @Singleton + fun provideHabitRoomTimeLineDataSource( + habitRoomTimeLineService: HabitRoomTimeLineService + ): HabitRoomTimeLineDataSource = + HabitRoomTImeLineDataSourceImpl(habitRoomTimeLineService) } diff --git a/app/src/main/java/com/teamsparker/android/di/RepositoryModule.kt b/app/src/main/java/com/teamsparker/android/di/RepositoryModule.kt index 76fcd9e9..558280b1 100644 --- a/app/src/main/java/com/teamsparker/android/di/RepositoryModule.kt +++ b/app/src/main/java/com/teamsparker/android/di/RepositoryModule.kt @@ -3,12 +3,7 @@ package com.teamsparker.android.di import com.teamsparker.android.data.local.datasource.* import com.teamsparker.android.data.remote.datasource.* import com.teamsparker.android.data.remote.repository.* -import com.teamsparker.android.data.remote.service.JoinCodeRoomDoneService -import com.teamsparker.android.data.remote.service.JoinCodeRoomInfoService -import com.teamsparker.android.data.remote.service.MakeRoomService -import com.teamsparker.android.data.remote.service.RefreshService -import com.teamsparker.android.data.remote.service.SetPurposeService -import com.teamsparker.android.data.remote.service.StartHabitService +import com.teamsparker.android.data.remote.service.* import dagger.Module import dagger.Provides import dagger.hilt.InstallIn @@ -120,9 +115,8 @@ object RepositoryModule { @Provides @Singleton fun providesHabitRepository( - localPreferencesHabitDataSource: LocalPreferencesHabitDataSource + localPreferencesHabitDataSource: LocalPreferencesHabitDataSource, + habitRoomTimeLineDataSource: HabitRoomTimeLineDataSource ): HabitRepository = - HabitRepositoryImpl(localPreferencesHabitDataSource) + HabitRepositoryImpl(localPreferencesHabitDataSource, habitRoomTimeLineDataSource) } - - diff --git a/app/src/main/java/com/teamsparker/android/di/RetrofitModule.kt b/app/src/main/java/com/teamsparker/android/di/RetrofitModule.kt index e2fe353a..3cbd94b2 100644 --- a/app/src/main/java/com/teamsparker/android/di/RetrofitModule.kt +++ b/app/src/main/java/com/teamsparker/android/di/RetrofitModule.kt @@ -1,6 +1,7 @@ package com.teamsparker.android.di import com.teamsparker.android.data.local.datasource.LocalPreferencesDataSource +import com.teamsparker.android.data.remote.calladapter.CustomCallAdapterFactory import dagger.Module import dagger.Provides import dagger.hilt.InstallIn @@ -45,6 +46,7 @@ object RetrofitModule { Retrofit.Builder() .baseUrl("https://asia-northeast3-we-sopt-spark.cloudfunctions.net/api/") .client(okHttpClient) + .addCallAdapterFactory(CustomCallAdapterFactory()) .addConverterFactory(GsonConverterFactory.create()) .build() } diff --git a/app/src/main/java/com/teamsparker/android/di/RetrofitServiceModule.kt b/app/src/main/java/com/teamsparker/android/di/RetrofitServiceModule.kt index da53980a..ebc0078b 100644 --- a/app/src/main/java/com/teamsparker/android/di/RetrofitServiceModule.kt +++ b/app/src/main/java/com/teamsparker/android/di/RetrofitServiceModule.kt @@ -95,4 +95,9 @@ object RetrofitServiceModule { @Singleton fun provideHomeNoticeRedDotService(retrofit: Retrofit): HomeNoticeRedDotService = retrofit.create(HomeNoticeRedDotService::class.java) + + @Provides + @Singleton + fun provideHabitRoomTimeLineService(retrofit: Retrofit): HabitRoomTimeLineService = + retrofit.create(HabitRoomTimeLineService::class.java) } diff --git a/app/src/main/java/com/teamsparker/android/ui/base/BaseActivity.kt b/app/src/main/java/com/teamsparker/android/ui/base/BaseActivity.kt index 6a6f066b..a0cf1e46 100644 --- a/app/src/main/java/com/teamsparker/android/ui/base/BaseActivity.kt +++ b/app/src/main/java/com/teamsparker/android/ui/base/BaseActivity.kt @@ -1,10 +1,28 @@ package com.teamsparker.android.ui.base +import android.content.Intent +import android.content.SharedPreferences import android.os.Bundle import androidx.annotation.LayoutRes import androidx.appcompat.app.AppCompatActivity +import androidx.core.content.edit import androidx.databinding.DataBindingUtil import androidx.databinding.ViewDataBinding +import com.teamsparker.android.data.local.datasource.LocalPreferencesDataSourceImpl +import com.teamsparker.android.data.local.datasource.LocalPreferencesDataSourceImpl.Companion.ACCESS_TOKEN +import com.teamsparker.android.data.local.datasource.LocalPreferencesDataSourceImpl.Companion.ALARM_LOCAL_SAVED +import com.teamsparker.android.data.local.datasource.LocalPreferencesDataSourceImpl.Companion.USER_KAKAO_USER_ID +import com.teamsparker.android.ui.alarmsetting.AlarmSettingViewModel +import com.teamsparker.android.ui.alarmsetting.AlarmSettingViewModel.Companion.ALARM_CERTIFICATION +import com.teamsparker.android.ui.alarmsetting.AlarmSettingViewModel.Companion.ALARM_CONSIDER +import com.teamsparker.android.ui.alarmsetting.AlarmSettingViewModel.Companion.ALARM_REMIND +import com.teamsparker.android.ui.alarmsetting.AlarmSettingViewModel.Companion.ALARM_ROOM_START +import com.teamsparker.android.ui.alarmsetting.AlarmSettingViewModel.Companion.ALARM_SPARK +import com.teamsparker.android.ui.auth.AuthActivity +import com.teamsparker.android.util.EventObserver +import com.teamsparker.android.util.Injector +import dagger.hilt.android.EntryPointAccessors +import com.teamsparker.android.ui.alarmsetting.AlarmSettingViewModel.Companion as AlarmSettingViewModel1 abstract class BaseActivity( @LayoutRes private val layoutRes: Int @@ -13,7 +31,33 @@ abstract class BaseActivity( override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - binding = DataBindingUtil.setContentView(this,layoutRes) + binding = DataBindingUtil.setContentView(this, layoutRes) binding.lifecycleOwner = this } + + private val sharedPreferences: SharedPreferences by lazy { + EntryPointAccessors.fromActivity( + this, + Injector.SharedPreferencesInjector::class.java + ).sharedPreferences() + } + + protected fun terminationTokenHandling(viewModel: BaseViewModel) { + viewModel.moveToLogin.observe( + this, + EventObserver { + val intent = Intent(this, AuthActivity::class.java) + startActivity(intent) + sharedPreferences.edit { remove(ACCESS_TOKEN) } + sharedPreferences.edit { remove(USER_KAKAO_USER_ID) } + sharedPreferences.edit { remove(ALARM_LOCAL_SAVED) } + sharedPreferences.edit { remove(ALARM_ROOM_START) } + sharedPreferences.edit { remove(ALARM_SPARK) } + sharedPreferences.edit { remove(ALARM_CONSIDER) } + sharedPreferences.edit { remove(ALARM_CERTIFICATION) } + sharedPreferences.edit { remove(ALARM_REMIND) } + finishAffinity() + } + ) + } } diff --git a/app/src/main/java/com/teamsparker/android/ui/base/BaseFragment.kt b/app/src/main/java/com/teamsparker/android/ui/base/BaseFragment.kt index 507963c0..73140fef 100644 --- a/app/src/main/java/com/teamsparker/android/ui/base/BaseFragment.kt +++ b/app/src/main/java/com/teamsparker/android/ui/base/BaseFragment.kt @@ -1,13 +1,30 @@ package com.teamsparker.android.ui.base +import android.content.Intent +import android.content.SharedPreferences import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.annotation.LayoutRes +import androidx.core.content.edit import androidx.databinding.DataBindingUtil import androidx.databinding.ViewDataBinding import androidx.fragment.app.Fragment +import com.teamsparker.android.data.local.datasource.LocalPreferencesDataSourceImpl +import com.teamsparker.android.data.local.datasource.LocalPreferencesDataSourceImpl.Companion.ACCESS_TOKEN +import com.teamsparker.android.data.local.datasource.LocalPreferencesDataSourceImpl.Companion.ALARM_LOCAL_SAVED +import com.teamsparker.android.data.local.datasource.LocalPreferencesDataSourceImpl.Companion.USER_KAKAO_USER_ID +import com.teamsparker.android.ui.alarmsetting.AlarmSettingViewModel +import com.teamsparker.android.ui.alarmsetting.AlarmSettingViewModel.Companion.ALARM_CERTIFICATION +import com.teamsparker.android.ui.alarmsetting.AlarmSettingViewModel.Companion.ALARM_CONSIDER +import com.teamsparker.android.ui.alarmsetting.AlarmSettingViewModel.Companion.ALARM_REMIND +import com.teamsparker.android.ui.alarmsetting.AlarmSettingViewModel.Companion.ALARM_ROOM_START +import com.teamsparker.android.ui.alarmsetting.AlarmSettingViewModel.Companion.ALARM_SPARK +import com.teamsparker.android.ui.auth.AuthActivity +import com.teamsparker.android.util.EventObserver +import com.teamsparker.android.util.Injector +import dagger.hilt.android.EntryPointAccessors abstract class BaseFragment( @LayoutRes private val layoutRes: Int @@ -26,6 +43,32 @@ abstract class BaseFragment( return binding.root } + private val sharedPreferences: SharedPreferences by lazy { + EntryPointAccessors.fromActivity( + requireActivity(), + Injector.SharedPreferencesInjector::class.java + ).sharedPreferences() + } + + fun terminationTokenHandling(viewModel: BaseViewModel) { + viewModel.moveToLogin.observe( + viewLifecycleOwner, + EventObserver { + val intent = Intent(requireActivity(), AuthActivity::class.java) + startActivity(intent) + sharedPreferences.edit { remove(ACCESS_TOKEN) } + sharedPreferences.edit { remove(USER_KAKAO_USER_ID) } + sharedPreferences.edit { remove(ALARM_LOCAL_SAVED) } + sharedPreferences.edit { remove(ALARM_ROOM_START) } + sharedPreferences.edit { remove(ALARM_SPARK) } + sharedPreferences.edit { remove(ALARM_CONSIDER) } + sharedPreferences.edit { remove(ALARM_CERTIFICATION) } + sharedPreferences.edit { remove(ALARM_REMIND) } + requireActivity().finishAffinity() + } + ) + } + override fun onDestroyView() { super.onDestroyView() _binding = null diff --git a/app/src/main/java/com/teamsparker/android/ui/base/BaseViewModel.kt b/app/src/main/java/com/teamsparker/android/ui/base/BaseViewModel.kt new file mode 100644 index 00000000..0dd69963 --- /dev/null +++ b/app/src/main/java/com/teamsparker/android/ui/base/BaseViewModel.kt @@ -0,0 +1,20 @@ +package com.teamsparker.android.ui.base + +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import com.teamsparker.android.util.Event +import kotlinx.coroutines.CoroutineExceptionHandler +import java.security.cert.CertificateException + +abstract class BaseViewModel : ViewModel() { + + private val _moveToLogin = MutableLiveData>() + val moveToLogin: LiveData> = _moveToLogin + + val exceptionHandler = CoroutineExceptionHandler { _, throwable -> + when (throwable) { + is CertificateException -> _moveToLogin.postValue(Event(true)) + } + } +} diff --git a/app/src/main/java/com/teamsparker/android/ui/habit/FlameRoadMapDialogFragment.kt b/app/src/main/java/com/teamsparker/android/ui/habit/FlameRoadMapDialogFragment.kt new file mode 100644 index 00000000..8a6c51b4 --- /dev/null +++ b/app/src/main/java/com/teamsparker/android/ui/habit/FlameRoadMapDialogFragment.kt @@ -0,0 +1,139 @@ +package com.teamsparker.android.ui.habit + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.DialogFragment +import androidx.fragment.app.activityViewModels +import androidx.viewpager2.widget.ViewPager2 +import com.teamsparker.android.R +import com.teamsparker.android.databinding.FragmentFlameRoadMapDialogBinding +import com.teamsparker.android.ui.habit.adapter.FlameRoadMapAdapter +import com.teamsparker.android.ui.habit.flameroadmap.* +import com.teamsparker.android.ui.habit.viewmodel.HabitViewModel +import dagger.hilt.android.AndroidEntryPoint +import kotlin.math.abs + +@AndroidEntryPoint +class FlameRoadMapDialogFragment : DialogFragment() { + + private val habitViewModel by activityViewModels() + private lateinit var flameRoadMapAdapter: FlameRoadMapAdapter + + private var _binding: FragmentFlameRoadMapDialogBinding? = null + private val binding + get() = _binding ?: error(getString(com.teamsparker.android.R.string.binding_error)) + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + isCancelable = true + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + // Inflate the layout for this fragment + _binding = FragmentFlameRoadMapDialogBinding.inflate(inflater, container, false) + binding.lifecycleOwner = viewLifecycleOwner + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + binding.habitViewModel = habitViewModel + initViewPagerAdapter() + setViewPagerOption() + initViewPagerPositionListener() + initCheckButtonOnclickListener() + initViewPagerPosition() + } + + override fun onStart() { + super.onStart() + setLayout() + } + + private fun setLayout() { + requireNotNull(dialog).apply { + requireNotNull(window).apply { + setLayout( + (resources.displayMetrics.widthPixels * 0.91).toInt(), + ViewGroup.LayoutParams.WRAP_CONTENT + ) + setBackgroundDrawableResource(R.drawable.shape_spark_white_fill_2_rect) + } + } + } + + private fun initViewPagerAdapter() { + val flakeList = listOf( + Level1Fragment(), + Level2Fragment(), + Level3Fragment(), + Level4Fragment(), + Level5Fragment(), + Level6Fragment() + ) + + flameRoadMapAdapter = FlameRoadMapAdapter(this) + flameRoadMapAdapter.fragments.addAll(flakeList) + binding.vpFlameRoadmap.adapter = flameRoadMapAdapter + } + + private fun setViewPagerOption() { + val pageMarginPx = resources.getDimensionPixelOffset(R.dimen.flameRoadMapPageMargin) + val pagerWidth = resources.getDimensionPixelOffset(R.dimen.flameRoadMapPagerWidth) + val screenWidth = resources.displayMetrics.widthPixels + val offsetPx = screenWidth - pageMarginPx - pagerWidth + + binding.vpFlameRoadmap.apply { + offscreenPageLimit = 3 + setPageTransformer { page, position -> + // 미리보기 살짝 보이기 + page.translationX = position * -offsetPx + // 미리보기 크기 조절 + var focusedPageDistanceRatio = 1 - abs(position) + page.scaleY = 0.7f + focusedPageDistanceRatio * 0.3f + page.scaleX = 0.7f + focusedPageDistanceRatio * 0.3f + } + } + } + + private fun initViewPagerPositionListener() { + binding.vpFlameRoadmap.registerOnPageChangeCallback(object : + ViewPager2.OnPageChangeCallback() { + override fun onPageSelected(position: Int) { + super.onPageSelected(position) + binding.level = position + 1 + } + }) + } + + private fun initCheckButtonOnclickListener() { + binding.checkButton.setOnClickListener { + dismiss() + } + } + + private fun initViewPagerPosition() { + binding.vpFlameRoadmap.currentItem + when (habitViewModel.habitInfo.value?.leftDay) { + 0 -> binding.vpFlameRoadmap.currentItem = 5 + in 1..6 -> binding.vpFlameRoadmap.currentItem = 4 + in 7..32 -> binding.vpFlameRoadmap.currentItem = 3 + in 33..58 -> binding.vpFlameRoadmap.currentItem = 2 + in 59..62 -> binding.vpFlameRoadmap.currentItem = 1 + in 63..65 -> binding.vpFlameRoadmap.currentItem = 0 + else -> throw IllegalArgumentException("leftDay 범위 에러") + } + } + + override fun onDestroyView() { + super.onDestroyView() + _binding = null + } +} diff --git a/app/src/main/java/com/teamsparker/android/ui/habit/HabitActivity.kt b/app/src/main/java/com/teamsparker/android/ui/habit/HabitActivity.kt index dd9b8922..05bb6830 100644 --- a/app/src/main/java/com/teamsparker/android/ui/habit/HabitActivity.kt +++ b/app/src/main/java/com/teamsparker/android/ui/habit/HabitActivity.kt @@ -1,6 +1,7 @@ package com.teamsparker.android.ui.habit import android.os.Bundle +import android.view.View import androidx.activity.viewModels import com.teamsparker.android.R import com.teamsparker.android.data.remote.LocalPreferences @@ -10,9 +11,13 @@ import com.teamsparker.android.ui.habit.adapter.HabitRecyclerViewAdapter import com.teamsparker.android.ui.habit.userguide.UserGuideFragmentDialog import com.teamsparker.android.ui.habit.viewmodel.HabitViewModel import com.teamsparker.android.util.FirebaseLogUtil +import com.teamsparker.android.util.FirebaseLogUtil.CLICK_TIMELINE_NEW_HABIT_ROOM +import com.teamsparker.android.util.FirebaseLogUtil.CLICK_TIMELINE_NONE_HABIT_ROOM import com.teamsparker.android.util.FirebaseLogUtil.SCREEN_HABIT_ROOM +import com.teamsparker.android.util.ext.setOnSingleClickListener import com.teamsparker.android.util.initStatusBarColor import dagger.hilt.android.AndroidEntryPoint +import java.io.Serializable import kotlin.properties.Delegates @AndroidEntryPoint @@ -38,6 +43,7 @@ class HabitActivity : BaseActivity(R.layout.activity_habit initHabitMoreBtnClickListener() initHabitTodayBtnClickListener() checkUserGuideDialog() + initGroupTeamLifeClickListener() } private fun initRoomId() { @@ -48,7 +54,16 @@ class HabitActivity : BaseActivity(R.layout.activity_habit habitViewModel.habitInfo.observe(this) { habitRecyclerViewAdapter.response = it binding.habitViewModel = habitViewModel - initHabitLifeLessDialog() + if (habitViewModel.habitInfo.value?.isTermNew + ?: throw IllegalStateException("isTermNew 값 null로 옴") + ) { + FlameRoadMapDialogFragment().show( + supportFragmentManager, + "FlameRoadMapDialogFragment" + ) + } +// 1.1.0에서 삭제 다른기능으로 대체 +// initHabitLifeLessDialog() } } @@ -69,7 +84,7 @@ class HabitActivity : BaseActivity(R.layout.activity_habit toastMessage = toastMessage.chunked(8)[0] + "..." } habitViewModel.initExitSuccess(false) - LocalPreferences.setExitHabitRoomHomeToastMessage("‘${toastMessage}’ 방을 나갔어요.") + LocalPreferences.setExitHabitRoomHomeToastMessage("‘$toastMessage’ 방을 나갔어요.") LocalPreferences.setExitHabitRoomHomeToastMessageState(true) finish() } @@ -134,12 +149,37 @@ class HabitActivity : BaseActivity(R.layout.activity_habit } } - private fun initHabitLifeLessDialog() { - val lifeDeductionCount = habitViewModel.habitInfo.value?.lifeDeductionCount ?: 0 - if (lifeDeductionCount != 0) { - HabitLifeLessDialogFragment(lifeDeductionCount).show( - supportFragmentManager, "LifeLessDialog" - ) +// 1.1.0 에서 삭제 다른기능으로 대체됨 +// private fun initHabitLifeLessDialog() { +// val lifeDeductionCount = habitViewModel.habitInfo.value?.lifeDeductionCount ?: 0 +// if (lifeDeductionCount != 0) { +// HabitLifeLessDialogFragment(lifeDeductionCount).show( +// supportFragmentManager, +// "LifeLessDialog" +// ) +// } +// } + + private fun initGroupTeamLifeClickListener() { + val lifeList = listOf( + binding.ivHabitTeamlifeFirst, + binding.ivHabitTeamlifeSecond, + binding.ivHabitTeamlifeThird + ) + + lifeList.forEach { + it.setOnSingleClickListener { + HabitTimeLineBottomSheet().apply { + arguments = Bundle().apply { + putSerializable(REFRESH_DATA, { refreshData() } as Serializable) + } + show(supportFragmentManager, this.javaClass.name) + } + if (habitViewModel.habitInfo.value?.isTimelineNew + ?: throw IllegalStateException("타임라인 클릭리스너 GA로그 관련오류") + ) FirebaseLogUtil.logClickEvent(CLICK_TIMELINE_NEW_HABIT_ROOM) + else FirebaseLogUtil.logClickEvent(CLICK_TIMELINE_NONE_HABIT_ROOM) + } } } @@ -152,4 +192,8 @@ class HabitActivity : BaseActivity(R.layout.activity_habit super.onPause() overridePendingTransition(0, 0) } + + companion object { + const val REFRESH_DATA = "REFRESH_DATA" + } } diff --git a/app/src/main/java/com/teamsparker/android/ui/habit/HabitMoreBottomSheet.kt b/app/src/main/java/com/teamsparker/android/ui/habit/HabitMoreBottomSheet.kt index dd63d455..d7a93545 100644 --- a/app/src/main/java/com/teamsparker/android/ui/habit/HabitMoreBottomSheet.kt +++ b/app/src/main/java/com/teamsparker/android/ui/habit/HabitMoreBottomSheet.kt @@ -13,6 +13,7 @@ import com.teamsparker.android.databinding.BottomSheetHabitMoreBinding import com.teamsparker.android.ui.habit.userguide.UserGuideFragmentDialog import com.teamsparker.android.ui.habit.viewmodel.HabitViewModel import com.teamsparker.android.util.DialogEditTextUtil +import com.teamsparker.android.util.ext.setOnSingleClickListener class HabitMoreBottomSheet : BottomSheetDialogFragment() { private var _binding: BottomSheetHabitMoreBinding? = null @@ -22,7 +23,7 @@ class HabitMoreBottomSheet : BottomSheetDialogFragment() { override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle?, + savedInstanceState: Bundle? ): View? { _binding = BottomSheetHabitMoreBinding.inflate(inflater, container, false) return binding.root @@ -38,6 +39,7 @@ class HabitMoreBottomSheet : BottomSheetDialogFragment() { initGoalBtnClickListener() initExitBtnClickListener() initUserGuideBtnClickListener() + initFlameRoadMapBtnClickListener() } private fun initGoalBtnClickListener() { @@ -83,6 +85,13 @@ class HabitMoreBottomSheet : BottomSheetDialogFragment() { } } + private fun initFlameRoadMapBtnClickListener() { + binding.tvHabitMoreFlameRoadmap.setOnSingleClickListener { + FlameRoadMapDialogFragment().show(requireActivity().supportFragmentManager, "FlameRoadMapDialogFragment") + dismiss() + } + } + override fun onDestroyView() { super.onDestroyView() _binding = null diff --git a/app/src/main/java/com/teamsparker/android/ui/habit/HabitTimeLineBottomSheet.kt b/app/src/main/java/com/teamsparker/android/ui/habit/HabitTimeLineBottomSheet.kt new file mode 100644 index 00000000..6547486b --- /dev/null +++ b/app/src/main/java/com/teamsparker/android/ui/habit/HabitTimeLineBottomSheet.kt @@ -0,0 +1,102 @@ +package com.teamsparker.android.ui.habit + +import android.app.Dialog +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.activityViewModels +import com.google.android.material.bottomsheet.BottomSheetBehavior +import com.google.android.material.bottomsheet.BottomSheetDialog +import com.google.android.material.bottomsheet.BottomSheetDialogFragment +import com.teamsparker.android.R +import com.teamsparker.android.databinding.BottomSheetHabitTimeLineBinding +import com.teamsparker.android.ui.habit.HabitActivity.Companion.REFRESH_DATA +import com.teamsparker.android.ui.habit.adapter.HabitTimeLineRecyclerViewAdapter +import com.teamsparker.android.ui.habit.viewmodel.HabitViewModel +import dagger.hilt.android.AndroidEntryPoint + +@AndroidEntryPoint +class HabitTimeLineBottomSheet : BottomSheetDialogFragment() { + + private var _binding: BottomSheetHabitTimeLineBinding? = null + val binding get() = _binding ?: error(getString(R.string.binding_error)) + + private val habitViewModel by activityViewModels() + private lateinit var habitTimeLineRecyclerViewAdapter: HabitTimeLineRecyclerViewAdapter + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + refreshDataOnHabitActivity() + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + _binding = BottomSheetHabitTimeLineBinding.inflate(inflater, container, false) + binding.lifecycleOwner = viewLifecycleOwner + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + initTimeLineData() + initHabitTimeLineRecyclerViewAdapter() + updateRecyclerViewList() + } + + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + val dialog = super.onCreateDialog(savedInstanceState) + dialog.setOnShowListener { dialogInterface -> + val bottomSheetDialog = dialogInterface as BottomSheetDialog + setupRatio(bottomSheetDialog) + } + return dialog + } + + private fun refreshDataOnHabitActivity() { + val refreshDataOnHabitActivity = arguments?.getSerializable(REFRESH_DATA) as () -> Unit + refreshDataOnHabitActivity() + } + + // 다이얼로그 높이 비율로 조정 코드 + private fun setupRatio(bottomSheetDialog: BottomSheetDialog) { + val bottomSheet = + bottomSheetDialog.findViewById(com.google.android.material.R.id.design_bottom_sheet) as View + val behavior = BottomSheetBehavior.from(bottomSheet) + val layoutParams = bottomSheet!!.layoutParams + layoutParams.height = getBottomSheetDialogDefaultHeight() + bottomSheet.layoutParams = layoutParams + behavior.state = BottomSheetBehavior.STATE_EXPANDED + } + + private fun getBottomSheetDialogDefaultHeight(): Int { + return getWindowHeight() * 449 / 736 + // 기기 높이 대비 비율 설정 부분!! + } + + private fun getWindowHeight(): Int = resources.displayMetrics.heightPixels + + private fun initTimeLineData() { + habitViewModel.getHabitRoomTimeLine() + } + + private fun initHabitTimeLineRecyclerViewAdapter() { + habitTimeLineRecyclerViewAdapter = HabitTimeLineRecyclerViewAdapter() + binding.rvTimeLine.adapter = habitTimeLineRecyclerViewAdapter + } + + private fun updateRecyclerViewList() { + habitViewModel.timeLineList.observe(viewLifecycleOwner) { + habitTimeLineRecyclerViewAdapter.submitList(it.timelines) + } + } + + override fun onDestroyView() { + super.onDestroyView() + _binding = null + } +} diff --git a/app/src/main/java/com/teamsparker/android/ui/habit/adapter/FlameRoadMapAdapter.kt b/app/src/main/java/com/teamsparker/android/ui/habit/adapter/FlameRoadMapAdapter.kt new file mode 100644 index 00000000..fb2bdbb4 --- /dev/null +++ b/app/src/main/java/com/teamsparker/android/ui/habit/adapter/FlameRoadMapAdapter.kt @@ -0,0 +1,12 @@ +package com.teamsparker.android.ui.habit.adapter + +import androidx.fragment.app.Fragment +import androidx.viewpager2.adapter.FragmentStateAdapter + +class FlameRoadMapAdapter(fragment: Fragment) : FragmentStateAdapter(fragment) { + val fragments = mutableListOf() + + override fun getItemCount(): Int = fragments.size + + override fun createFragment(position: Int): Fragment = fragments[position] +} diff --git a/app/src/main/java/com/teamsparker/android/ui/habit/adapter/HabitTimeLineRecyclerViewAdapter.kt b/app/src/main/java/com/teamsparker/android/ui/habit/adapter/HabitTimeLineRecyclerViewAdapter.kt new file mode 100644 index 00000000..682060dc --- /dev/null +++ b/app/src/main/java/com/teamsparker/android/ui/habit/adapter/HabitTimeLineRecyclerViewAdapter.kt @@ -0,0 +1,44 @@ +package com.teamsparker.android.ui.habit.adapter + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.ListAdapter +import androidx.recyclerview.widget.RecyclerView +import com.teamsparker.android.data.remote.entity.response.Timeline +import com.teamsparker.android.databinding.ItemHabitTimeLineBinding +import com.teamsparker.android.util.ItemDiffCallback + +class HabitTimeLineRecyclerViewAdapter() : + ListAdapter( + ItemDiffCallback( + onContentsTheSame = { old, new -> old == new }, + onItemsTheSame = { old, new -> old.title == new.title } + ) + ) { + + private lateinit var inflater: LayoutInflater + + override fun onCreateViewHolder( + parent: ViewGroup, + viewType: Int + ): HabitTImeLineRecyclerViewHolder { + if (!::inflater.isInitialized) { + inflater = LayoutInflater.from(parent.context) + } + + val binding = ItemHabitTimeLineBinding.inflate(inflater, parent, false) + + return HabitTImeLineRecyclerViewHolder(binding) + } + + override fun onBindViewHolder(holder: HabitTImeLineRecyclerViewHolder, position: Int) { + holder.onBInd(getItem(position)) + } + + class HabitTImeLineRecyclerViewHolder(private val binding: ItemHabitTimeLineBinding) : + RecyclerView.ViewHolder(binding.root) { + fun onBInd(data: Timeline) { + binding.timeLine = data + } + } +} diff --git a/app/src/main/java/com/teamsparker/android/ui/habit/flameroadmap/Level1Fragment.kt b/app/src/main/java/com/teamsparker/android/ui/habit/flameroadmap/Level1Fragment.kt new file mode 100644 index 00000000..777d7d70 --- /dev/null +++ b/app/src/main/java/com/teamsparker/android/ui/habit/flameroadmap/Level1Fragment.kt @@ -0,0 +1,9 @@ +package com.teamsparker.android.ui.habit.flameroadmap + +import androidx.fragment.app.activityViewModels +import com.teamsparker.android.R +import com.teamsparker.android.databinding.FragmentLevel1Binding +import com.teamsparker.android.ui.base.BaseFragment +import com.teamsparker.android.ui.habit.viewmodel.HabitViewModel + +class Level1Fragment : BaseFragment(R.layout.fragment_level1) diff --git a/app/src/main/java/com/teamsparker/android/ui/habit/flameroadmap/Level2Fragment.kt b/app/src/main/java/com/teamsparker/android/ui/habit/flameroadmap/Level2Fragment.kt new file mode 100644 index 00000000..c86766b2 --- /dev/null +++ b/app/src/main/java/com/teamsparker/android/ui/habit/flameroadmap/Level2Fragment.kt @@ -0,0 +1,19 @@ +package com.teamsparker.android.ui.habit.flameroadmap + +import android.os.Bundle +import android.view.View +import androidx.fragment.app.activityViewModels +import com.teamsparker.android.R +import com.teamsparker.android.databinding.FragmentLevel2Binding +import com.teamsparker.android.ui.base.BaseFragment +import com.teamsparker.android.ui.habit.viewmodel.HabitViewModel + +class Level2Fragment : BaseFragment(R.layout.fragment_level2){ + + private val habitViewModel by activityViewModels() + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + binding.habitViewMdoel = habitViewModel + } +} diff --git a/app/src/main/java/com/teamsparker/android/ui/habit/flameroadmap/Level3Fragment.kt b/app/src/main/java/com/teamsparker/android/ui/habit/flameroadmap/Level3Fragment.kt new file mode 100644 index 00000000..6b8bd234 --- /dev/null +++ b/app/src/main/java/com/teamsparker/android/ui/habit/flameroadmap/Level3Fragment.kt @@ -0,0 +1,19 @@ +package com.teamsparker.android.ui.habit.flameroadmap + +import android.os.Bundle +import android.view.View +import androidx.fragment.app.activityViewModels +import com.teamsparker.android.R +import com.teamsparker.android.databinding.FragmentLevel3Binding +import com.teamsparker.android.ui.base.BaseFragment +import com.teamsparker.android.ui.habit.viewmodel.HabitViewModel + +class Level3Fragment : BaseFragment(R.layout.fragment_level3){ + + private val habitViewModel by activityViewModels() + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + binding.habitViewMdoel = habitViewModel + } +} diff --git a/app/src/main/java/com/teamsparker/android/ui/habit/flameroadmap/Level4Fragment.kt b/app/src/main/java/com/teamsparker/android/ui/habit/flameroadmap/Level4Fragment.kt new file mode 100644 index 00000000..d55d0ab4 --- /dev/null +++ b/app/src/main/java/com/teamsparker/android/ui/habit/flameroadmap/Level4Fragment.kt @@ -0,0 +1,19 @@ +package com.teamsparker.android.ui.habit.flameroadmap + +import android.os.Bundle +import android.view.View +import androidx.fragment.app.activityViewModels +import com.teamsparker.android.R +import com.teamsparker.android.databinding.FragmentLevel4Binding +import com.teamsparker.android.ui.base.BaseFragment +import com.teamsparker.android.ui.habit.viewmodel.HabitViewModel + +class Level4Fragment : BaseFragment(R.layout.fragment_level4){ + + private val habitViewModel by activityViewModels() + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + binding.habitViewMdoel = habitViewModel + } +} diff --git a/app/src/main/java/com/teamsparker/android/ui/habit/flameroadmap/Level5Fragment.kt b/app/src/main/java/com/teamsparker/android/ui/habit/flameroadmap/Level5Fragment.kt new file mode 100644 index 00000000..e79bb9c4 --- /dev/null +++ b/app/src/main/java/com/teamsparker/android/ui/habit/flameroadmap/Level5Fragment.kt @@ -0,0 +1,19 @@ +package com.teamsparker.android.ui.habit.flameroadmap + +import android.os.Bundle +import android.view.View +import androidx.fragment.app.activityViewModels +import com.teamsparker.android.R +import com.teamsparker.android.databinding.FragmentLevel5Binding +import com.teamsparker.android.ui.base.BaseFragment +import com.teamsparker.android.ui.habit.viewmodel.HabitViewModel + +class Level5Fragment : BaseFragment(R.layout.fragment_level5){ + + private val habitViewModel by activityViewModels() + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + binding.habitViewMdoel = habitViewModel + } +} diff --git a/app/src/main/java/com/teamsparker/android/ui/habit/flameroadmap/Level6Fragment.kt b/app/src/main/java/com/teamsparker/android/ui/habit/flameroadmap/Level6Fragment.kt new file mode 100644 index 00000000..fca59d2e --- /dev/null +++ b/app/src/main/java/com/teamsparker/android/ui/habit/flameroadmap/Level6Fragment.kt @@ -0,0 +1,19 @@ +package com.teamsparker.android.ui.habit.flameroadmap + +import android.os.Bundle +import android.view.View +import androidx.fragment.app.activityViewModels +import com.teamsparker.android.R +import com.teamsparker.android.databinding.FragmentLevel6Binding +import com.teamsparker.android.ui.base.BaseFragment +import com.teamsparker.android.ui.habit.viewmodel.HabitViewModel + +class Level6Fragment : BaseFragment(R.layout.fragment_level6){ + + private val habitViewModel by activityViewModels() + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + binding.habitViewMdoel = habitViewModel + } +} diff --git a/app/src/main/java/com/teamsparker/android/ui/habit/viewmodel/HabitViewModel.kt b/app/src/main/java/com/teamsparker/android/ui/habit/viewmodel/HabitViewModel.kt index 4f97ffae..c403cebc 100644 --- a/app/src/main/java/com/teamsparker/android/ui/habit/viewmodel/HabitViewModel.kt +++ b/app/src/main/java/com/teamsparker/android/ui/habit/viewmodel/HabitViewModel.kt @@ -9,6 +9,7 @@ import com.teamsparker.android.data.remote.entity.request.SendSparkRequest import com.teamsparker.android.data.remote.entity.request.SetStatusRequest import com.teamsparker.android.data.remote.entity.response.HabitRecord import com.teamsparker.android.data.remote.entity.response.HabitResponse +import com.teamsparker.android.data.remote.entity.response.HabitRoomTimeLine import com.teamsparker.android.data.remote.repository.HabitRepository import com.teamsparker.android.data.remote.service.HabitService import com.teamsparker.android.data.remote.service.LeaveRoomService @@ -16,11 +17,12 @@ import com.teamsparker.android.data.remote.service.SendSparkService import com.teamsparker.android.data.remote.service.SetStatusService import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.launch +import timber.log.Timber import javax.inject.Inject @HiltViewModel class HabitViewModel @Inject constructor( - private val habitRepository: HabitRepository, + private val habitRepository: HabitRepository ) : ViewModel() { private val habitService: HabitService = RetrofitBuilder.habitService private val setStatusService: SetStatusService = RetrofitBuilder.setStatusService @@ -45,6 +47,9 @@ class HabitViewModel @Inject constructor( private val _exitSuccess = MutableLiveData() val exitSuccess: LiveData = _exitSuccess + private var _timeLineList = MutableLiveData() + val timeLineList: LiveData = _timeLineList + private fun initIsLoading(isLoading: Boolean) { _isLoading.value = isLoading } @@ -70,18 +75,22 @@ class HabitViewModel @Inject constructor( val data = response.data ?: throw NullPointerException("습관방 통신 에러") _habitInfo.postValue(data) - _habitRecordList.postValue(mutableListOf().apply { - add(data.myRecord) - addAll(data.otherRecords.map { - HabitRecord( - nickname = it.nickname, - profileImg = it.profileImg, - recordId = it.recordId, - status = it.status, - userId = it.userId + _habitRecordList.postValue( + mutableListOf().apply { + add(data.myRecord) + addAll( + data.otherRecords.map { + HabitRecord( + nickname = it.nickname, + profileImg = it.profileImg, + recordId = it.recordId, + status = it.status, + userId = it.userId + ) + } ) - }) - }) + } + ) initIsLoading(false) }.onFailure { } } @@ -109,7 +118,6 @@ class HabitViewModel @Inject constructor( SendSparkRequest(content, recordId) ) }.onSuccess { _sendSuccess.value = true } - } } } @@ -127,5 +135,18 @@ class HabitViewModel @Inject constructor( fun setUserGuideDialogState(state: Boolean) { habitRepository.setHabitUserGuideState(state) } -} + fun getHabitRoomTimeLine() { + viewModelScope.launch { + habitRepository.getHabitRoomTimeLine( + habitInfo.value?.roomId + ?: throw IllegalStateException("getHabitRoomTimeLine in viewModel") + ) + .onSuccess { + _timeLineList.value = it + }.onFailure { + Timber.d(it) + } + } + } +} diff --git a/app/src/main/java/com/teamsparker/android/util/BindingAdapters.kt b/app/src/main/java/com/teamsparker/android/util/BindingAdapters.kt index 3bdc8683..5cd02443 100644 --- a/app/src/main/java/com/teamsparker/android/util/BindingAdapters.kt +++ b/app/src/main/java/com/teamsparker/android/util/BindingAdapters.kt @@ -4,6 +4,7 @@ import android.content.res.ColorStateList import android.net.Uri import android.text.InputFilter import android.view.View +import android.view.ViewGroup import android.widget.* import androidx.core.content.ContextCompat import androidx.databinding.BindingAdapter @@ -12,7 +13,7 @@ import com.airbnb.lottie.LottieAnimationView import com.bumptech.glide.Glide import com.google.android.material.floatingactionbutton.FloatingActionButton import com.teamsparker.android.R -import java.lang.IllegalArgumentException +import kotlin.math.roundToInt object BindingAdapters { @JvmStatic @@ -60,11 +61,9 @@ object BindingAdapters { } } - @JvmStatic @BindingAdapter("setLeftBackground") fun setLeftBackground(imageview: ImageView, leftDay: Int?) { - if (leftDay != null) { imageview.setImageResource( when { @@ -81,7 +80,6 @@ object BindingAdapters { } } - @JvmStatic @BindingAdapter("setLeftTicketColor") fun setLeftTicketColor(textView: TextView, leftDay: Int?) { @@ -170,7 +168,6 @@ object BindingAdapters { @JvmStatic @BindingAdapter("setLeftTicketComment") fun setLeftTicketComment(textview: TextView, leftDay: Int?) { - val context = textview.context if (leftDay != null) { textview.text = when { @@ -328,7 +325,7 @@ object BindingAdapters { imageButton: ImageButton, status: String?, habitRestCount: Int?, - habitUserLeftDay: Int?, + habitUserLeftDay: Int? ) { if (status != null) { if (habitRestCount != -1) { @@ -351,18 +348,17 @@ object BindingAdapters { } ) imageButton.isEnabled = ( - when (status) { - "DONE", "REST" -> false - "NONE", "CONSIDER" -> true - else -> throw IllegalStateException("bindingAdapter setSendSparkImg error") - } - ) + when (status) { + "DONE", "REST" -> false + "NONE", "CONSIDER" -> true + else -> throw IllegalStateException("bindingAdapter setSendSparkImg error") + } + ) } } } } - @JvmStatic @BindingAdapter(value = ["certificationLeftDay", "certificationStatus"], requireAll = true) fun setHabitCertificationButton(button: Button, leftDay: Int?, status: String?) { @@ -379,12 +375,12 @@ object BindingAdapters { } ) button.isEnabled = ( - when (status) { - "DONE", "REST" -> false - "NONE", "CONSIDER" -> true - else -> throw IllegalStateException("bindingAdapter setHabitCertificationButton error") - } - ) + when (status) { + "DONE", "REST" -> false + "NONE", "CONSIDER" -> true + else -> throw IllegalStateException("bindingAdapter setHabitCertificationButton error") + } + ) } } } @@ -397,13 +393,12 @@ object BindingAdapters { imageview.visibility = View.GONE } else { imageview.visibility = ( - when (status) { - "DONE", "REST" -> View.GONE - "NONE", "CONSIDER" -> View.VISIBLE - else -> throw IllegalStateException("bindingAdapter setHabitCertificationVisibility error") - } - ) - + when (status) { + "DONE", "REST" -> View.GONE + "NONE", "CONSIDER" -> View.VISIBLE + else -> throw IllegalStateException("bindingAdapter setHabitCertificationVisibility error") + } + ) } } } @@ -411,7 +406,6 @@ object BindingAdapters { @JvmStatic @BindingAdapter("setCardOutLineColor") fun setCardOutLineColor(view: View, leftDay: Int?) { - if (leftDay != null) { view.setBackgroundResource( when { @@ -431,7 +425,6 @@ object BindingAdapters { @JvmStatic @BindingAdapter("setCardInLineColor") fun setCardInLineColor(view: View, leftDay: Int?) { - if (leftDay != null) { view.setBackgroundResource( when { @@ -451,7 +444,6 @@ object BindingAdapters { @JvmStatic @BindingAdapter("setProgressingCardSparkFlake") fun setProgressingCardSparkFlake(imageview: ImageView, leftDay: Int?) { - if (leftDay != null) { imageview.setBackgroundResource( when { @@ -471,7 +463,6 @@ object BindingAdapters { @JvmStatic @BindingAdapter("setIncompleteCardSparkFlake") fun setIncompleteCardSparkFlake(imageview: ImageView, failDay: Int?) { - if (failDay != null) { imageview.setBackgroundResource( when { @@ -515,7 +506,6 @@ object BindingAdapters { .into(this) } "CONSIDER" -> { - } } } @@ -593,11 +583,11 @@ object BindingAdapters { @BindingAdapter("setIncompleteCardFailDay") fun TextView.setIncompleteCardFailDay(failDay: Int) { if (failDay == 1) { - this.text = "${failDay} Day" + this.text = "$failDay Day" } else if (failDay == 6) { this.text = "D-day" } else { - this.text = "${failDay} Days" + this.text = "$failDay Days" } } @@ -653,4 +643,80 @@ object BindingAdapters { } editText.filters = arrayOf(InputFilter.LengthFilter(max)) } + + @JvmStatic + @BindingAdapter("TimeLineProfileState", "ProfileVisibleMargin", "ProfileGoneMargin") + fun TextView.timeLimeProfileMargin( + timeLineProfileState: Boolean, + profileVisibleMargin: Int, + profileGoneMargin: Int + ) { + if (timeLineProfileState) { + val tempMargin = (profileVisibleMargin * resources.displayMetrics.density).roundToInt() + val layoutParams = this.layoutParams as ViewGroup.MarginLayoutParams + layoutParams.topMargin = tempMargin + this.layoutParams = layoutParams + } else { + val tempMargin = (profileGoneMargin * resources.displayMetrics.density).roundToInt() + val layoutParams = this.layoutParams as ViewGroup.MarginLayoutParams + layoutParams.topMargin = tempMargin + this.layoutParams = layoutParams + } + } + + @JvmStatic + @BindingAdapter("setFlameRoadMapTitle") + fun setFlameRoadMapTitle(textview: TextView, level: Int?) { + when (level) { + 1 -> textview.setText(R.string.flame_road_map_title_level_1) + 2 -> textview.setText(R.string.flame_road_map_title_level_2) + 3 -> textview.setText(R.string.flame_road_map_title_level_3) + 4 -> textview.setText(R.string.flame_road_map_title_level_4) + 5 -> textview.setText(R.string.flame_road_map_title_level_5) + 6 -> textview.setText(R.string.flame_road_map_title_level_6) + } + } + + @JvmStatic + @BindingAdapter("setFlameRoadMapContnentDay", "setFlameRoadMapContnentLevel") + fun setFlameRoadMapContent(textview: TextView, leftDay: Int, level: Int?) { + when (level) { + 1 -> textview.setText(R.string.flame_road_map_content_level_1) + 2 -> { + if (leftDay <= 62) { + textview.setText(R.string.flame_road_map_content_level_2) + } else { + textview.setText(R.string.flame_road_map_content_not_ready_level_2) + } + } + 3 -> { + if (leftDay <= 58) { + textview.setText(R.string.flame_road_map_content_level_3) + } else { + textview.setText(R.string.flame_road_map_content_not_ready_level_3) + } + } + 4 -> { + if (leftDay <= 32) { + textview.setText(R.string.flame_road_map_content_level_4) + } else { + textview.setText(R.string.flame_road_map_content_not_ready_level_4) + } + } + 5 -> { + if (leftDay <= 6) { + textview.setText(R.string.flame_road_map_content_level_5) + } else { + textview.setText(R.string.flame_road_map_content_not_ready_level_5) + } + } + 6 -> { + if (leftDay == 0) { + textview.setText(R.string.flame_road_map_content_level_6) + } else { + textview.setText(R.string.flame_road_map_content_not_ready_level_6) + } + } + } + } } diff --git a/app/src/main/java/com/teamsparker/android/util/FirebaseLogUtil.kt b/app/src/main/java/com/teamsparker/android/util/FirebaseLogUtil.kt index 0a9b82c5..44e57a92 100644 --- a/app/src/main/java/com/teamsparker/android/util/FirebaseLogUtil.kt +++ b/app/src/main/java/com/teamsparker/android/util/FirebaseLogUtil.kt @@ -32,10 +32,11 @@ object FirebaseLogUtil { const val CLICK_CONSIDER_HABIT_ROOM = "click_CONSIDER_habit_room" const val CLICK_HEART_FEED = "click_HEART_feed" const val CLICK_CARD_MY_ROOM = "click_CARD_my_room" + const val CLICK_TIMELINE_NEW_HABIT_ROOM = "click_TIMELINE_NEW_habit_room" + const val CLICK_TIMELINE_NONE_HABIT_ROOM = "click_TIMELINE_NONE_habit_room" private const val NOTIFICATION_OPEN = "notification_open_" - fun logScreenEvent(screenClass: String, screenName: String) { Firebase.analytics.logEvent(FirebaseAnalytics.Event.SCREEN_VIEW) { param(FirebaseAnalytics.Param.SCREEN_CLASS, screenClass) diff --git a/app/src/main/java/com/teamsparker/android/util/Injector.kt b/app/src/main/java/com/teamsparker/android/util/Injector.kt new file mode 100644 index 00000000..bb5d1ba0 --- /dev/null +++ b/app/src/main/java/com/teamsparker/android/util/Injector.kt @@ -0,0 +1,15 @@ +package com.teamsparker.android.util + +import android.content.SharedPreferences +import dagger.hilt.EntryPoint +import dagger.hilt.InstallIn +import dagger.hilt.android.components.ActivityComponent + +sealed interface Injector { + + @EntryPoint + @InstallIn(ActivityComponent::class) + interface SharedPreferencesInjector { + fun sharedPreferences(): SharedPreferences + } +} diff --git a/app/src/main/java/com/teamsparker/android/util/ItemDiffCallback.kt b/app/src/main/java/com/teamsparker/android/util/ItemDiffCallback.kt new file mode 100644 index 00000000..5f66791e --- /dev/null +++ b/app/src/main/java/com/teamsparker/android/util/ItemDiffCallback.kt @@ -0,0 +1,12 @@ +package com.teamsparker.android.util + +import androidx.recyclerview.widget.DiffUtil + +class ItemDiffCallback( + val onItemsTheSame: (T, T) -> Boolean, + val onContentsTheSame: (T, T) -> Boolean +) : DiffUtil.ItemCallback() { + override fun areItemsTheSame(oldItem: T, newItem: T): Boolean = onItemsTheSame(oldItem, newItem) + override fun areContentsTheSame(oldItem: T, newItem: T): Boolean = + onContentsTheSame(oldItem, newItem) +} diff --git a/app/src/main/java/com/teamsparker/android/util/ext/ViewExtension.kt b/app/src/main/java/com/teamsparker/android/util/ext/ViewExtension.kt new file mode 100644 index 00000000..2a3b2582 --- /dev/null +++ b/app/src/main/java/com/teamsparker/android/util/ext/ViewExtension.kt @@ -0,0 +1,17 @@ +package com.teamsparker.android.util.ext + +import android.view.View + +inline fun View.setOnSingleClickListener( + delay: Long = 500L, + crossinline block: (View) -> Unit +) { + var previousClickedTime = 0L + setOnClickListener { view -> + val clickedTime = System.currentTimeMillis() + if (clickedTime - previousClickedTime >= delay) { + block(view) + previousClickedTime = clickedTime + } + } +} diff --git a/app/src/main/res/drawable-hdpi/bg_flame_road_map_gradation_left.png b/app/src/main/res/drawable-hdpi/bg_flame_road_map_gradation_left.png new file mode 100644 index 00000000..90599f75 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/bg_flame_road_map_gradation_left.png differ diff --git a/app/src/main/res/drawable-hdpi/bg_flame_road_map_gradation_right.png b/app/src/main/res/drawable-hdpi/bg_flame_road_map_gradation_right.png new file mode 100644 index 00000000..3ddaa7f2 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/bg_flame_road_map_gradation_right.png differ diff --git a/app/src/main/res/drawable-hdpi/ic_life_red_dot.png b/app/src/main/res/drawable-hdpi/ic_life_red_dot.png new file mode 100644 index 00000000..a745159e Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_life_red_dot.png differ diff --git a/app/src/main/res/drawable-hdpi/ic_time_line_blank_spark_flake_2.png b/app/src/main/res/drawable-hdpi/ic_time_line_blank_spark_flake_2.png new file mode 100644 index 00000000..b210a0ae Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_time_line_blank_spark_flake_2.png differ diff --git a/app/src/main/res/drawable-hdpi/ic_time_line_blank_spark_flake_3.png b/app/src/main/res/drawable-hdpi/ic_time_line_blank_spark_flake_3.png new file mode 100644 index 00000000..1292fffa Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_time_line_blank_spark_flake_3.png differ diff --git a/app/src/main/res/drawable-hdpi/ic_time_line_blank_spark_flake_4.png b/app/src/main/res/drawable-hdpi/ic_time_line_blank_spark_flake_4.png new file mode 100644 index 00000000..1db18c86 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_time_line_blank_spark_flake_4.png differ diff --git a/app/src/main/res/drawable-hdpi/ic_time_line_blank_spark_flake_5.png b/app/src/main/res/drawable-hdpi/ic_time_line_blank_spark_flake_5.png new file mode 100644 index 00000000..d947d0a4 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_time_line_blank_spark_flake_5.png differ diff --git a/app/src/main/res/drawable-hdpi/ic_time_line_blank_spark_flake_6.png b/app/src/main/res/drawable-hdpi/ic_time_line_blank_spark_flake_6.png new file mode 100644 index 00000000..23f0c847 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_time_line_blank_spark_flake_6.png differ diff --git a/app/src/main/res/drawable-hdpi/ic_time_line_spark_flake_1.png b/app/src/main/res/drawable-hdpi/ic_time_line_spark_flake_1.png new file mode 100644 index 00000000..90dc816b Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_time_line_spark_flake_1.png differ diff --git a/app/src/main/res/drawable-hdpi/ic_time_line_spark_flake_2.png b/app/src/main/res/drawable-hdpi/ic_time_line_spark_flake_2.png new file mode 100644 index 00000000..a6951118 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_time_line_spark_flake_2.png differ diff --git a/app/src/main/res/drawable-hdpi/ic_time_line_spark_flake_3.png b/app/src/main/res/drawable-hdpi/ic_time_line_spark_flake_3.png new file mode 100644 index 00000000..f87c5e49 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_time_line_spark_flake_3.png differ diff --git a/app/src/main/res/drawable-hdpi/ic_time_line_spark_flake_4.png b/app/src/main/res/drawable-hdpi/ic_time_line_spark_flake_4.png new file mode 100644 index 00000000..97392f5e Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_time_line_spark_flake_4.png differ diff --git a/app/src/main/res/drawable-hdpi/ic_time_line_spark_flake_5.png b/app/src/main/res/drawable-hdpi/ic_time_line_spark_flake_5.png new file mode 100644 index 00000000..d343632f Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_time_line_spark_flake_5.png differ diff --git a/app/src/main/res/drawable-hdpi/ic_time_line_spark_flake_6.png b/app/src/main/res/drawable-hdpi/ic_time_line_spark_flake_6.png new file mode 100644 index 00000000..f57d48ec Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_time_line_spark_flake_6.png differ diff --git a/app/src/main/res/drawable-mdpi/bg_flame_road_map_gradation_left.png b/app/src/main/res/drawable-mdpi/bg_flame_road_map_gradation_left.png new file mode 100644 index 00000000..a80b2314 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/bg_flame_road_map_gradation_left.png differ diff --git a/app/src/main/res/drawable-mdpi/bg_flame_road_map_gradation_right.png b/app/src/main/res/drawable-mdpi/bg_flame_road_map_gradation_right.png new file mode 100644 index 00000000..b4634351 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/bg_flame_road_map_gradation_right.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_life_red_dot.png b/app/src/main/res/drawable-mdpi/ic_life_red_dot.png new file mode 100644 index 00000000..574adce2 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_life_red_dot.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_time_line_blank_spark_flake_2.png b/app/src/main/res/drawable-mdpi/ic_time_line_blank_spark_flake_2.png new file mode 100644 index 00000000..6e0d532c Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_time_line_blank_spark_flake_2.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_time_line_blank_spark_flake_3.png b/app/src/main/res/drawable-mdpi/ic_time_line_blank_spark_flake_3.png new file mode 100644 index 00000000..4921ad8b Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_time_line_blank_spark_flake_3.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_time_line_blank_spark_flake_4.png b/app/src/main/res/drawable-mdpi/ic_time_line_blank_spark_flake_4.png new file mode 100644 index 00000000..41855fe4 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_time_line_blank_spark_flake_4.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_time_line_blank_spark_flake_5.png b/app/src/main/res/drawable-mdpi/ic_time_line_blank_spark_flake_5.png new file mode 100644 index 00000000..66499607 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_time_line_blank_spark_flake_5.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_time_line_blank_spark_flake_6.png b/app/src/main/res/drawable-mdpi/ic_time_line_blank_spark_flake_6.png new file mode 100644 index 00000000..37c29201 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_time_line_blank_spark_flake_6.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_time_line_spark_flake_1.png b/app/src/main/res/drawable-mdpi/ic_time_line_spark_flake_1.png new file mode 100644 index 00000000..8acd9509 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_time_line_spark_flake_1.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_time_line_spark_flake_2.png b/app/src/main/res/drawable-mdpi/ic_time_line_spark_flake_2.png new file mode 100644 index 00000000..ead67828 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_time_line_spark_flake_2.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_time_line_spark_flake_3.png b/app/src/main/res/drawable-mdpi/ic_time_line_spark_flake_3.png new file mode 100644 index 00000000..8af32701 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_time_line_spark_flake_3.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_time_line_spark_flake_4.png b/app/src/main/res/drawable-mdpi/ic_time_line_spark_flake_4.png new file mode 100644 index 00000000..d0667c96 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_time_line_spark_flake_4.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_time_line_spark_flake_5.png b/app/src/main/res/drawable-mdpi/ic_time_line_spark_flake_5.png new file mode 100644 index 00000000..0258d864 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_time_line_spark_flake_5.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_time_line_spark_flake_6.png b/app/src/main/res/drawable-mdpi/ic_time_line_spark_flake_6.png new file mode 100644 index 00000000..036f8651 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_time_line_spark_flake_6.png differ diff --git a/app/src/main/res/drawable-xhdpi/bg_flame_road_map_gradation_left.png b/app/src/main/res/drawable-xhdpi/bg_flame_road_map_gradation_left.png new file mode 100644 index 00000000..46347f50 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/bg_flame_road_map_gradation_left.png differ diff --git a/app/src/main/res/drawable-xhdpi/bg_flame_road_map_gradation_right.png b/app/src/main/res/drawable-xhdpi/bg_flame_road_map_gradation_right.png new file mode 100644 index 00000000..d17524c1 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/bg_flame_road_map_gradation_right.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_life_red_dot.png b/app/src/main/res/drawable-xhdpi/ic_life_red_dot.png new file mode 100644 index 00000000..97d2f67c Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_life_red_dot.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_time_line_blank_spark_flake_2.png b/app/src/main/res/drawable-xhdpi/ic_time_line_blank_spark_flake_2.png new file mode 100644 index 00000000..9914515f Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_time_line_blank_spark_flake_2.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_time_line_blank_spark_flake_3.png b/app/src/main/res/drawable-xhdpi/ic_time_line_blank_spark_flake_3.png new file mode 100644 index 00000000..5053508f Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_time_line_blank_spark_flake_3.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_time_line_blank_spark_flake_4.png b/app/src/main/res/drawable-xhdpi/ic_time_line_blank_spark_flake_4.png new file mode 100644 index 00000000..2c2dc033 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_time_line_blank_spark_flake_4.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_time_line_blank_spark_flake_5.png b/app/src/main/res/drawable-xhdpi/ic_time_line_blank_spark_flake_5.png new file mode 100644 index 00000000..9b3b4a48 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_time_line_blank_spark_flake_5.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_time_line_blank_spark_flake_6.png b/app/src/main/res/drawable-xhdpi/ic_time_line_blank_spark_flake_6.png new file mode 100644 index 00000000..5e56b9f3 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_time_line_blank_spark_flake_6.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_time_line_spark_flake_1.png b/app/src/main/res/drawable-xhdpi/ic_time_line_spark_flake_1.png new file mode 100644 index 00000000..a2067cb3 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_time_line_spark_flake_1.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_time_line_spark_flake_2.png b/app/src/main/res/drawable-xhdpi/ic_time_line_spark_flake_2.png new file mode 100644 index 00000000..39bacdf3 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_time_line_spark_flake_2.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_time_line_spark_flake_3.png b/app/src/main/res/drawable-xhdpi/ic_time_line_spark_flake_3.png new file mode 100644 index 00000000..d5b242ae Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_time_line_spark_flake_3.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_time_line_spark_flake_4.png b/app/src/main/res/drawable-xhdpi/ic_time_line_spark_flake_4.png new file mode 100644 index 00000000..31fb854f Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_time_line_spark_flake_4.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_time_line_spark_flake_5.png b/app/src/main/res/drawable-xhdpi/ic_time_line_spark_flake_5.png new file mode 100644 index 00000000..98e25d6a Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_time_line_spark_flake_5.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_time_line_spark_flake_6.png b/app/src/main/res/drawable-xhdpi/ic_time_line_spark_flake_6.png new file mode 100644 index 00000000..08e287f5 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_time_line_spark_flake_6.png differ diff --git a/app/src/main/res/drawable-xxhdpi/bg_flame_road_map_gradation_left.png b/app/src/main/res/drawable-xxhdpi/bg_flame_road_map_gradation_left.png new file mode 100644 index 00000000..2c115316 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/bg_flame_road_map_gradation_left.png differ diff --git a/app/src/main/res/drawable-xxhdpi/bg_flame_road_map_gradation_right.png b/app/src/main/res/drawable-xxhdpi/bg_flame_road_map_gradation_right.png new file mode 100644 index 00000000..b24b1cd8 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/bg_flame_road_map_gradation_right.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_life_red_dot.png b/app/src/main/res/drawable-xxhdpi/ic_life_red_dot.png new file mode 100644 index 00000000..ef9c881f Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_life_red_dot.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_time_line_blank_spark_flake_2.png b/app/src/main/res/drawable-xxhdpi/ic_time_line_blank_spark_flake_2.png new file mode 100644 index 00000000..be428a2f Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_time_line_blank_spark_flake_2.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_time_line_blank_spark_flake_3.png b/app/src/main/res/drawable-xxhdpi/ic_time_line_blank_spark_flake_3.png new file mode 100644 index 00000000..67ad8300 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_time_line_blank_spark_flake_3.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_time_line_blank_spark_flake_4.png b/app/src/main/res/drawable-xxhdpi/ic_time_line_blank_spark_flake_4.png new file mode 100644 index 00000000..e1a1bb1f Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_time_line_blank_spark_flake_4.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_time_line_blank_spark_flake_5.png b/app/src/main/res/drawable-xxhdpi/ic_time_line_blank_spark_flake_5.png new file mode 100644 index 00000000..c555d8bf Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_time_line_blank_spark_flake_5.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_time_line_blank_spark_flake_6.png b/app/src/main/res/drawable-xxhdpi/ic_time_line_blank_spark_flake_6.png new file mode 100644 index 00000000..97efc656 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_time_line_blank_spark_flake_6.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_time_line_spark_flake_1.png b/app/src/main/res/drawable-xxhdpi/ic_time_line_spark_flake_1.png new file mode 100644 index 00000000..97f1dcf1 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_time_line_spark_flake_1.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_time_line_spark_flake_2.png b/app/src/main/res/drawable-xxhdpi/ic_time_line_spark_flake_2.png new file mode 100644 index 00000000..b29feeba Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_time_line_spark_flake_2.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_time_line_spark_flake_3.png b/app/src/main/res/drawable-xxhdpi/ic_time_line_spark_flake_3.png new file mode 100644 index 00000000..ad89d11d Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_time_line_spark_flake_3.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_time_line_spark_flake_4.png b/app/src/main/res/drawable-xxhdpi/ic_time_line_spark_flake_4.png new file mode 100644 index 00000000..8acbbab5 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_time_line_spark_flake_4.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_time_line_spark_flake_5.png b/app/src/main/res/drawable-xxhdpi/ic_time_line_spark_flake_5.png new file mode 100644 index 00000000..0e32c576 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_time_line_spark_flake_5.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_time_line_spark_flake_6.png b/app/src/main/res/drawable-xxhdpi/ic_time_line_spark_flake_6.png new file mode 100644 index 00000000..118c3c50 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_time_line_spark_flake_6.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/bg_flame_road_map_gradation_left.png b/app/src/main/res/drawable-xxxhdpi/bg_flame_road_map_gradation_left.png new file mode 100644 index 00000000..4498f401 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/bg_flame_road_map_gradation_left.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/bg_flame_road_map_gradation_right.png b/app/src/main/res/drawable-xxxhdpi/bg_flame_road_map_gradation_right.png new file mode 100644 index 00000000..1a48a683 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/bg_flame_road_map_gradation_right.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_life_red_dot.png b/app/src/main/res/drawable-xxxhdpi/ic_life_red_dot.png new file mode 100644 index 00000000..9421c796 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_life_red_dot.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_time_line_blank_spark_flake_2.png b/app/src/main/res/drawable-xxxhdpi/ic_time_line_blank_spark_flake_2.png new file mode 100644 index 00000000..fdb6c384 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_time_line_blank_spark_flake_2.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_time_line_blank_spark_flake_3.png b/app/src/main/res/drawable-xxxhdpi/ic_time_line_blank_spark_flake_3.png new file mode 100644 index 00000000..9acc3379 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_time_line_blank_spark_flake_3.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_time_line_blank_spark_flake_4.png b/app/src/main/res/drawable-xxxhdpi/ic_time_line_blank_spark_flake_4.png new file mode 100644 index 00000000..4222c1a7 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_time_line_blank_spark_flake_4.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_time_line_blank_spark_flake_5.png b/app/src/main/res/drawable-xxxhdpi/ic_time_line_blank_spark_flake_5.png new file mode 100644 index 00000000..ccf6983c Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_time_line_blank_spark_flake_5.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_time_line_blank_spark_flake_6.png b/app/src/main/res/drawable-xxxhdpi/ic_time_line_blank_spark_flake_6.png new file mode 100644 index 00000000..09ec7ff0 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_time_line_blank_spark_flake_6.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_time_line_spark_flake_1.png b/app/src/main/res/drawable-xxxhdpi/ic_time_line_spark_flake_1.png new file mode 100644 index 00000000..65403638 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_time_line_spark_flake_1.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_time_line_spark_flake_2.png b/app/src/main/res/drawable-xxxhdpi/ic_time_line_spark_flake_2.png new file mode 100644 index 00000000..659b6966 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_time_line_spark_flake_2.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_time_line_spark_flake_3.png b/app/src/main/res/drawable-xxxhdpi/ic_time_line_spark_flake_3.png new file mode 100644 index 00000000..7510d243 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_time_line_spark_flake_3.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_time_line_spark_flake_4.png b/app/src/main/res/drawable-xxxhdpi/ic_time_line_spark_flake_4.png new file mode 100644 index 00000000..fa5018e5 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_time_line_spark_flake_4.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_time_line_spark_flake_5.png b/app/src/main/res/drawable-xxxhdpi/ic_time_line_spark_flake_5.png new file mode 100644 index 00000000..50cc4292 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_time_line_spark_flake_5.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_time_line_spark_flake_6.png b/app/src/main/res/drawable-xxxhdpi/ic_time_line_spark_flake_6.png new file mode 100644 index 00000000..2409759b Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_time_line_spark_flake_6.png differ diff --git a/app/src/main/res/layout/activity_habit.xml b/app/src/main/res/layout/activity_habit.xml index 79e78baf..433218d2 100644 --- a/app/src/main/res/layout/activity_habit.xml +++ b/app/src/main/res/layout/activity_habit.xml @@ -142,6 +142,17 @@ app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toBottomOf="@+id/btn_habit_more" /> + + + + + + + app:layout_constraintTop_toBottomOf="@id/view_habit_more_divider3" /> + app:layout_constraintTop_toBottomOf="@id/view_habit_more_divider4" /> diff --git a/app/src/main/res/layout/bottom_sheet_habit_time_line.xml b/app/src/main/res/layout/bottom_sheet_habit_time_line.xml new file mode 100644 index 00000000..aaba6817 --- /dev/null +++ b/app/src/main/res/layout/bottom_sheet_habit_time_line.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/fragment_flame_road_map_dialog.xml b/app/src/main/res/layout/fragment_flame_road_map_dialog.xml new file mode 100644 index 00000000..d06d74cb --- /dev/null +++ b/app/src/main/res/layout/fragment_flame_road_map_dialog.xml @@ -0,0 +1,101 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/fragment_level1.xml b/app/src/main/res/layout/fragment_level1.xml new file mode 100644 index 00000000..f2c7dca7 --- /dev/null +++ b/app/src/main/res/layout/fragment_level1.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + diff --git a/app/src/main/res/layout/fragment_level2.xml b/app/src/main/res/layout/fragment_level2.xml new file mode 100644 index 00000000..c7772030 --- /dev/null +++ b/app/src/main/res/layout/fragment_level2.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/fragment_level3.xml b/app/src/main/res/layout/fragment_level3.xml new file mode 100644 index 00000000..56e413c4 --- /dev/null +++ b/app/src/main/res/layout/fragment_level3.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + diff --git a/app/src/main/res/layout/fragment_level4.xml b/app/src/main/res/layout/fragment_level4.xml new file mode 100644 index 00000000..1523e6d6 --- /dev/null +++ b/app/src/main/res/layout/fragment_level4.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + diff --git a/app/src/main/res/layout/fragment_level5.xml b/app/src/main/res/layout/fragment_level5.xml new file mode 100644 index 00000000..e6a81ce5 --- /dev/null +++ b/app/src/main/res/layout/fragment_level5.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + diff --git a/app/src/main/res/layout/fragment_level6.xml b/app/src/main/res/layout/fragment_level6.xml new file mode 100644 index 00000000..8d4fbb46 --- /dev/null +++ b/app/src/main/res/layout/fragment_level6.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + diff --git a/app/src/main/res/layout/item_habit_time_line.xml b/app/src/main/res/layout/item_habit_time_line.xml new file mode 100644 index 00000000..c08455b4 --- /dev/null +++ b/app/src/main/res/layout/item_habit_time_line.xml @@ -0,0 +1,96 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/values/dimen.xml b/app/src/main/res/values/dimen.xml index 2fdf817b..9446b721 100644 --- a/app/src/main/res/values/dimen.xml +++ b/app/src/main/res/values/dimen.xml @@ -2,4 +2,6 @@ 12dp 36dp - \ No newline at end of file + 80dp + 120dp + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index d3122f45..6f72ea92 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -210,6 +210,7 @@ 아직 습관이 완료되지 않았어요.\n그래도 나가려면 ‘방 이름’을 입력해주세요. 습관방 이용 팁 방 나가기 + 불꽃 결정 레벨 오늘도 꺼지지 않는 스파크, @@ -394,5 +395,27 @@ Oops... 확인했어요 + + 생명 타임라인 + + + Level 1 + Level 2 + Level 3 + Level 4 + Level 5 + Level 6 + 새로운 습관 시작!\n첫 번째 불꽃 결정을 얻었어요. + 3일 동안 열심히 달렸네요!\n두 번째 불꽃 결정을 얻었어요. + 7일 동안 열심히 달렸네요!\n세 번째 불꽃 결정을 얻었어요. + 33일 동안 열심히 달렸네요!\n네 번째 불꽃 결정을 얻었어요. + 마지막 7일 남았네요!\n다섯 번째 불꽃 결정을 얻었어요. + 드디어 마지막 날이네요!\n여섯 번째 불꽃 결정을 얻었어요. + 3일을 달성하면 얻을 수 있어요! + 7일을 달성하면 얻을 수 있어요! + 33일을 달성하면 얻을 수 있어요! + 마지막 7일 남았을 때 얻을 수 있어요! + 마지막 날에 얻을 수 있어요! + diff --git a/build.gradle b/build.gradle index 5fbe6001..28e3ba48 100644 --- a/build.gradle +++ b/build.gradle @@ -7,7 +7,7 @@ buildscript { } dependencies { classpath "com.android.tools.build:gradle:7.0.2" - classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.10' + classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.21' // Hilt classpath 'com.google.dagger:hilt-android-gradle-plugin:2.38.1'