├── .gitignore ├── LICENSE ├── README.md ├── app ├── .gitignore ├── build.gradle.kts ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── lateinit │ │ └── rightweight │ │ ├── CustomTestRunner.kt │ │ ├── ExampleInstrumentedTest.kt │ │ └── SharedRoutineTest.kt │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── lateinit │ │ │ └── rightweight │ │ │ ├── RightWeightApplication.kt │ │ │ ├── data │ │ │ ├── api │ │ │ │ ├── AuthApiService.kt │ │ │ │ ├── RoutineApiService.kt │ │ │ │ └── UserApiService.kt │ │ │ ├── dataStore │ │ │ │ └── AppPreferencesDataStore.kt │ │ │ ├── database │ │ │ │ ├── AppDatabase.kt │ │ │ │ ├── Converters.kt │ │ │ │ ├── dao │ │ │ │ │ ├── HistoryDao.kt │ │ │ │ │ ├── RoutineDao.kt │ │ │ │ │ └── SharedRoutineDao.kt │ │ │ │ ├── entity │ │ │ │ │ ├── Day.kt │ │ │ │ │ ├── Exercise.kt │ │ │ │ │ ├── ExerciseSet.kt │ │ │ │ │ ├── History.kt │ │ │ │ │ ├── HistoryExercise.kt │ │ │ │ │ ├── HistorySet.kt │ │ │ │ │ ├── Routine.kt │ │ │ │ │ ├── SharedRoutine.kt │ │ │ │ │ ├── SharedRoutineDay.kt │ │ │ │ │ ├── SharedRoutineExercise.kt │ │ │ │ │ └── SharedRoutineExerciseSet.kt │ │ │ │ └── intermediate │ │ │ │ │ ├── DayWithExercises.kt │ │ │ │ │ ├── ExerciseWithSets.kt │ │ │ │ │ ├── HistoryExerciseWithHistorySets.kt │ │ │ │ │ ├── HistoryWithHistoryExercises.kt │ │ │ │ │ ├── RoutineWithDays.kt │ │ │ │ │ ├── SharedRoutineDayWithExercises.kt │ │ │ │ │ ├── SharedRoutineExerciseWithExerciseSets.kt │ │ │ │ │ └── SharedRoutineWithDays.kt │ │ │ ├── datasource │ │ │ │ ├── local │ │ │ │ │ ├── HistoryLocalDataSource.kt │ │ │ │ │ ├── RoutineLocalDataSource.kt │ │ │ │ │ ├── SharedRoutineLocalDataSource.kt │ │ │ │ │ ├── UserLocalDataSource.kt │ │ │ │ │ └── impl │ │ │ │ │ │ ├── HistoryLocalDataSourceImpl.kt │ │ │ │ │ │ ├── RoutineLocalDataSourceImpl.kt │ │ │ │ │ │ ├── SharedRoutineLocalDataSourceImpl.kt │ │ │ │ │ │ └── UserLocalDataSourceImpl.kt │ │ │ │ └── remote │ │ │ │ │ ├── HistoryRemoteDatasource.kt │ │ │ │ │ ├── LoginDataSource.kt │ │ │ │ │ ├── RoutineRemoteDataSource.kt │ │ │ │ │ ├── SharedRoutineRemoteDataSource.kt │ │ │ │ │ ├── UserRemoteDataSource.kt │ │ │ │ │ └── impl │ │ │ │ │ ├── HistoryRemoteDataSourceImpl.kt │ │ │ │ │ ├── LoginDataSourceImpl.kt │ │ │ │ │ ├── RoutineRemoteDataSourceImpl.kt │ │ │ │ │ ├── SharedRoutineRemoteDataSourceImpl.kt │ │ │ │ │ └── UserRemoteDataSourceImpl.kt │ │ │ ├── mapper │ │ │ │ ├── local │ │ │ │ │ ├── ToExercisePartType.kt │ │ │ │ │ ├── ToHistory.kt │ │ │ │ │ ├── ToRoutine.kt │ │ │ │ │ ├── ToSharedRoutine.kt │ │ │ │ │ └── ToSharedRoutineSortType.kt │ │ │ │ └── remote │ │ │ │ │ ├── ToHisttoryField.kt │ │ │ │ │ ├── ToRoutineField.kt │ │ │ │ │ └── ToSharedRoutineField.kt │ │ │ ├── mediator │ │ │ │ └── SharedRoutineRemoteMediator.kt │ │ │ ├── model │ │ │ │ ├── local │ │ │ │ │ ├── ExercisePartType.kt │ │ │ │ │ └── User.kt │ │ │ │ └── remote │ │ │ │ │ ├── DataValue.kt │ │ │ │ │ ├── Documents.kt │ │ │ │ │ ├── LoginModel.kt │ │ │ │ │ ├── LoginResponse.kt │ │ │ │ │ ├── RemoteData.kt │ │ │ │ │ ├── RootField.kt │ │ │ │ │ ├── RunQueryModel.kt │ │ │ │ │ └── WriteModel.kt │ │ │ └── repository │ │ │ │ ├── HistoryRepository.kt │ │ │ │ ├── LoginRepository.kt │ │ │ │ ├── RoutineRepository.kt │ │ │ │ ├── SharedRoutineRepository.kt │ │ │ │ ├── UserRepository.kt │ │ │ │ └── impl │ │ │ │ ├── HistoryRepositoryImpl.kt │ │ │ │ ├── LoginRepositoryImpl.kt │ │ │ │ ├── RoutineRepositoryImpl.kt │ │ │ │ ├── SharedRoutineRepositoryImpl.kt │ │ │ │ └── UserRepositoryImpl.kt │ │ │ ├── di │ │ │ ├── ApiModule.kt │ │ │ ├── DataSourceModule.kt │ │ │ ├── DatabaseModule.kt │ │ │ └── RepositoryModule.kt │ │ │ ├── service │ │ │ └── TimerService.kt │ │ │ ├── ui │ │ │ ├── MainActivity.kt │ │ │ ├── MainViewModel.kt │ │ │ ├── calendar │ │ │ │ ├── CalendarFragment.kt │ │ │ │ ├── CalendarViewModel.kt │ │ │ │ ├── CompletedDayDecorator.kt │ │ │ │ └── DayDecorator.kt │ │ │ ├── dialog │ │ │ │ ├── CommonDialogFragment.kt │ │ │ │ └── LoadingDialog.kt │ │ │ ├── exercise │ │ │ │ ├── ExerciseFragment.kt │ │ │ │ ├── ExerciseViewModel.kt │ │ │ │ ├── HistoryEventListener.kt │ │ │ │ ├── HistoryExerciseAdapter.kt │ │ │ │ └── HistorySetAdapter.kt │ │ │ ├── home │ │ │ │ ├── ExpandableItemAnimator.kt │ │ │ │ ├── HomeAdapter.kt │ │ │ │ ├── HomeBindingAdapter.kt │ │ │ │ ├── HomeFragment.kt │ │ │ │ └── HomeViewModel.kt │ │ │ ├── login │ │ │ │ ├── LoginActivity.kt │ │ │ │ └── LoginViewModel.kt │ │ │ ├── mapper │ │ │ │ ├── ExercisePartType.kt │ │ │ │ ├── HistoryUiModel.kt │ │ │ │ ├── Routine.kt │ │ │ │ └── SharedRoutine.kt │ │ │ ├── model │ │ │ │ ├── LoadingState.kt │ │ │ │ ├── ParentDayUiModel.kt │ │ │ │ ├── ParentExerciseSetUiModel.kt │ │ │ │ ├── ParentExerciseUiModel.kt │ │ │ │ ├── ParentRoutineUiModel.kt │ │ │ │ ├── history │ │ │ │ │ ├── HistoryExerciseSetUiModel.kt │ │ │ │ │ ├── HistoryExerciseUiModel.kt │ │ │ │ │ └── HistoryUiModel.kt │ │ │ │ ├── routine │ │ │ │ │ ├── DayUiModel.kt │ │ │ │ │ ├── ExercisePartTypeUiModel.kt │ │ │ │ │ ├── ExerciseSetUiModel.kt │ │ │ │ │ ├── ExerciseUiModel.kt │ │ │ │ │ └── RoutineUiModel.kt │ │ │ │ └── shared │ │ │ │ │ ├── SharedRoutineSortTypeUiModel.kt │ │ │ │ │ └── SharedRoutineUiModel.kt │ │ │ ├── routine │ │ │ │ ├── detail │ │ │ │ │ ├── DetailExerciseAdapter.kt │ │ │ │ │ ├── DetailExerciseSetAdapter.kt │ │ │ │ │ ├── RoutineDetailFragment.kt │ │ │ │ │ └── RoutineDetailViewModel.kt │ │ │ │ ├── editor │ │ │ │ │ ├── RoutineDayAdapter.kt │ │ │ │ │ ├── RoutineEditorFragment.kt │ │ │ │ │ ├── RoutineEditorViewModel.kt │ │ │ │ │ ├── RoutineExerciseAdapter.kt │ │ │ │ │ └── RoutineSetAdapter.kt │ │ │ │ └── management │ │ │ │ │ ├── RoutineAdapter.kt │ │ │ │ │ ├── RoutineManagementFragment.kt │ │ │ │ │ └── RoutineManagementViewModel.kt │ │ │ └── share │ │ │ │ ├── SharedRoutineClickHandler.kt │ │ │ │ ├── SharedRoutineFragment.kt │ │ │ │ ├── SharedRoutinePagingAdapter.kt │ │ │ │ ├── SharedRoutineViewModel.kt │ │ │ │ └── detail │ │ │ │ ├── SharedRoutineDetailFragment.kt │ │ │ │ └── SharedRoutineDetailViewModel.kt │ │ │ └── util │ │ │ ├── CenterSmoothScroller.kt │ │ │ ├── Consts.kt │ │ │ ├── Extensions.kt │ │ │ ├── TimeConverter.kt │ │ │ └── UUID.kt │ └── res │ │ ├── color │ │ ├── color_completed_day_background.xml │ │ ├── color_day_background.xml │ │ └── color_day_text.xml │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable │ │ ├── bg_calendar_day.xml │ │ ├── bg_calendar_day_completed.xml │ │ ├── bg_day_order.xml │ │ ├── bg_day_order_selected.xml │ │ ├── bg_exercise_part.xml │ │ ├── ic_add.xml │ │ ├── ic_arrow_down.xml │ │ ├── ic_arrow_up.xml │ │ ├── ic_back.xml │ │ ├── ic_backup.xml │ │ ├── ic_calendar.xml │ │ ├── ic_down_arrow.xml │ │ ├── ic_download.xml │ │ ├── ic_home.xml │ │ ├── ic_launcher_background.xml │ │ ├── ic_logout.xml │ │ ├── ic_more.xml │ │ ├── ic_pause.xml │ │ ├── ic_play_arrow.xml │ │ ├── ic_remove.xml │ │ ├── ic_routine_management.xml │ │ ├── ic_shared_routine.xml │ │ ├── ic_splash_logo.xml │ │ ├── ic_up_arrow.xml │ │ ├── ic_withdraw.xml │ │ ├── img_completed.xml │ │ └── img_right_weight.xml │ │ ├── layout │ │ ├── activity_login.xml │ │ ├── activity_main.xml │ │ ├── dialog_loading.xml │ │ ├── fragment_calendar.xml │ │ ├── fragment_exercise.xml │ │ ├── fragment_home.xml │ │ ├── fragment_routine_detail.xml │ │ ├── fragment_routine_editor.xml │ │ ├── fragment_routine_management.xml │ │ ├── fragment_shared_routine.xml │ │ ├── fragment_shared_routine_detail.xml │ │ ├── item_day.xml │ │ ├── item_exercise.xml │ │ ├── item_exercise_part.xml │ │ ├── item_exercise_view_home.xml │ │ ├── item_exercise_with_sets.xml │ │ ├── item_history_set.xml │ │ ├── item_routine.xml │ │ ├── item_set.xml │ │ ├── item_set_read.xml │ │ ├── item_shared_routine.xml │ │ ├── item_shared_routine_sort_type.xml │ │ ├── layout_day_exercises.xml │ │ ├── navigation_header.xml │ │ └── notification_foreground.xml │ │ ├── menu │ │ ├── menu_bottom_navigation.xml │ │ ├── menu_navigation_drawer.xml │ │ └── menu_routine_detail.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── navigation │ │ └── nav_graph.xml │ │ ├── values-night │ │ └── themes.xml │ │ ├── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ ├── styles.xml │ │ └── themes.xml │ │ └── xml │ │ ├── backup_rules.xml │ │ └── data_extraction_rules.xml │ └── test │ └── java │ └── com │ └── lateinit │ └── rightweight │ └── ExampleUnitTest.kt ├── build.gradle.kts ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle.kts /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 부스트캠프 웹·모바일 7기 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle.kts. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /app/src/androidTest/java/com/lateinit/rightweight/CustomTestRunner.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight 2 | 3 | import android.app.Application 4 | import android.content.Context 5 | import androidx.test.runner.AndroidJUnitRunner 6 | import dagger.hilt.android.testing.HiltTestApplication 7 | 8 | class CustomTestRunner : AndroidJUnitRunner() { 9 | override fun newApplication(cl: ClassLoader?, name: String?, context: Context?): Application { 10 | return super.newApplication(cl, HiltTestApplication::class.java.name, context) 11 | } 12 | } -------------------------------------------------------------------------------- /app/src/androidTest/java/com/lateinit/rightweight/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight 2 | 3 | import androidx.test.platform.app.InstrumentationRegistry 4 | import androidx.test.ext.junit.runners.AndroidJUnit4 5 | 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | import org.junit.Assert.* 10 | 11 | /** 12 | * Instrumented test, which will execute on an Android device. 13 | * 14 | * See [testing documentation](http://d.android.com/tools/testing). 15 | */ 16 | @RunWith(AndroidJUnit4::class) 17 | class ExampleInstrumentedTest { 18 | @Test 19 | fun useAppContext() { 20 | // Context of the app under test. 21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 22 | assertEquals("com.lateinit.rightweight", appContext.packageName) 23 | } 24 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 18 | 25 | 26 | 29 | 30 | 31 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 46 | 47 | 48 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/RightWeightApplication.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight 2 | 3 | import android.app.Application 4 | import dagger.hilt.android.HiltAndroidApp 5 | 6 | @HiltAndroidApp 7 | class RightWeightApplication : Application() -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/data/api/AuthApiService.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight.data.api 2 | 3 | import com.lateinit.rightweight.data.model.remote.LoginRequestBody 4 | import com.lateinit.rightweight.data.model.remote.LoginResponse 5 | import retrofit2.http.Body 6 | import retrofit2.http.Field 7 | import retrofit2.http.FormUrlEncoded 8 | import retrofit2.http.POST 9 | import retrofit2.http.Query 10 | 11 | interface AuthApiService { 12 | 13 | @POST("./accounts:signInWithIdp") 14 | suspend fun loginToFirebase( 15 | @Query("key") key: String, 16 | @Body body: LoginRequestBody, 17 | ): LoginResponse 18 | 19 | @FormUrlEncoded 20 | @POST("./accounts:delete") 21 | suspend fun deleteAccount( 22 | @Query("key") key: String, 23 | @Field("idToken") idToken: String 24 | ) 25 | } -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/data/api/RoutineApiService.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight.data.api 2 | 3 | import com.lateinit.rightweight.data.model.remote.DetailResponse 4 | import com.lateinit.rightweight.data.model.remote.DocumentResponse 5 | import com.lateinit.rightweight.data.model.remote.DocumentsResponse 6 | import com.lateinit.rightweight.data.model.remote.RunQueryBody 7 | import com.lateinit.rightweight.data.model.remote.WriteRequestBody 8 | import com.lateinit.rightweight.data.remote.model.* 9 | import retrofit2.Response 10 | import retrofit2.http.Body 11 | import retrofit2.http.GET 12 | import retrofit2.http.POST 13 | import retrofit2.http.Path 14 | 15 | interface RoutineApiService { 16 | 17 | @POST("./documents:runQuery") 18 | suspend fun getSharedRoutines( 19 | @Body order: RunQueryBody 20 | ): List> 21 | 22 | @GET("documents/{path}") 23 | suspend fun getChildrenDocument( 24 | @Path(value = "path", encoded = true) 25 | path: String 26 | ): DocumentsResponse> 27 | 28 | @GET("documents/routine/{path}") 29 | suspend fun getRoutine( 30 | @Path("path") path: String 31 | ): DetailResponse 32 | 33 | @GET("documents/{path}") 34 | suspend fun getDays( 35 | @Path(value = "path", encoded = true) 36 | path: String 37 | ): DocumentsResponse 38 | 39 | @GET("documents/{path}") 40 | suspend fun getExercises( 41 | @Path(value = "path", encoded = true) 42 | path: String 43 | ): DocumentsResponse 44 | 45 | @GET("documents/{path}") 46 | suspend fun getExerciseSets( 47 | @Path(value = "path", encoded = true) 48 | path: String 49 | ): DocumentsResponse 50 | 51 | @GET("documents/shared_routine/{routineId}") 52 | suspend fun getSharedRoutine( 53 | @Path("routineId") routineId: String 54 | ): Response 55 | 56 | @GET("documents/shared_routine/{routineId}/day") 57 | suspend fun getSharedRoutineDays( 58 | @Path("routineId") routineId: String 59 | ): DocumentsResponse? 60 | 61 | @GET("documents/shared_routine/{routineId}/day/{dayId}/exercise") 62 | suspend fun getSharedRoutineExercises( 63 | @Path("routineId") routineId: String, 64 | @Path("dayId") dayId: String 65 | ): DocumentsResponse? 66 | 67 | @GET("documents/shared_routine/{routineId}/day/{dayId}/exercise/{exerciseId}/exercise_set") 68 | suspend fun getSharedRoutineExerciseSets( 69 | @Path("routineId") routineId: String, 70 | @Path("dayId") dayId: String, 71 | @Path("exerciseId") exerciseId: String 72 | ): DocumentsResponse? 73 | 74 | @POST("./documents:runQuery") 75 | suspend fun getUserRoutine( 76 | @Body query: RunQueryBody 77 | ): List> 78 | 79 | 80 | @POST("./documents:commit") 81 | suspend fun commitTransaction( 82 | @Body 83 | writes: WriteRequestBody 84 | ) 85 | 86 | } -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/data/api/UserApiService.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight.data.api 2 | 3 | import com.lateinit.rightweight.data.model.remote.DetailResponse 4 | import com.lateinit.rightweight.data.model.remote.DocumentResponse 5 | import com.lateinit.rightweight.data.model.remote.DocumentsResponse 6 | import com.lateinit.rightweight.data.model.remote.RunQueryBody 7 | import com.lateinit.rightweight.data.model.remote.WriteRequestBody 8 | import com.lateinit.rightweight.data.remote.model.HistoryExerciseField 9 | import com.lateinit.rightweight.data.remote.model.HistoryExerciseSetField 10 | import com.lateinit.rightweight.data.remote.model.HistoryField 11 | import com.lateinit.rightweight.data.remote.model.RemoteData 12 | import com.lateinit.rightweight.data.remote.model.RootField 13 | import com.lateinit.rightweight.data.remote.model.UserInfoField 14 | import retrofit2.Response 15 | import retrofit2.http.Body 16 | import retrofit2.http.GET 17 | import retrofit2.http.PATCH 18 | import retrofit2.http.POST 19 | import retrofit2.http.Path 20 | 21 | interface UserApiService { 22 | @PATCH("documents/user/{userId}") 23 | suspend fun backupUserInfo( 24 | @Path("userId") userId: String, 25 | @Body userInfoField: RootField 26 | ) 27 | 28 | @POST("./documents:commit") 29 | suspend fun commitTransaction( 30 | @Body 31 | writes: WriteRequestBody 32 | ) 33 | 34 | @GET("documents/routine/{path}") 35 | suspend fun getChildrenDocumentName( 36 | @Path(value = "path", encoded = true) 37 | path: String 38 | ): DocumentsResponse> 39 | 40 | @POST("documents/user/{userId}/:runQuery") 41 | suspend fun getLastHistoryDate( 42 | @Path(value = "userId", encoded = true) userId: String, 43 | @Body query: RunQueryBody 44 | ): List> 45 | 46 | @GET("documents/{path}") 47 | suspend fun getHistories( 48 | @Path(value = "path", encoded = true) 49 | path: String 50 | ): DocumentsResponse 51 | 52 | @GET("documents/{path}") 53 | suspend fun getHistoryExercises( 54 | @Path(value = "path", encoded = true) 55 | path: String 56 | ): DocumentsResponse 57 | 58 | @GET("documents/{path}") 59 | suspend fun getHistoryExerciseSets( 60 | @Path(value = "path", encoded = true) 61 | path: String 62 | ): DocumentsResponse 63 | 64 | @GET("documents/user/{userId}") 65 | suspend fun restoreUserInfo( 66 | @Path(value = "userId", encoded = true) userId: String 67 | ): Response> 68 | } -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/data/dataStore/AppPreferencesDataStore.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight.data.dataStore 2 | 3 | import android.content.Context 4 | import androidx.datastore.core.DataStore 5 | import androidx.datastore.preferences.core.Preferences 6 | import androidx.datastore.preferences.core.edit 7 | import androidx.datastore.preferences.core.stringPreferencesKey 8 | import androidx.datastore.preferences.preferencesDataStore 9 | import com.google.gson.Gson 10 | import com.lateinit.rightweight.data.model.local.User 11 | import kotlinx.coroutines.flow.Flow 12 | import kotlinx.coroutines.flow.map 13 | import javax.inject.Inject 14 | 15 | class AppPreferencesDataStore @Inject constructor(private val context: Context) { 16 | 17 | private val Context.dataStore: DataStore by preferencesDataStore(name = "RightWeight") 18 | 19 | val userInfo: Flow = context.dataStore.data 20 | .map { preferences -> 21 | Gson().fromJson(preferences[USER_INFO], User::class.java) ?: null 22 | } 23 | 24 | val sharedRoutinePagingFlag: Flow = context.dataStore.data 25 | .map { preferences -> 26 | preferences[PAGING_FLAG] ?: "" 27 | } 28 | 29 | suspend fun saveUser(user: User) { 30 | context.dataStore.edit { 31 | it[USER_INFO] = Gson().toJson(user) 32 | } 33 | } 34 | 35 | suspend fun deleteUserInfo() { 36 | context.dataStore.edit { 37 | it.remove(USER_INFO) 38 | } 39 | } 40 | 41 | suspend fun saveSharedRoutinePagingFlag(flag: String) { 42 | context.dataStore.edit { 43 | it[PAGING_FLAG] = flag 44 | } 45 | } 46 | 47 | companion object { 48 | private val USER_INFO = stringPreferencesKey("user") 49 | private val PAGING_FLAG = stringPreferencesKey("paging_flag") 50 | } 51 | } -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/data/database/AppDatabase.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight.data.database 2 | 3 | import androidx.room.Database 4 | import androidx.room.RoomDatabase 5 | import androidx.room.TypeConverters 6 | import com.lateinit.rightweight.data.database.dao.HistoryDao 7 | import com.lateinit.rightweight.data.database.dao.RoutineDao 8 | import com.lateinit.rightweight.data.database.dao.SharedRoutineDao 9 | import com.lateinit.rightweight.data.database.entity.* 10 | 11 | @Database( 12 | entities = [ 13 | Routine::class, Day::class, Exercise::class, ExerciseSet::class, 14 | History::class, HistoryExercise::class, HistorySet::class, 15 | SharedRoutine::class, SharedRoutineDay::class, SharedRoutineExercise::class, SharedRoutineExerciseSet::class 16 | ], 17 | version = 1, 18 | exportSchema = false 19 | ) 20 | @TypeConverters(Converters::class) 21 | abstract class AppDatabase : RoomDatabase() { 22 | abstract fun routineDao(): RoutineDao 23 | abstract fun historyDao(): HistoryDao 24 | abstract fun sharedRoutineDao(): SharedRoutineDao 25 | } -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/data/database/Converters.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight.data.database 2 | 3 | import androidx.room.TypeConverter 4 | import java.time.* 5 | 6 | class Converters { 7 | 8 | @TypeConverter 9 | fun toLocalDateTime(timestamp: Long): LocalDateTime { 10 | return Instant.ofEpochMilli(timestamp).atZone(ZoneId.systemDefault()).toLocalDateTime() 11 | } 12 | 13 | @TypeConverter 14 | fun toTimestamp(date: LocalDateTime): Long { 15 | return date.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli() 16 | } 17 | 18 | @TypeConverter 19 | fun toTimestamp(date: LocalDate): Long { 20 | return date.atTime(LocalTime.MIDNIGHT).toInstant(ZoneOffset.UTC).toEpochMilli() 21 | } 22 | 23 | @TypeConverter 24 | fun toLocalDate(timestamp: Long): LocalDate { 25 | return Instant.ofEpochMilli(timestamp).atZone(ZoneId.systemDefault()).toLocalDate() 26 | } 27 | } -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/data/database/dao/HistoryDao.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight.data.database.dao 2 | 3 | import androidx.room.Dao 4 | import androidx.room.Insert 5 | import androidx.room.OnConflictStrategy 6 | import androidx.room.Query 7 | import androidx.room.Transaction 8 | import androidx.room.Update 9 | import com.lateinit.rightweight.data.database.entity.History 10 | import com.lateinit.rightweight.data.database.entity.HistoryExercise 11 | import com.lateinit.rightweight.data.database.entity.HistorySet 12 | import com.lateinit.rightweight.data.database.intermediate.HistoryWithHistoryExercises 13 | import kotlinx.coroutines.flow.Flow 14 | import java.time.LocalDate 15 | 16 | @Dao 17 | interface HistoryDao { 18 | 19 | @Insert(onConflict = OnConflictStrategy.REPLACE) 20 | suspend fun insertHistory( 21 | history: History, 22 | exercises: List, 23 | sets: List 24 | ) 25 | 26 | @Insert 27 | suspend fun restoreHistory( 28 | history: List, 29 | exercises: List, 30 | sets: List 31 | ) 32 | 33 | @Insert(onConflict = OnConflictStrategy.REPLACE) 34 | suspend fun insertHistorySet(set: HistorySet) 35 | 36 | @Insert(onConflict = OnConflictStrategy.REPLACE) 37 | suspend fun insertHistoryExercise(historyExercise: HistoryExercise) 38 | 39 | @Query("SELECT * FROM history WHERE date = :localDate") 40 | fun getHistoryByDate(localDate: LocalDate): Flow 41 | 42 | @Query("SELECT * FROM history ORDER BY date DESC LIMIT 1") 43 | fun getLatestHistory(): History 44 | 45 | @Transaction 46 | @Query("SELECT * FROM history WHERE date > :startDate") 47 | suspend fun getHistoryAfterDate( 48 | startDate: LocalDate, 49 | ): List 50 | 51 | @Transaction 52 | @Query("SELECT * FROM history WHERE date = :localDate") 53 | fun getHistoryWithHistoryExercisesByDate(localDate: LocalDate): Flow 54 | 55 | @Transaction 56 | @Query("SELECT * FROM history WHERE date BETWEEN :startDate AND :endDate") 57 | fun getHistoryBetweenDate( 58 | startDate: LocalDate, 59 | endDate: LocalDate 60 | ): Flow> 61 | 62 | @Query("SELECT COALESCE(MAX(`order`), 0) FROM history_exercise WHERE history_id = :historyId") 63 | suspend fun getMaxHistoryExerciseOrder(historyId: String): Int 64 | 65 | @Query("SELECT COALESCE(MAX(`order`), 0) FROM history_set WHERE exercise_id = :exerciseId") 66 | suspend fun getMaxHistorySetOrder(exerciseId: String): Int 67 | 68 | @Update 69 | suspend fun updateHistory(history: History) 70 | 71 | @Update 72 | suspend fun updateHistorySet(set: HistorySet) 73 | 74 | @Update 75 | suspend fun updateHistoryExercise(historyExercise: HistoryExercise) 76 | 77 | @Query("DELETE FROM history_set WHERE set_id = :historySetId") 78 | suspend fun removeHistorySet(historySetId: String) 79 | 80 | @Query("DELETE FROM history_exercise WHERE exercise_id = :historyExerciseId") 81 | suspend fun removeHistoryExercise(historyExerciseId: String) 82 | 83 | @Query("DELETE FROM history") 84 | suspend fun removeAllHistories() 85 | 86 | @Query("DELETE FROM history_set WHERE checked = 0") 87 | suspend fun removeUncheckedHistorySet() 88 | 89 | @Query("DELETE FROM history_exercise WHERE (SELECT count(*) FROM history_set WHERE history_set.exercise_id = history_exercise.exercise_id) = 0") 90 | suspend fun removeUncheckedHistoryExercise() 91 | } -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/data/database/dao/RoutineDao.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight.data.database.dao 2 | 3 | import androidx.room.Dao 4 | import androidx.room.Insert 5 | import androidx.room.OnConflictStrategy 6 | import androidx.room.Query 7 | import androidx.room.Transaction 8 | import androidx.room.Update 9 | import com.lateinit.rightweight.data.database.entity.Day 10 | import com.lateinit.rightweight.data.database.entity.Exercise 11 | import com.lateinit.rightweight.data.database.entity.ExerciseSet 12 | import com.lateinit.rightweight.data.database.entity.Routine 13 | import com.lateinit.rightweight.data.database.intermediate.DayWithExercises 14 | import com.lateinit.rightweight.data.database.intermediate.RoutineWithDays 15 | import kotlinx.coroutines.flow.Flow 16 | 17 | @Dao 18 | interface RoutineDao { 19 | 20 | @Insert(onConflict = OnConflictStrategy.REPLACE) 21 | suspend fun insertRoutine( 22 | routine: Routine, 23 | days: List, 24 | exercises: List, 25 | sets: List 26 | ) 27 | 28 | @Insert 29 | suspend fun restoreRoutine( 30 | routine: List, 31 | days: List, 32 | exercises: List, 33 | sets: List 34 | ) 35 | 36 | @Update 37 | suspend fun updateRoutines(routines: List) 38 | 39 | @Query("SELECT (`order`) FROM routine ORDER BY `order` DESC LIMIT 1") 40 | suspend fun getHigherRoutineOrder(): Int? 41 | 42 | @Query("SELECT * FROM routine WHERE routine_id = :routineId") 43 | suspend fun getRoutineById(routineId: String): Routine 44 | 45 | @Query("SELECT * FROM day WHERE day_id = :dayId") 46 | suspend fun getDayById(dayId: String): Day 47 | 48 | @Query("SELECT * FROM day WHERE routine_id = :routineId ORDER BY `order`") 49 | suspend fun getDaysByRoutineId(routineId: String): List 50 | 51 | @Query("SELECT * FROM exercise WHERE day_id = :dayId ORDER BY `order`") 52 | suspend fun getExercisesByDayId(dayId: String): List 53 | 54 | @Query("SELECT * FROM exercise_set WHERE exercise_id = :exerciseId ORDER BY `order`") 55 | suspend fun getSetsByExerciseId(exerciseId: String): List 56 | 57 | @Transaction 58 | @Query("SELECT * FROM routine") 59 | suspend fun getAllRoutineWithDays(): List 60 | 61 | @Query("SELECT * FROM routine ORDER BY `order`") 62 | fun getAllRoutines(): Flow> 63 | 64 | @Transaction 65 | @Query("SELECT * FROM routine WHERE routine_id = :routineId") 66 | suspend fun getRoutineWithDaysByRoutineId(routineId: String): RoutineWithDays 67 | 68 | @Transaction 69 | @Query("SELECT * FROM day WHERE day_id = :dayId") 70 | fun getDayWithExercisesByDayId(dayId: String): Flow 71 | 72 | @Query("DELETE FROM routine WHERE routine_id = :routineId") 73 | suspend fun removeRoutineById(routineId: String) 74 | 75 | @Query("DELETE FROM routine") 76 | suspend fun removeAllRoutines() 77 | 78 | } -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/data/database/dao/SharedRoutineDao.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight.data.database.dao 2 | 3 | import androidx.paging.PagingSource 4 | import androidx.room.* 5 | import com.lateinit.rightweight.data.database.entity.SharedRoutine 6 | import com.lateinit.rightweight.data.database.entity.SharedRoutineDay 7 | import com.lateinit.rightweight.data.database.entity.SharedRoutineExercise 8 | import com.lateinit.rightweight.data.database.entity.SharedRoutineExerciseSet 9 | import com.lateinit.rightweight.data.database.intermediate.SharedRoutineDayWithExercises 10 | import com.lateinit.rightweight.data.database.intermediate.SharedRoutineWithDays 11 | import kotlinx.coroutines.flow.Flow 12 | 13 | @Dao 14 | interface SharedRoutineDao { 15 | @Insert(onConflict = OnConflictStrategy.REPLACE) 16 | suspend fun insertSharedRoutine( 17 | sharedRoutine: SharedRoutine, 18 | ) 19 | 20 | @Insert(onConflict = OnConflictStrategy.REPLACE) 21 | suspend fun insertSharedRoutineDetail( 22 | days: List, 23 | exercises: List, 24 | sets: List 25 | ) 26 | 27 | @Query("DELETE FROM shared_routine") 28 | fun removeAllSharedRoutines() 29 | 30 | @Query("SELECT * FROM shared_routine") 31 | fun getAllSharedRoutinesByPaging(): PagingSource 32 | 33 | @Query("SELECT * FROM shared_routine") 34 | fun getAllSharedRoutines(): List 35 | 36 | @Transaction 37 | @Query("SELECT * FROM shared_routine WHERE routine_id = :routineId") 38 | fun getSharedRoutineWithDaysByRoutineId(routineId: String): Flow 39 | } -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/data/database/entity/Day.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight.data.database.entity 2 | 3 | import androidx.room.ColumnInfo 4 | import androidx.room.Entity 5 | import androidx.room.ForeignKey 6 | import androidx.room.ForeignKey.CASCADE 7 | import androidx.room.PrimaryKey 8 | 9 | @Entity( 10 | tableName = "day", 11 | foreignKeys = [ 12 | ForeignKey( 13 | entity = Routine::class, 14 | parentColumns = ["routine_id"], 15 | childColumns = ["routine_id"], 16 | onDelete = CASCADE 17 | ) 18 | ] 19 | ) 20 | data class Day( 21 | @PrimaryKey 22 | @ColumnInfo(name = "day_id") 23 | val dayId: String, 24 | @ColumnInfo(name = "routine_id") 25 | val routineId: String, 26 | @ColumnInfo(name = "order") 27 | val order: Int 28 | ) -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/data/database/entity/Exercise.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight.data.database.entity 2 | 3 | import androidx.room.ColumnInfo 4 | import androidx.room.Entity 5 | import androidx.room.ForeignKey 6 | import androidx.room.ForeignKey.CASCADE 7 | import androidx.room.PrimaryKey 8 | import com.lateinit.rightweight.data.model.local.ExercisePartType 9 | 10 | @Entity( 11 | tableName = "exercise", 12 | foreignKeys = [ 13 | ForeignKey( 14 | entity = Day::class, 15 | parentColumns = ["day_id"], 16 | childColumns = ["day_id"], 17 | onDelete = CASCADE 18 | ) 19 | ] 20 | ) 21 | data class Exercise( 22 | @PrimaryKey 23 | @ColumnInfo(name = "exercise_id") 24 | val exerciseId: String, 25 | @ColumnInfo(name = "day_id") 26 | val dayId: String, 27 | @ColumnInfo(name = "title") 28 | val title: String, 29 | @ColumnInfo(name = "order") 30 | val order: Int, 31 | @ColumnInfo(name = "part") 32 | val part: ExercisePartType 33 | ) 34 | -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/data/database/entity/ExerciseSet.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight.data.database.entity 2 | 3 | import androidx.room.ColumnInfo 4 | import androidx.room.Entity 5 | import androidx.room.ForeignKey 6 | import androidx.room.ForeignKey.CASCADE 7 | import androidx.room.PrimaryKey 8 | 9 | @Entity( 10 | tableName = "exercise_set", 11 | foreignKeys = [ 12 | ForeignKey( 13 | entity = Exercise::class, 14 | parentColumns = ["exercise_id"], 15 | childColumns = ["exercise_id"], 16 | onDelete = CASCADE 17 | ) 18 | ] 19 | ) 20 | data class ExerciseSet( 21 | @PrimaryKey 22 | @ColumnInfo(name = "set_id") 23 | val setId: String, 24 | @ColumnInfo(name = "exercise_id") 25 | val exerciseId: String, 26 | @ColumnInfo(name = "weight") 27 | val weight: String, 28 | @ColumnInfo(name = "count") 29 | val count: String, 30 | @ColumnInfo(name = "order") 31 | val order: Int 32 | ) -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/data/database/entity/History.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight.data.database.entity 2 | 3 | import androidx.room.ColumnInfo 4 | import androidx.room.Entity 5 | import androidx.room.PrimaryKey 6 | import java.time.LocalDate 7 | 8 | @Entity(tableName = "history") 9 | data class History( 10 | @PrimaryKey 11 | @ColumnInfo(name = "history_id") 12 | val historyId: String, 13 | @ColumnInfo(name = "date") 14 | val date: LocalDate, 15 | @ColumnInfo(name = "time") 16 | val time: String, 17 | @ColumnInfo(name = "routine_title") 18 | val routineTitle: String, 19 | @ColumnInfo(name = "day_order") 20 | val dayOrder: Int, 21 | @ColumnInfo(name = "completed") 22 | val completed: Boolean 23 | ) -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/data/database/entity/HistoryExercise.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight.data.database.entity 2 | 3 | import androidx.room.ColumnInfo 4 | import androidx.room.Entity 5 | import androidx.room.ForeignKey 6 | import androidx.room.PrimaryKey 7 | import com.lateinit.rightweight.data.model.local.ExercisePartType 8 | 9 | @Entity( 10 | tableName = "history_exercise", 11 | foreignKeys = [ 12 | ForeignKey( 13 | entity = History::class, 14 | parentColumns = ["history_id"], 15 | childColumns = ["history_id"], 16 | onDelete = ForeignKey.CASCADE 17 | ) 18 | ] 19 | ) 20 | data class HistoryExercise( 21 | @PrimaryKey 22 | @ColumnInfo(name = "exercise_id") 23 | val exerciseId: String, 24 | @ColumnInfo(name = "history_id") 25 | val historyId: String, 26 | @ColumnInfo(name = "title") 27 | val title: String, 28 | @ColumnInfo(name = "order") 29 | val order: Int, 30 | @ColumnInfo(name = "part") 31 | val part: ExercisePartType 32 | ) -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/data/database/entity/HistorySet.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight.data.database.entity 2 | 3 | import androidx.room.ColumnInfo 4 | import androidx.room.Entity 5 | import androidx.room.ForeignKey 6 | import androidx.room.PrimaryKey 7 | 8 | @Entity( 9 | tableName = "history_set", 10 | foreignKeys = [ 11 | ForeignKey( 12 | entity = HistoryExercise::class, 13 | parentColumns = ["exercise_id"], 14 | childColumns = ["exercise_id"], 15 | onDelete = ForeignKey.CASCADE 16 | ) 17 | ] 18 | ) 19 | data class HistorySet( 20 | @PrimaryKey 21 | @ColumnInfo(name = "set_id") 22 | val setId: String, 23 | @ColumnInfo(name = "exercise_id") 24 | val exerciseId: String, 25 | @ColumnInfo(name = "weight") 26 | val weight: String, 27 | @ColumnInfo(name = "count") 28 | val count: String, 29 | @ColumnInfo(name = "order") 30 | val order: Int, 31 | @ColumnInfo(name = "checked") 32 | val checked: Boolean 33 | ) 34 | -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/data/database/entity/Routine.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight.data.database.entity 2 | 3 | import androidx.room.ColumnInfo 4 | import androidx.room.Entity 5 | import androidx.room.PrimaryKey 6 | import java.time.LocalDateTime 7 | 8 | @Entity(tableName = "routine") 9 | data class Routine( 10 | @PrimaryKey 11 | @ColumnInfo(name = "routine_id") 12 | val routineId: String, 13 | @ColumnInfo(name = "title") 14 | val title: String, 15 | @ColumnInfo(name = "author") 16 | val author: String, 17 | @ColumnInfo(name = "description") 18 | val description: String, 19 | @ColumnInfo(name = "modified_date") 20 | val modifiedDate: LocalDateTime, 21 | @ColumnInfo(name = "order") 22 | val order: Int 23 | ) 24 | -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/data/database/entity/SharedRoutine.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight.data.database.entity 2 | 3 | import androidx.room.ColumnInfo 4 | import androidx.room.Entity 5 | import androidx.room.PrimaryKey 6 | import java.time.LocalDateTime 7 | 8 | @Entity(tableName = "shared_routine") 9 | data class SharedRoutine( 10 | @PrimaryKey 11 | @ColumnInfo(name = "routine_id") 12 | val routineId: String, 13 | @ColumnInfo(name = "title") 14 | val title: String, 15 | @ColumnInfo(name = "author") 16 | val author: String, 17 | @ColumnInfo(name = "description") 18 | val description: String, 19 | @ColumnInfo(name = "modified_date") 20 | val modifiedDate: LocalDateTime, 21 | @ColumnInfo(name = "shared_count") 22 | val sharedCount: String 23 | ) -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/data/database/entity/SharedRoutineDay.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight.data.database.entity 2 | 3 | import androidx.room.ColumnInfo 4 | import androidx.room.Entity 5 | import androidx.room.ForeignKey 6 | import androidx.room.PrimaryKey 7 | 8 | @Entity( 9 | tableName = "shared_routine_day", 10 | foreignKeys = [ 11 | ForeignKey( 12 | entity = SharedRoutine::class, 13 | parentColumns = ["routine_id"], 14 | childColumns = ["routine_id"], 15 | onDelete = ForeignKey.CASCADE 16 | ) 17 | ] 18 | ) 19 | data class SharedRoutineDay( 20 | @PrimaryKey 21 | @ColumnInfo(name = "day_id") 22 | val dayId: String, 23 | @ColumnInfo(name = "routine_id") 24 | val routineId: String, 25 | @ColumnInfo(name = "order") 26 | val order: Int 27 | ) -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/data/database/entity/SharedRoutineExercise.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight.data.database.entity 2 | 3 | import androidx.room.ColumnInfo 4 | import androidx.room.Entity 5 | import androidx.room.ForeignKey 6 | import androidx.room.PrimaryKey 7 | import com.lateinit.rightweight.data.model.local.ExercisePartType 8 | 9 | @Entity( 10 | tableName = "shared_routine_exercise", 11 | foreignKeys = [ 12 | ForeignKey( 13 | entity = SharedRoutineDay::class, 14 | parentColumns = ["day_id"], 15 | childColumns = ["day_id"], 16 | onDelete = ForeignKey.CASCADE 17 | ) 18 | ] 19 | ) 20 | data class SharedRoutineExercise( 21 | @PrimaryKey 22 | @ColumnInfo(name = "exercise_id") 23 | val exerciseId: String, 24 | @ColumnInfo(name = "day_id") 25 | val dayId: String, 26 | @ColumnInfo(name = "title") 27 | val title: String, 28 | @ColumnInfo(name = "order") 29 | val order: Int, 30 | @ColumnInfo(name = "part") 31 | val part: ExercisePartType 32 | ) -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/data/database/entity/SharedRoutineExerciseSet.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight.data.database.entity 2 | 3 | import androidx.room.ColumnInfo 4 | import androidx.room.Entity 5 | import androidx.room.ForeignKey 6 | import androidx.room.PrimaryKey 7 | 8 | @Entity( 9 | tableName = "shared_routine_exercise_set", 10 | foreignKeys = [ 11 | ForeignKey( 12 | entity = SharedRoutineExercise::class, 13 | parentColumns = ["exercise_id"], 14 | childColumns = ["exercise_id"], 15 | onDelete = ForeignKey.CASCADE 16 | ) 17 | ] 18 | ) 19 | data class SharedRoutineExerciseSet( 20 | @PrimaryKey 21 | @ColumnInfo(name = "set_id") 22 | val setId: String, 23 | @ColumnInfo(name = "exercise_id") 24 | val exerciseId: String, 25 | @ColumnInfo(name = "weight") 26 | val weight: String, 27 | @ColumnInfo(name = "count") 28 | val count: String, 29 | @ColumnInfo(name = "order") 30 | val order: Int 31 | ) -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/data/database/intermediate/DayWithExercises.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight.data.database.intermediate 2 | 3 | import androidx.room.Embedded 4 | import androidx.room.Relation 5 | import com.lateinit.rightweight.data.database.entity.Day 6 | import com.lateinit.rightweight.data.database.entity.Exercise 7 | 8 | data class DayWithExercises( 9 | @Embedded val day: Day, 10 | 11 | @Relation( 12 | entity = Exercise::class, 13 | parentColumn = "day_id", 14 | entityColumn = "day_id" 15 | ) 16 | val exercises: List 17 | ) -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/data/database/intermediate/ExerciseWithSets.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight.data.database.intermediate 2 | 3 | import androidx.room.Embedded 4 | import androidx.room.Relation 5 | import com.lateinit.rightweight.data.database.entity.Exercise 6 | import com.lateinit.rightweight.data.database.entity.ExerciseSet 7 | 8 | data class ExerciseWithSets( 9 | @Embedded val exercise: Exercise, 10 | 11 | @Relation( 12 | entity = ExerciseSet::class, 13 | parentColumn = "exercise_id", 14 | entityColumn = "exercise_id" 15 | ) 16 | val sets: List 17 | ) 18 | 19 | -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/data/database/intermediate/HistoryExerciseWithHistorySets.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight.data.database.intermediate 2 | 3 | import androidx.room.Embedded 4 | import androidx.room.Relation 5 | import com.lateinit.rightweight.data.database.entity.HistoryExercise 6 | import com.lateinit.rightweight.data.database.entity.HistorySet 7 | 8 | data class HistoryExerciseWithHistorySets( 9 | @Embedded val historyExercise: HistoryExercise, 10 | 11 | @Relation( 12 | entity = HistorySet::class, 13 | parentColumn = "exercise_id", 14 | entityColumn = "exercise_id" 15 | ) 16 | val historySets: List 17 | ) 18 | -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/data/database/intermediate/HistoryWithHistoryExercises.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight.data.database.intermediate 2 | 3 | import androidx.room.Embedded 4 | import androidx.room.Relation 5 | import com.lateinit.rightweight.data.database.entity.History 6 | import com.lateinit.rightweight.data.database.entity.HistoryExercise 7 | 8 | data class HistoryWithHistoryExercises( 9 | @Embedded val history: History, 10 | 11 | @Relation( 12 | entity = HistoryExercise::class, 13 | parentColumn = "history_id", 14 | entityColumn = "history_id" 15 | ) 16 | val historyExercises: List 17 | ) 18 | -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/data/database/intermediate/RoutineWithDays.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight.data.database.intermediate 2 | 3 | import androidx.room.Embedded 4 | import androidx.room.Relation 5 | import com.lateinit.rightweight.data.database.entity.Day 6 | import com.lateinit.rightweight.data.database.entity.Routine 7 | 8 | data class RoutineWithDays( 9 | @Embedded val routine: Routine, 10 | 11 | @Relation( 12 | entity = Day::class, 13 | parentColumn = "routine_id", 14 | entityColumn = "routine_id" 15 | ) 16 | val days: List 17 | ) 18 | -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/data/database/intermediate/SharedRoutineDayWithExercises.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight.data.database.intermediate 2 | 3 | import androidx.room.Embedded 4 | import androidx.room.Relation 5 | import com.lateinit.rightweight.data.database.entity.* 6 | 7 | data class SharedRoutineDayWithExercises( 8 | @Embedded val day: SharedRoutineDay, 9 | 10 | @Relation( 11 | entity = SharedRoutineExercise::class, 12 | parentColumn = "day_id", 13 | entityColumn = "day_id" 14 | ) 15 | val exercises: List 16 | ) -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/data/database/intermediate/SharedRoutineExerciseWithExerciseSets.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight.data.database.intermediate 2 | 3 | import androidx.room.Embedded 4 | import androidx.room.Relation 5 | import com.lateinit.rightweight.data.database.entity.SharedRoutineDay 6 | import com.lateinit.rightweight.data.database.entity.SharedRoutineExercise 7 | import com.lateinit.rightweight.data.database.entity.SharedRoutineExerciseSet 8 | 9 | data class SharedRoutineExerciseWithExerciseSets( 10 | @Embedded val exercise: SharedRoutineExercise, 11 | 12 | @Relation( 13 | entity = SharedRoutineExerciseSet::class, 14 | parentColumn = "exercise_id", 15 | entityColumn = "exercise_id" 16 | ) 17 | val sets: List 18 | ) -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/data/database/intermediate/SharedRoutineWithDays.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight.data.database.intermediate 2 | 3 | import androidx.room.Embedded 4 | import androidx.room.Relation 5 | import com.lateinit.rightweight.data.database.entity.SharedRoutine 6 | import com.lateinit.rightweight.data.database.entity.SharedRoutineDay 7 | 8 | data class SharedRoutineWithDays( 9 | @Embedded val routine: SharedRoutine, 10 | 11 | @Relation( 12 | entity = SharedRoutineDay::class, 13 | parentColumn = "routine_id", 14 | entityColumn = "routine_id" 15 | ) 16 | val days: List 17 | ) 18 | -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/data/datasource/local/HistoryLocalDataSource.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight.data.datasource.local 2 | 3 | import com.lateinit.rightweight.data.database.entity.Day 4 | import com.lateinit.rightweight.data.database.entity.Exercise 5 | import com.lateinit.rightweight.data.database.entity.ExerciseSet 6 | import com.lateinit.rightweight.data.database.entity.History 7 | import com.lateinit.rightweight.data.database.entity.HistoryExercise 8 | import com.lateinit.rightweight.data.database.entity.HistorySet 9 | import com.lateinit.rightweight.data.database.intermediate.HistoryWithHistoryExercises 10 | import kotlinx.coroutines.flow.Flow 11 | import java.time.LocalDate 12 | 13 | interface HistoryLocalDataSource { 14 | 15 | suspend fun insertHistory( 16 | routineId: String, 17 | day: Day, 18 | routineTitle: String, 19 | exercises: List, 20 | exerciseSets: List 21 | ) 22 | 23 | suspend fun restoreHistory( 24 | histories: List, 25 | exercises: List, 26 | sets: List 27 | ) 28 | 29 | suspend fun insertHistorySet(historyExerciseId: String) 30 | 31 | suspend fun insertHistoryExercise(historyId: String) 32 | 33 | suspend fun getHistoryAfterDate(startDate: LocalDate): List 34 | 35 | fun getHistoryByDate(localDate: LocalDate): Flow 36 | 37 | fun getHistoryWithHistoryExercisesByDate(localDate: LocalDate): Flow 38 | 39 | fun getHistoryBetweenDate( 40 | startDate: LocalDate, 41 | endDate: LocalDate 42 | ): Flow> 43 | 44 | suspend fun updateHistory(history: History) 45 | 46 | suspend fun updateHistorySet(historySet: HistorySet) 47 | 48 | suspend fun updateHistoryExercise(historyExercise: HistoryExercise) 49 | 50 | suspend fun removeHistorySet(historySetId: String) 51 | 52 | suspend fun removeHistoryExercise(historyExerciseId: String) 53 | 54 | suspend fun removeAllHistories() 55 | 56 | suspend fun removeUncheckedHistorySet() 57 | 58 | suspend fun removeUncheckedHistoryExercise() 59 | } -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/data/datasource/local/RoutineLocalDataSource.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight.data.datasource.local 2 | 3 | import com.lateinit.rightweight.data.database.entity.Day 4 | import com.lateinit.rightweight.data.database.entity.Exercise 5 | import com.lateinit.rightweight.data.database.entity.ExerciseSet 6 | import com.lateinit.rightweight.data.database.entity.Routine 7 | import com.lateinit.rightweight.data.database.intermediate.DayWithExercises 8 | import com.lateinit.rightweight.data.database.intermediate.RoutineWithDays 9 | import kotlinx.coroutines.flow.Flow 10 | 11 | interface RoutineLocalDataSource { 12 | 13 | suspend fun insertRoutine( 14 | routine: Routine, 15 | days: List, 16 | exercises: List, 17 | sets: List 18 | ) 19 | 20 | suspend fun restoreRoutines( 21 | routines: List, 22 | days: List, 23 | exercises: List, 24 | sets: List 25 | ) 26 | 27 | suspend fun getRoutineById(routineId: String): Routine 28 | 29 | suspend fun getHigherRoutineOrder(): Int? 30 | 31 | suspend fun getDayById(dayId: String): Day 32 | 33 | suspend fun getDaysByRoutineId(routineId: String): List 34 | 35 | suspend fun getExercisesByDayId(dayId: String): List 36 | 37 | suspend fun getSetsByExerciseId(exerciseId: String): List 38 | 39 | suspend fun getRoutineWithDaysByRoutineId(routineId: String): RoutineWithDays 40 | 41 | suspend fun getAllRoutineWithDays(): List 42 | 43 | fun getAllRoutines(): Flow> 44 | 45 | fun getDayWithExercisesByDayId(dayId: String): Flow 46 | 47 | suspend fun updateRoutines(routines: List) 48 | 49 | suspend fun removeRoutineById(routineId: String) 50 | 51 | suspend fun removeAllRoutines() 52 | } -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/data/datasource/local/SharedRoutineLocalDataSource.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight.data.datasource.local 2 | 3 | import com.lateinit.rightweight.data.database.entity.SharedRoutineDay 4 | import com.lateinit.rightweight.data.database.entity.SharedRoutineExercise 5 | import com.lateinit.rightweight.data.database.entity.SharedRoutineExerciseSet 6 | import com.lateinit.rightweight.data.database.intermediate.SharedRoutineWithDays 7 | import kotlinx.coroutines.flow.Flow 8 | 9 | interface SharedRoutineLocalDataSource { 10 | suspend fun insertSharedRoutineDetail( 11 | days: List, 12 | exercises: List, 13 | sets: List 14 | ) 15 | 16 | fun getSharedRoutineWithDaysByRoutineId(routineId: String): Flow 17 | } -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/data/datasource/local/UserLocalDataSource.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight.data.datasource.local 2 | 3 | import com.lateinit.rightweight.data.model.local.User 4 | import kotlinx.coroutines.flow.Flow 5 | 6 | interface UserLocalDataSource { 7 | 8 | suspend fun saveUser(user: User) 9 | 10 | fun getUser(): Flow 11 | 12 | suspend fun removeUserInfo() 13 | } -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/data/datasource/local/impl/RoutineLocalDataSourceImpl.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight.data.datasource.local.impl 2 | 3 | import com.lateinit.rightweight.data.database.dao.RoutineDao 4 | import com.lateinit.rightweight.data.database.entity.Day 5 | import com.lateinit.rightweight.data.database.entity.Exercise 6 | import com.lateinit.rightweight.data.database.entity.ExerciseSet 7 | import com.lateinit.rightweight.data.database.entity.Routine 8 | import com.lateinit.rightweight.data.database.intermediate.DayWithExercises 9 | import com.lateinit.rightweight.data.database.intermediate.RoutineWithDays 10 | import com.lateinit.rightweight.data.datasource.local.RoutineLocalDataSource 11 | import kotlinx.coroutines.flow.Flow 12 | import javax.inject.Inject 13 | 14 | class RoutineLocalDataSourceImpl @Inject constructor( 15 | private val routineDao: RoutineDao 16 | ) : RoutineLocalDataSource { 17 | 18 | override suspend fun insertRoutine( 19 | routine: Routine, 20 | days: List, 21 | exercises: List, 22 | sets: List 23 | ) { 24 | routineDao.insertRoutine(routine, days, exercises, sets) 25 | } 26 | 27 | override suspend fun restoreRoutines( 28 | routines: List, 29 | days: List, 30 | exercises: List, 31 | sets: List 32 | ) { 33 | routineDao.restoreRoutine(routines, days, exercises, sets) 34 | } 35 | 36 | override suspend fun updateRoutines(routines: List) { 37 | routineDao.updateRoutines(routines) 38 | } 39 | 40 | override suspend fun getHigherRoutineOrder(): Int? { 41 | return routineDao.getHigherRoutineOrder() 42 | } 43 | 44 | override suspend fun getRoutineById(routineId: String): Routine { 45 | return routineDao.getRoutineById(routineId) 46 | } 47 | 48 | override suspend fun getDayById(dayId: String): Day { 49 | return routineDao.getDayById(dayId) 50 | } 51 | 52 | override suspend fun getDaysByRoutineId(routineId: String): List { 53 | return routineDao.getDaysByRoutineId(routineId) 54 | } 55 | 56 | override suspend fun getExercisesByDayId(dayId: String): List { 57 | return routineDao.getExercisesByDayId(dayId) 58 | } 59 | 60 | override suspend fun getSetsByExerciseId(exerciseId: String): List { 61 | return routineDao.getSetsByExerciseId(exerciseId) 62 | } 63 | 64 | override suspend fun getRoutineWithDaysByRoutineId(routineId: String): RoutineWithDays { 65 | return routineDao.getRoutineWithDaysByRoutineId(routineId) 66 | } 67 | 68 | override fun getAllRoutines(): Flow> { 69 | return routineDao.getAllRoutines() 70 | } 71 | 72 | override suspend fun getAllRoutineWithDays(): List { 73 | return routineDao.getAllRoutineWithDays() 74 | } 75 | 76 | override fun getDayWithExercisesByDayId(dayId: String): Flow { 77 | return routineDao.getDayWithExercisesByDayId(dayId) 78 | } 79 | 80 | override suspend fun removeRoutineById(routineId: String) { 81 | return routineDao.removeRoutineById(routineId) 82 | } 83 | 84 | override suspend fun removeAllRoutines() { 85 | routineDao.removeAllRoutines() 86 | } 87 | } -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/data/datasource/local/impl/SharedRoutineLocalDataSourceImpl.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight.data.datasource.local.impl 2 | 3 | import com.lateinit.rightweight.data.database.dao.SharedRoutineDao 4 | import com.lateinit.rightweight.data.database.entity.SharedRoutineDay 5 | import com.lateinit.rightweight.data.database.entity.SharedRoutineExercise 6 | import com.lateinit.rightweight.data.database.entity.SharedRoutineExerciseSet 7 | import com.lateinit.rightweight.data.database.intermediate.SharedRoutineWithDays 8 | import com.lateinit.rightweight.data.datasource.local.SharedRoutineLocalDataSource 9 | import kotlinx.coroutines.flow.Flow 10 | import javax.inject.Inject 11 | 12 | class SharedRoutineLocalDataSourceImpl @Inject constructor( 13 | private val sharedRoutineDao: SharedRoutineDao 14 | ): SharedRoutineLocalDataSource { 15 | override suspend fun insertSharedRoutineDetail( 16 | days: List, 17 | exercises: List, 18 | sets: List 19 | ) { 20 | sharedRoutineDao.insertSharedRoutineDetail(days, exercises, sets) 21 | } 22 | 23 | override fun getSharedRoutineWithDaysByRoutineId(routineId: String): Flow { 24 | return sharedRoutineDao.getSharedRoutineWithDaysByRoutineId(routineId) 25 | } 26 | } -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/data/datasource/local/impl/UserLocalDataSourceImpl.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight.data.datasource.local.impl 2 | 3 | import com.lateinit.rightweight.data.dataStore.AppPreferencesDataStore 4 | import com.lateinit.rightweight.data.datasource.local.UserLocalDataSource 5 | import com.lateinit.rightweight.data.model.local.User 6 | import kotlinx.coroutines.flow.Flow 7 | import javax.inject.Inject 8 | 9 | class UserLocalDataSourceImpl @Inject constructor( 10 | private val appPreferencesDataStore: AppPreferencesDataStore 11 | ) : UserLocalDataSource { 12 | 13 | override suspend fun saveUser(user: User) { 14 | appPreferencesDataStore.saveUser(user) 15 | } 16 | 17 | override fun getUser(): Flow { 18 | return appPreferencesDataStore.userInfo 19 | } 20 | 21 | override suspend fun removeUserInfo() { 22 | appPreferencesDataStore.deleteUserInfo() 23 | } 24 | } -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/data/datasource/remote/HistoryRemoteDatasource.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight.data.datasource.remote 2 | 3 | import com.lateinit.rightweight.data.model.remote.WriteModelData 4 | import com.lateinit.rightweight.data.database.entity.History 5 | import com.lateinit.rightweight.data.database.entity.HistoryExercise 6 | import com.lateinit.rightweight.data.database.entity.HistorySet 7 | import java.time.LocalDate 8 | 9 | interface HistoryRemoteDatasource { 10 | suspend fun getLatestHistoryDate(userId: String): LocalDate 11 | 12 | suspend fun commitTransaction(writes: List) 13 | 14 | suspend fun getHistories(path: String): List 15 | 16 | suspend fun getHistoryExercises(path: String): List 17 | 18 | suspend fun getHistoryExerciseSets(path: String): List 19 | } -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/data/datasource/remote/LoginDataSource.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight.data.datasource.remote 2 | 3 | import com.lateinit.rightweight.data.model.remote.LoginResponse 4 | 5 | interface LoginDataSource { 6 | 7 | suspend fun login(key: String, token: String): LoginResponse 8 | 9 | suspend fun deleteAccount(key: String, idToken: String) 10 | } -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/data/datasource/remote/RoutineRemoteDataSource.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight.data.datasource.remote 2 | 3 | import com.lateinit.rightweight.data.database.entity.Day 4 | import com.lateinit.rightweight.data.database.entity.Exercise 5 | import com.lateinit.rightweight.data.database.entity.ExerciseSet 6 | import com.lateinit.rightweight.data.database.entity.Routine 7 | import com.lateinit.rightweight.data.model.remote.DocumentResponse 8 | import com.lateinit.rightweight.data.remote.model.RoutineField 9 | 10 | interface RoutineRemoteDataSource { 11 | suspend fun getRoutineByUserId(userId: String): List> 12 | 13 | suspend fun getRoutine(routineId: String): Routine 14 | 15 | suspend fun getRoutineDays(routineId: String): List 16 | 17 | suspend fun getRoutineExercises(path: String): List 18 | 19 | suspend fun getRoutineExerciseSets(path: String): List 20 | 21 | suspend fun getChildrenDocumentName(path: String): List 22 | } -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/data/datasource/remote/SharedRoutineRemoteDataSource.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight.data.datasource.remote 2 | 3 | import androidx.paging.PagingData 4 | import com.lateinit.rightweight.data.database.entity.SharedRoutine 5 | import com.lateinit.rightweight.data.database.entity.SharedRoutineDay 6 | import com.lateinit.rightweight.data.database.entity.SharedRoutineExercise 7 | import com.lateinit.rightweight.data.database.entity.SharedRoutineExerciseSet 8 | import com.lateinit.rightweight.data.model.remote.SharedRoutineSortType 9 | import com.lateinit.rightweight.data.model.remote.WriteModelData 10 | import com.lateinit.rightweight.data.remote.model.SharedRoutineField 11 | import kotlinx.coroutines.flow.Flow 12 | 13 | interface SharedRoutineRemoteDataSource { 14 | 15 | suspend fun getSharedRoutine(routineId: String): SharedRoutineField? 16 | 17 | suspend fun getSharedRoutineDays(routineId: String): List 18 | 19 | suspend fun getSharedRoutineExercises( 20 | routineId: String, 21 | dayId: String 22 | ): List 23 | 24 | suspend fun getSharedRoutineExerciseSets( 25 | routineId: String, 26 | dayId: String, 27 | exerciseId: String 28 | ): List 29 | 30 | fun getSharedRoutinesByPaging(): Flow> 31 | 32 | suspend fun setSharedRoutineSortType(sortType: SharedRoutineSortType) 33 | 34 | suspend fun getChildrenDocumentName(path: String): List 35 | 36 | suspend fun commitTransaction(writes: List) 37 | } -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/data/datasource/remote/UserRemoteDataSource.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight.data.datasource.remote 2 | 3 | import com.lateinit.rightweight.data.remote.model.UserInfoField 4 | 5 | interface UserRemoteDataSource { 6 | suspend fun backupUserInfo(userId: String, routineId: String, dayId: String, completedDayId: String) 7 | 8 | suspend fun getChildrenDocumentName(path: String): List 9 | 10 | suspend fun restoreUserInfo(userId: String): UserInfoField? 11 | } -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/data/datasource/remote/impl/HistoryRemoteDataSourceImpl.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight.data.datasource.remote.impl 2 | 3 | import com.lateinit.rightweight.data.api.UserApiService 4 | import com.lateinit.rightweight.data.database.entity.History 5 | import com.lateinit.rightweight.data.database.entity.HistoryExercise 6 | import com.lateinit.rightweight.data.database.entity.HistorySet 7 | import com.lateinit.rightweight.data.datasource.remote.HistoryRemoteDatasource 8 | import com.lateinit.rightweight.data.mapper.toHistory 9 | import com.lateinit.rightweight.data.mapper.toHistoryExercise 10 | import com.lateinit.rightweight.data.mapper.toHistorySet 11 | import com.lateinit.rightweight.data.model.remote.Direction 12 | import com.lateinit.rightweight.data.model.remote.FiledReferenceData 13 | import com.lateinit.rightweight.data.model.remote.FromData 14 | import com.lateinit.rightweight.data.model.remote.OrderByData 15 | import com.lateinit.rightweight.data.model.remote.RunQueryBody 16 | import com.lateinit.rightweight.data.model.remote.StructuredQueryData 17 | import com.lateinit.rightweight.data.model.remote.WriteModelData 18 | import com.lateinit.rightweight.data.model.remote.WriteRequestBody 19 | import java.time.LocalDate 20 | import java.time.format.DateTimeFormatter 21 | import javax.inject.Inject 22 | 23 | class HistoryRemoteDataSourceImpl @Inject constructor( 24 | private val api: UserApiService 25 | ) : HistoryRemoteDatasource { 26 | override suspend fun getLatestHistoryDate(userId: String): LocalDate { 27 | val documentResponseList = api.getLastHistoryDate( 28 | userId, 29 | RunQueryBody( 30 | StructuredQueryData( 31 | from = FromData("history"), 32 | orderBy = listOf( 33 | OrderByData( 34 | FiledReferenceData("date"), 35 | Direction.DESCENDING.toString() 36 | ) 37 | ), 38 | limit = 1 39 | ) 40 | ) 41 | ) 42 | val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss'Z'") 43 | val lastDateTime = documentResponseList.first() 44 | .document?.fields?.date?.value ?: return DEFAULT_LOCAL_DATE 45 | return LocalDate.parse(lastDateTime, formatter) 46 | } 47 | 48 | override suspend fun commitTransaction(writes: List) { 49 | api.commitTransaction(WriteRequestBody(writes)) 50 | } 51 | 52 | override suspend fun getHistories(path: String): List{ 53 | val histories = api.getHistories(path) 54 | return histories.documents?.map { 55 | val historyId = it.name.split("/").last() 56 | it.fields.toHistory(historyId) 57 | } ?: emptyList() 58 | } 59 | 60 | override suspend fun getHistoryExercises(path: String): List { 61 | val exercises = api.getHistoryExercises(path) 62 | return exercises.documents?.map { 63 | val exerciseId = it.name.split("/").last() 64 | it.fields.toHistoryExercise(exerciseId) 65 | } ?: emptyList() 66 | } 67 | 68 | override suspend fun getHistoryExerciseSets(path: String): List { 69 | val exerciseSets = api.getHistoryExerciseSets(path) 70 | return exerciseSets.documents?.map { 71 | val exerciseSetId = it.name.split("/").last() 72 | it.fields.toHistorySet(exerciseSetId) 73 | } ?: emptyList() 74 | } 75 | 76 | companion object { 77 | val DEFAULT_LOCAL_DATE: LocalDate = LocalDate.parse("1990-01-01") 78 | } 79 | } -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/data/datasource/remote/impl/LoginDataSourceImpl.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight.data.datasource.remote.impl 2 | 3 | import com.lateinit.rightweight.data.api.AuthApiService 4 | import com.lateinit.rightweight.data.model.remote.LoginResponse 5 | import com.lateinit.rightweight.data.datasource.remote.LoginDataSource 6 | import com.lateinit.rightweight.data.model.remote.LoginRequestBody 7 | import com.lateinit.rightweight.data.model.remote.PostBody 8 | import javax.inject.Inject 9 | 10 | class LoginDataSourceImpl @Inject constructor( 11 | val api: AuthApiService 12 | ) : LoginDataSource { 13 | 14 | override suspend fun login(key: String, token: String): LoginResponse { 15 | val postBody = PostBody(token, "google.com").toString() 16 | return api.loginToFirebase( 17 | key, LoginRequestBody( 18 | postBody, "http://localhost", 19 | returnIdpCredential = true, 20 | returnSecureToken = true 21 | ) 22 | ) 23 | } 24 | 25 | override suspend fun deleteAccount(key: String, idToken: String) { 26 | api.deleteAccount(key, idToken) 27 | } 28 | } -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/data/datasource/remote/impl/RoutineRemoteDataSourceImpl.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight.data.datasource.remote.impl 2 | 3 | import com.lateinit.rightweight.data.api.RoutineApiService 4 | import com.lateinit.rightweight.data.database.entity.Day 5 | import com.lateinit.rightweight.data.database.entity.Exercise 6 | import com.lateinit.rightweight.data.database.entity.ExerciseSet 7 | import com.lateinit.rightweight.data.database.entity.Routine 8 | import com.lateinit.rightweight.data.datasource.remote.RoutineRemoteDataSource 9 | import com.lateinit.rightweight.data.mapper.local.toDay 10 | import com.lateinit.rightweight.data.mapper.local.toExercise 11 | import com.lateinit.rightweight.data.mapper.local.toExerciseSet 12 | import com.lateinit.rightweight.data.mapper.local.toRoutine 13 | import com.lateinit.rightweight.data.model.remote.DocumentResponse 14 | import com.lateinit.rightweight.data.model.remote.FiledReferenceData 15 | import com.lateinit.rightweight.data.model.remote.FilterData 16 | import com.lateinit.rightweight.data.model.remote.FilterOperator 17 | import com.lateinit.rightweight.data.model.remote.FromData 18 | import com.lateinit.rightweight.data.model.remote.RunQueryBody 19 | import com.lateinit.rightweight.data.model.remote.StructuredQueryData 20 | import com.lateinit.rightweight.data.model.remote.WhereData 21 | import com.lateinit.rightweight.data.remote.model.RoutineField 22 | import com.lateinit.rightweight.data.remote.model.StringValue 23 | import javax.inject.Inject 24 | 25 | class RoutineRemoteDataSourceImpl @Inject constructor( 26 | private val api: RoutineApiService 27 | ) : RoutineRemoteDataSource { 28 | override suspend fun getRoutineByUserId(userId: String): List> { 29 | return api.getUserRoutine( 30 | RunQueryBody( 31 | StructuredQueryData( 32 | from = FromData("routine"), 33 | where = WhereData( 34 | FilterData( 35 | FiledReferenceData("userId"), 36 | FilterOperator.EQUAL.toString(), 37 | StringValue(userId) 38 | ) 39 | ) 40 | ) 41 | ) 42 | ) 43 | } 44 | 45 | override suspend fun getRoutine(routineId: String): Routine { 46 | return api.getRoutine(routineId).fields.toRoutine(routineId) 47 | } 48 | 49 | override suspend fun getRoutineDays(routineId: String): List { 50 | val path = "routine/${routineId}/day" 51 | val days = api.getDays(path) 52 | return days.documents?.map { 53 | val dayId = it.name.split("/").last() 54 | it.fields.toDay(dayId) 55 | } ?: emptyList() 56 | } 57 | 58 | override suspend fun getRoutineExercises(path: String): List { 59 | val exercises = api.getExercises(path) 60 | return exercises.documents?.map { 61 | val exerciseId = it.name.split("/").last() 62 | it.fields.toExercise(exerciseId) 63 | } ?: emptyList() 64 | } 65 | 66 | override suspend fun getRoutineExerciseSets(path: String): List { 67 | val exerciseSets = api.getExerciseSets(path) 68 | return exerciseSets.documents?.map { 69 | val exerciseSetId = it.name.split("/").last() 70 | it.fields.toExerciseSet(exerciseSetId) 71 | } ?: emptyList() 72 | } 73 | 74 | override suspend fun getChildrenDocumentName(path: String): List { 75 | val documentNameList = api.getChildrenDocument(path) 76 | return documentNameList.documents?.map { 77 | it.name.split("/").last() 78 | } ?: emptyList() 79 | } 80 | } -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/data/datasource/remote/impl/UserRemoteDataSourceImpl.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight.data.datasource.remote.impl 2 | 3 | import com.lateinit.rightweight.data.api.UserApiService 4 | import com.lateinit.rightweight.data.datasource.remote.UserRemoteDataSource 5 | import com.lateinit.rightweight.data.remote.model.RootField 6 | import com.lateinit.rightweight.data.remote.model.StringValue 7 | import com.lateinit.rightweight.data.remote.model.UserInfoField 8 | import javax.inject.Inject 9 | 10 | class UserRemoteDataSourceImpl @Inject constructor( 11 | private val api: UserApiService 12 | ) : UserRemoteDataSource { 13 | override suspend fun backupUserInfo(userId: String, routineId: String, dayId: String, completedDayId: String) { 14 | api.backupUserInfo( 15 | userId, RootField( 16 | UserInfoField( 17 | routineId = StringValue(routineId), 18 | dayId = StringValue(dayId), 19 | completedDayId = StringValue(completedDayId) 20 | ) 21 | ) 22 | ) 23 | } 24 | 25 | 26 | override suspend fun restoreUserInfo(userId: String): UserInfoField? { 27 | val response = api.restoreUserInfo(userId) 28 | return if (response.isSuccessful) { 29 | response.body()?.fields 30 | } else null 31 | } 32 | 33 | override suspend fun getChildrenDocumentName(path: String): List { 34 | val documentNameList = api.getChildrenDocumentName(path) 35 | return documentNameList.documents?.map { 36 | it.name.split("/").last() 37 | } ?: emptyList() 38 | } 39 | 40 | } -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/data/mapper/local/ToExercisePartType.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight.data.mapper 2 | 3 | import com.lateinit.rightweight.data.model.local.ExercisePartType 4 | import com.lateinit.rightweight.ui.model.routine.ExercisePartTypeUiModel 5 | 6 | fun ExercisePartTypeUiModel.toExercisePartType(): ExercisePartType { 7 | return when (this) { 8 | ExercisePartTypeUiModel.CHEST -> ExercisePartType.CHEST 9 | ExercisePartTypeUiModel.BACK -> ExercisePartType.BACK 10 | ExercisePartTypeUiModel.LEG -> ExercisePartType.LEG 11 | ExercisePartTypeUiModel.SHOULDER -> ExercisePartType.SHOULDER 12 | ExercisePartTypeUiModel.BICEPS -> ExercisePartType.BICEPS 13 | ExercisePartTypeUiModel.TRICEPS -> ExercisePartType.TRICEPS 14 | ExercisePartTypeUiModel.CORE -> ExercisePartType.CORE 15 | ExercisePartTypeUiModel.FOREARM -> ExercisePartType.FOREARM 16 | ExercisePartTypeUiModel.CARDIO -> ExercisePartType.CARDIO 17 | } 18 | } -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/data/mapper/local/ToHistory.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight.data.mapper 2 | 3 | import com.lateinit.rightweight.data.database.entity.History 4 | import com.lateinit.rightweight.data.database.entity.HistoryExercise 5 | import com.lateinit.rightweight.data.database.entity.HistorySet 6 | import com.lateinit.rightweight.data.model.local.ExercisePartType 7 | import com.lateinit.rightweight.data.remote.model.HistoryExerciseField 8 | import com.lateinit.rightweight.data.remote.model.HistoryExerciseSetField 9 | import com.lateinit.rightweight.data.remote.model.HistoryField 10 | import com.lateinit.rightweight.ui.model.history.HistoryExerciseSetUiModel 11 | import com.lateinit.rightweight.ui.model.history.HistoryExerciseUiModel 12 | import com.lateinit.rightweight.ui.model.history.HistoryUiModel 13 | import com.lateinit.rightweight.util.DEFAULT_SET_COUNT 14 | import com.lateinit.rightweight.util.DEFAULT_SET_WEIGHT 15 | import java.time.LocalDate 16 | import java.time.format.DateTimeFormatter 17 | 18 | fun HistoryUiModel.toHistory(): History { 19 | return History( 20 | historyId = historyId, 21 | date = date, 22 | time = time, 23 | routineTitle = routineTitle, 24 | dayOrder = order, 25 | completed = completed 26 | ) 27 | } 28 | 29 | fun HistoryExerciseUiModel.toHistoryExercise(): HistoryExercise { 30 | return HistoryExercise( 31 | exerciseId = exerciseId, 32 | historyId = historyId, 33 | title = title, 34 | order = order, 35 | part = part.toExercisePartType() 36 | ) 37 | } 38 | 39 | fun HistoryExerciseSetUiModel.toHistorySet(): HistorySet { 40 | return HistorySet( 41 | setId = setId, 42 | exerciseId = exerciseId, 43 | weight = weight.ifEmpty { DEFAULT_SET_WEIGHT }, 44 | count = count.ifEmpty { DEFAULT_SET_COUNT }, 45 | order = order, 46 | checked = checked 47 | ) 48 | } 49 | 50 | fun HistoryField.toHistory(historyId: String): History { 51 | val refinedDateString = date.value 52 | val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss'Z'") 53 | val date = LocalDate.parse(refinedDateString, formatter) 54 | 55 | return History( 56 | historyId = historyId, 57 | date = date, 58 | time = time.value, 59 | routineTitle = routineTitle.value, 60 | dayOrder = order.value.toInt(), 61 | completed = true 62 | ) 63 | } 64 | 65 | fun HistoryExerciseField.toHistoryExercise(exerciseId: String): HistoryExercise { 66 | return HistoryExercise( 67 | exerciseId = exerciseId, 68 | historyId = historyId.value, 69 | title = title.value, 70 | order = order.value.toInt(), 71 | part = ExercisePartType.valueOf(part.value) 72 | ) 73 | } 74 | 75 | fun HistoryExerciseSetField.toHistorySet(exerciseSetId: String): HistorySet { 76 | return HistorySet( 77 | setId = exerciseSetId, 78 | exerciseId = exerciseId.value, 79 | weight = weight.value, 80 | count = count.value, 81 | order = order.value.toInt(), 82 | checked = true 83 | ) 84 | } -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/data/mapper/local/ToSharedRoutine.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight.data.mapper 2 | 3 | import com.lateinit.rightweight.data.database.entity.SharedRoutineDay 4 | import com.lateinit.rightweight.data.database.entity.SharedRoutineExercise 5 | import com.lateinit.rightweight.data.database.entity.SharedRoutineExerciseSet 6 | import com.lateinit.rightweight.data.model.local.ExercisePartType 7 | import com.lateinit.rightweight.data.model.remote.DetailResponse 8 | import com.lateinit.rightweight.data.remote.model.DayField 9 | import com.lateinit.rightweight.data.remote.model.ExerciseField 10 | import com.lateinit.rightweight.data.remote.model.ExerciseSetField 11 | 12 | fun DetailResponse.toSharedRoutineDay(): SharedRoutineDay { 13 | val splitedName = name.split("/") 14 | return SharedRoutineDay( 15 | routineId = fields.routineId.value.toString(), 16 | dayId = splitedName.last(), 17 | order = fields.order.value.toString().toInt() 18 | ) 19 | } 20 | 21 | fun DetailResponse.toSharedRoutineExercise(): SharedRoutineExercise { 22 | val splitedName = name.split("/") 23 | return SharedRoutineExercise( 24 | dayId = fields.dayId.value.toString(), 25 | exerciseId = splitedName.last(), 26 | title = fields.title.value.toString(), 27 | order = fields.order.value.toString().toInt(), 28 | part = ExercisePartType.valueOf(fields.partType.value.toString()) 29 | ) 30 | } 31 | 32 | fun DetailResponse.toSharedRoutineExerciseSet(): SharedRoutineExerciseSet { 33 | val splitedName = name.split("/") 34 | return SharedRoutineExerciseSet( 35 | exerciseId = fields.exerciseId.value.toString(), 36 | setId = splitedName.last(), 37 | weight = fields.weight.value.toString(), 38 | count = fields.count.value.toString(), 39 | order = fields.order.value.toString().toInt() 40 | ) 41 | } -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/data/mapper/local/ToSharedRoutineSortType.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight.data.mapper.local 2 | 3 | import com.lateinit.rightweight.data.model.remote.SharedRoutineSortType 4 | import com.lateinit.rightweight.ui.model.shared.SharedRoutineSortTypeUiModel 5 | 6 | fun SharedRoutineSortTypeUiModel.toSharedRoutineSortType(): SharedRoutineSortType { 7 | return when (this) { 8 | SharedRoutineSortTypeUiModel.MODIFIED_DATE_FIRST -> SharedRoutineSortType.MODIFIED_DATE_FIRST 9 | SharedRoutineSortTypeUiModel.SHARED_COUNT_FIRST -> SharedRoutineSortType.SHARED_COUNT_FIRST 10 | } 11 | } -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/data/mapper/remote/ToHisttoryField.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight.data.mapper.remote 2 | 3 | import com.lateinit.rightweight.data.remote.model.HistoryExerciseField 4 | import com.lateinit.rightweight.data.remote.model.HistoryExerciseSetField 5 | import com.lateinit.rightweight.data.remote.model.HistoryField 6 | import com.lateinit.rightweight.data.remote.model.IntValue 7 | import com.lateinit.rightweight.data.remote.model.StringValue 8 | import com.lateinit.rightweight.data.remote.model.TimeStampValue 9 | import com.lateinit.rightweight.ui.model.history.HistoryExerciseSetUiModel 10 | import com.lateinit.rightweight.ui.model.history.HistoryExerciseUiModel 11 | import com.lateinit.rightweight.ui.model.history.HistoryUiModel 12 | 13 | fun HistoryUiModel.toHistoryField(): HistoryField { 14 | return HistoryField( 15 | date = TimeStampValue(date.toString() + "T00:00:00Z"), 16 | time = StringValue(time), 17 | routineTitle = StringValue(routineTitle), 18 | order = IntValue(order.toString()) 19 | ) 20 | } 21 | 22 | fun HistoryExerciseUiModel.toHistoryExerciseField(): HistoryExerciseField { 23 | return HistoryExerciseField( 24 | historyId = StringValue(historyId), 25 | title = StringValue(title), 26 | order = IntValue(order.toString()), 27 | part = StringValue(part.name), 28 | ) 29 | } 30 | 31 | fun HistoryExerciseSetUiModel.toHistoryExerciseSetField(): HistoryExerciseSetField { 32 | return HistoryExerciseSetField( 33 | exerciseId = StringValue(exerciseId), 34 | weight = StringValue(weight), 35 | count = StringValue(count), 36 | order = IntValue(order.toString()), 37 | ) 38 | } -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/data/mapper/remote/ToRoutineField.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight.data.mapper.remote 2 | 3 | import com.lateinit.rightweight.data.remote.model.DayField 4 | import com.lateinit.rightweight.data.remote.model.ExerciseField 5 | import com.lateinit.rightweight.data.remote.model.ExerciseSetField 6 | import com.lateinit.rightweight.data.remote.model.IntValue 7 | import com.lateinit.rightweight.data.remote.model.RoutineField 8 | import com.lateinit.rightweight.data.remote.model.StringValue 9 | import com.lateinit.rightweight.data.remote.model.TimeStampValue 10 | import com.lateinit.rightweight.ui.model.routine.DayUiModel 11 | import com.lateinit.rightweight.ui.model.routine.ExerciseSetUiModel 12 | import com.lateinit.rightweight.ui.model.routine.ExerciseUiModel 13 | import com.lateinit.rightweight.ui.model.routine.RoutineUiModel 14 | 15 | fun RoutineUiModel.toRoutineField(userId: String): RoutineField { 16 | return RoutineField( 17 | author = StringValue(author), 18 | description = StringValue(description), 19 | modifiedDate = TimeStampValue(modifiedDate.toString() + "Z"), 20 | order = IntValue(order.toString()), 21 | title = StringValue(title), 22 | userId = StringValue(userId), 23 | ) 24 | } 25 | 26 | fun DayUiModel.toDayField(): DayField { 27 | return DayField( 28 | order = IntValue(order.toString()), 29 | routineId = StringValue(routineId) 30 | ) 31 | } 32 | 33 | fun ExerciseUiModel.toExerciseField(): ExerciseField { 34 | return ExerciseField( 35 | order = IntValue(order.toString()), 36 | partType = StringValue(part.name), 37 | title = StringValue(title), 38 | dayId = StringValue(dayId) 39 | ) 40 | } 41 | 42 | fun ExerciseSetUiModel.toExerciseSetField(): ExerciseSetField { 43 | return ExerciseSetField( 44 | order = IntValue(order.toString()), 45 | count = StringValue(count), 46 | weight = StringValue(weight), 47 | exerciseId = StringValue(exerciseId) 48 | ) 49 | } -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/data/mapper/remote/ToSharedRoutineField.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight.data.mapper.remote 2 | 3 | import com.lateinit.rightweight.data.remote.model.IntValue 4 | import com.lateinit.rightweight.data.remote.model.MapValue 5 | import com.lateinit.rightweight.data.remote.model.MapValueRootField 6 | import com.lateinit.rightweight.data.remote.model.SharedCount 7 | import com.lateinit.rightweight.data.remote.model.SharedRoutineField 8 | import com.lateinit.rightweight.data.remote.model.StringValue 9 | import com.lateinit.rightweight.data.remote.model.TimeStampValue 10 | import com.lateinit.rightweight.ui.model.routine.RoutineUiModel 11 | import java.time.LocalDateTime 12 | 13 | fun RoutineUiModel.toSharedRoutineField(userId: String): SharedRoutineField { 14 | return SharedRoutineField( 15 | author = StringValue(author), 16 | description = StringValue(description), 17 | modifiedDate = TimeStampValue(modifiedDate.toString() + "Z"), 18 | order = IntValue(order.toString()), 19 | title = StringValue(title), 20 | userId = StringValue(userId), 21 | sharedCount = MapValue( 22 | MapValueRootField( 23 | SharedCount( 24 | time = TimeStampValue(LocalDateTime.now().toString() + "Z"), 25 | count = IntValue("0") 26 | ) 27 | ) 28 | ) 29 | ) 30 | } -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/data/model/local/ExercisePartType.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight.data.model.local 2 | 3 | enum class ExercisePartType { 4 | CHEST, 5 | BACK, 6 | LEG, 7 | SHOULDER, 8 | BICEPS, 9 | TRICEPS, 10 | CORE, 11 | FOREARM, 12 | CARDIO 13 | } -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/data/model/local/User.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight.data.model.local 2 | 3 | data class User( 4 | val userId: String, 5 | val routineId: String, 6 | val dayId: String, 7 | val completedDayId: String = "", 8 | val email: String, 9 | val displayName: String, 10 | val photoUrl: String, 11 | val idToken: String 12 | ) 13 | -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/data/model/remote/DataValue.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight.data.remote.model 2 | 3 | import com.google.gson.annotations.SerializedName 4 | 5 | sealed class ValueData 6 | 7 | data class StringValue( 8 | @SerializedName("stringValue") 9 | val value: String 10 | ): ValueData() 11 | 12 | data class IntValue( 13 | @SerializedName("integerValue") 14 | val value: String 15 | ): ValueData() 16 | 17 | data class TimeStampValue( 18 | @SerializedName("timestampValue") 19 | val value: String 20 | ): ValueData() 21 | 22 | data class MapValue( 23 | @SerializedName("mapValue") 24 | val value: MapValueRootField 25 | ): ValueData() -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/data/model/remote/Documents.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight.data.model.remote 2 | 3 | data class DocumentResponse(val document: DetailResponse?) 4 | 5 | data class DocumentsResponse(val documents: List>?) 6 | 7 | data class DetailResponse (val name: String, val fields: T) -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/data/model/remote/LoginModel.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight.data.model.remote 2 | 3 | 4 | data class LoginRequestBody( 5 | val postBody: String, 6 | val requestUri: String, 7 | val returnIdpCredential: Boolean, 8 | val returnSecureToken: Boolean 9 | ) 10 | 11 | data class PostBody( 12 | val id_token: String, 13 | val providerId: String, 14 | ) { 15 | override fun toString(): String { 16 | return "id_token=$id_token&providerId=$providerId" 17 | } 18 | } -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/data/model/remote/LoginResponse.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight.data.model.remote 2 | 3 | data class LoginResponse( 4 | val federatedId: String, 5 | val providerId: String, 6 | val localId: String, 7 | val emailVerified: String, 8 | val email: String, 9 | val oauthIdToken: String, 10 | val firstName: String, 11 | val lastName: String, 12 | val fullName: String, 13 | val displayName: String, 14 | val idToken: String, 15 | val photoUrl: String, 16 | val refreshToken: String, 17 | val expiresIn: String, 18 | val rawUserInfo: String 19 | ) -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/data/model/remote/RootField.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight.data.remote.model 2 | 3 | import com.google.gson.annotations.SerializedName 4 | 5 | data class RootField( 6 | @SerializedName("fields") 7 | val remoteData: RemoteData 8 | ) 9 | 10 | data class MapValueRootField( 11 | @SerializedName("fields") 12 | val remoteData: SharedCount 13 | ) -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/data/model/remote/RunQueryModel.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight.data.model.remote 2 | 3 | import com.lateinit.rightweight.data.remote.model.ValueData 4 | 5 | data class RunQueryBody( 6 | val structuredQuery: StructuredQueryData 7 | ) 8 | 9 | data class StructuredQueryData( 10 | val from: FromData, 11 | val where: WhereData? = null, 12 | val orderBy: List? = null, 13 | val limit: Int? = null, 14 | val startAt: StartAtData? = null 15 | ) 16 | 17 | data class FromData( 18 | val collectionId: String, 19 | ) 20 | 21 | data class WhereData( 22 | val fieldFilter: FilterData 23 | ) 24 | 25 | data class FilterData( 26 | val field: FiledReferenceData, 27 | val op: String, 28 | val value: ValueData 29 | ) 30 | 31 | data class FiledReferenceData( 32 | val fieldPath: String 33 | ) 34 | 35 | data class OrderByData( 36 | val field: FiledReferenceData, 37 | val direction: String 38 | ) 39 | 40 | data class StartAtData( 41 | val values: List 42 | ) 43 | 44 | enum class SharedRoutineSortType{ 45 | MODIFIED_DATE_FIRST, 46 | SHARED_COUNT_FIRST 47 | } 48 | 49 | enum class FilterOperator { 50 | //https://firebase.google.com/docs/firestore/reference/rest/v1/StructuredQuery#operator_1 51 | EQUAL, 52 | NOT_EQUAL, 53 | } 54 | 55 | enum class Direction{ 56 | ASCENDING, 57 | DESCENDING 58 | } -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/data/model/remote/WriteModel.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight.data.model.remote 2 | 3 | import com.lateinit.rightweight.data.remote.model.IntValue 4 | import com.lateinit.rightweight.data.remote.model.RemoteData 5 | 6 | data class WriteRequestBody( 7 | val writes: List 8 | ) 9 | 10 | data class WriteModelData( 11 | val transform: TransformData? = null, 12 | val update: UpdateData? = null, 13 | val delete: String? = null, 14 | ){ 15 | companion object{ 16 | const val defaultPath = "projects/right-weight/databases/(default)/documents" 17 | } 18 | } 19 | 20 | data class UpdateData( 21 | val name: String, 22 | val fields: RemoteData 23 | ) 24 | 25 | data class TransformData( 26 | val document: String, 27 | val fieldTransforms: List 28 | ) 29 | 30 | data class FieldTransformsModelData( 31 | val fieldPath: String, 32 | val increment: IntValue 33 | ) -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/data/repository/HistoryRepository.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight.data.repository 2 | 3 | import com.lateinit.rightweight.data.database.entity.Day 4 | import com.lateinit.rightweight.data.database.entity.Exercise 5 | import com.lateinit.rightweight.data.database.entity.ExerciseSet 6 | import com.lateinit.rightweight.data.database.entity.History 7 | import com.lateinit.rightweight.data.database.intermediate.HistoryWithHistoryExercises 8 | import com.lateinit.rightweight.data.model.remote.WriteModelData 9 | import com.lateinit.rightweight.ui.model.history.HistoryExerciseSetUiModel 10 | import com.lateinit.rightweight.ui.model.history.HistoryExerciseUiModel 11 | import com.lateinit.rightweight.ui.model.history.HistoryUiModel 12 | import kotlinx.coroutines.flow.Flow 13 | import java.time.LocalDate 14 | 15 | interface HistoryRepository { 16 | 17 | suspend fun saveHistory( 18 | routineId: String, 19 | day: Day, 20 | routineTitle: String, 21 | exercises: List, 22 | exerciseSets: List 23 | ) 24 | 25 | suspend fun insertHistorySet(historyExerciseId: String) 26 | 27 | suspend fun insertHistoryExercise(historyId: String) 28 | 29 | suspend fun getLatestHistoryDate(userId: String): LocalDate 30 | 31 | suspend fun getHistoryAfterDate(startDate: LocalDate): List 32 | 33 | suspend fun restoreHistory(userId: String) 34 | 35 | fun getHistoryByDate(localDate: LocalDate): Flow 36 | 37 | fun getHistoryWithHistoryExercisesByDate(localDate: LocalDate): Flow 38 | 39 | fun getHistoryBetweenDate( 40 | startDate: LocalDate, 41 | endDate: LocalDate 42 | ): Flow> 43 | 44 | suspend fun updateHistory(historyUiModel: HistoryUiModel) 45 | 46 | suspend fun updateHistorySet(historyExerciseSetUiModel: HistoryExerciseSetUiModel) 47 | 48 | suspend fun updateHistoryExercise(historyExerciseUiModel: HistoryExerciseUiModel) 49 | 50 | suspend fun removeHistorySet(historySetId: String) 51 | 52 | suspend fun removeHistoryExercise(historyExerciseId: String) 53 | 54 | suspend fun removeAllHistories() 55 | 56 | suspend fun removeUncheckedHistorySet() 57 | 58 | suspend fun removeUncheckedHistoryExercise() 59 | 60 | suspend fun commitTransaction(writes: List) 61 | } -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/data/repository/LoginRepository.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight.data.repository 2 | 3 | import com.lateinit.rightweight.data.model.remote.LoginResponse 4 | 5 | interface LoginRepository { 6 | 7 | suspend fun login(key: String, token: String): LoginResponse 8 | 9 | suspend fun deleteAccount(key: String, idToken: String) 10 | } -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/data/repository/RoutineRepository.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight.data.repository 2 | 3 | import com.lateinit.rightweight.data.database.entity.Day 4 | import com.lateinit.rightweight.data.database.entity.Exercise 5 | import com.lateinit.rightweight.data.database.entity.ExerciseSet 6 | import com.lateinit.rightweight.data.database.entity.Routine 7 | import com.lateinit.rightweight.data.database.intermediate.DayWithExercises 8 | import com.lateinit.rightweight.data.database.intermediate.RoutineWithDays 9 | import com.lateinit.rightweight.ui.model.routine.RoutineUiModel 10 | import kotlinx.coroutines.flow.Flow 11 | 12 | interface RoutineRepository { 13 | 14 | suspend fun insertRoutine( 15 | routine: Routine, 16 | days: List, 17 | exercises: List, 18 | sets: List 19 | ) 20 | 21 | suspend fun getRoutineById(routineId: String): Routine 22 | 23 | suspend fun getHigherRoutineOrder(): Int? 24 | 25 | suspend fun getDayById(dayId: String): Day 26 | 27 | suspend fun getDaysByRoutineId(routineId: String): List 28 | 29 | suspend fun getExercisesByDayId(dayId: String): List 30 | 31 | suspend fun getSetsByExerciseId(exerciseId: String): List 32 | 33 | suspend fun getRoutineWithDaysByRoutineId(routineId: String): RoutineWithDays 34 | 35 | suspend fun getUserRoutineIds(userId: String): List 36 | 37 | suspend fun getAllRoutineWithDays(): List 38 | 39 | suspend fun restoreMyRoutine(routineIds: List) 40 | 41 | fun getAllRoutines(): Flow> 42 | 43 | fun getDayWithExercisesByDayId(dayId: String): Flow 44 | 45 | suspend fun updateRoutines(routines: List) 46 | 47 | suspend fun removeRoutineById(routineId: String) 48 | 49 | suspend fun removeAllRoutines() 50 | 51 | suspend fun getChildrenDocumentName(path: String): List 52 | } -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/data/repository/SharedRoutineRepository.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight.data.repository 2 | 3 | import androidx.paging.PagingData 4 | import com.lateinit.rightweight.data.database.entity.SharedRoutine 5 | import com.lateinit.rightweight.data.database.intermediate.SharedRoutineWithDays 6 | import com.lateinit.rightweight.data.model.remote.SharedRoutineSortType 7 | import com.lateinit.rightweight.data.model.remote.WriteModelData 8 | import kotlinx.coroutines.flow.Flow 9 | 10 | interface SharedRoutineRepository { 11 | 12 | fun getSharedRoutinesByPaging(): Flow> 13 | 14 | suspend fun getChildrenDocumentName(path: String): List 15 | 16 | fun getSharedRoutineDetail(routineId: String): Flow 17 | 18 | suspend fun requestSharedRoutineDetail(routineId: String) 19 | 20 | suspend fun commitTransaction(writes: List) 21 | 22 | suspend fun isRoutineShared(routineId: String): Boolean 23 | 24 | suspend fun increaseSharedCount(routineId: String) 25 | 26 | suspend fun setSharedRoutineSortType(sortType: SharedRoutineSortType) 27 | } -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/data/repository/UserRepository.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight.data.repository 2 | 3 | import com.lateinit.rightweight.data.model.local.User 4 | import com.lateinit.rightweight.data.remote.model.UserInfoField 5 | import kotlinx.coroutines.flow.Flow 6 | 7 | interface UserRepository { 8 | 9 | suspend fun saveUser(user: User) 10 | 11 | fun getUser(): Flow 12 | 13 | suspend fun removeUserInfo() 14 | 15 | suspend fun restoreUserInfo(userId: String): UserInfoField? 16 | 17 | suspend fun backupUserInfo(user: User) 18 | } -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/data/repository/impl/LoginRepositoryImpl.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight.data.repository.impl 2 | 3 | import com.lateinit.rightweight.data.model.remote.LoginResponse 4 | import com.lateinit.rightweight.data.datasource.remote.LoginDataSource 5 | import com.lateinit.rightweight.data.repository.LoginRepository 6 | import javax.inject.Inject 7 | 8 | class LoginRepositoryImpl @Inject constructor( 9 | private val loginDataSource: LoginDataSource 10 | ) : LoginRepository { 11 | 12 | override suspend fun login(key: String, token: String): LoginResponse { 13 | return loginDataSource.login(key, token) 14 | } 15 | 16 | override suspend fun deleteAccount(key: String, idToken: String) { 17 | loginDataSource.deleteAccount(key, idToken) 18 | } 19 | } -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/data/repository/impl/UserRepositoryImpl.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight.data.repository.impl 2 | 3 | import com.lateinit.rightweight.data.datasource.local.UserLocalDataSource 4 | import com.lateinit.rightweight.data.datasource.remote.UserRemoteDataSource 5 | import com.lateinit.rightweight.data.model.local.User 6 | import com.lateinit.rightweight.data.remote.model.UserInfoField 7 | import com.lateinit.rightweight.data.repository.UserRepository 8 | import kotlinx.coroutines.flow.Flow 9 | import javax.inject.Inject 10 | 11 | class UserRepositoryImpl @Inject constructor( 12 | private val userLocalDataSource: UserLocalDataSource, 13 | private val userRemoteDataSource: UserRemoteDataSource 14 | ) : UserRepository { 15 | 16 | override suspend fun saveUser(user: User) { 17 | userLocalDataSource.saveUser(user) 18 | } 19 | 20 | override fun getUser(): Flow { 21 | return userLocalDataSource.getUser() 22 | } 23 | 24 | override suspend fun removeUserInfo() { 25 | userLocalDataSource.removeUserInfo() 26 | } 27 | 28 | override suspend fun restoreUserInfo(userId: String): UserInfoField? { 29 | return userRemoteDataSource.restoreUserInfo(userId) 30 | } 31 | 32 | override suspend fun backupUserInfo(user: User) { 33 | userRemoteDataSource.backupUserInfo(user.userId, user.routineId, user.dayId, user.completedDayId) 34 | } 35 | } -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/di/ApiModule.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight.di 2 | 3 | import com.lateinit.rightweight.BuildConfig 4 | import com.lateinit.rightweight.data.api.AuthApiService 5 | import com.lateinit.rightweight.data.api.RoutineApiService 6 | import com.lateinit.rightweight.data.api.UserApiService 7 | import dagger.Module 8 | import dagger.Provides 9 | import dagger.hilt.InstallIn 10 | import dagger.hilt.components.SingletonComponent 11 | import okhttp3.Interceptor 12 | import okhttp3.OkHttpClient 13 | import okhttp3.logging.HttpLoggingInterceptor 14 | import retrofit2.Retrofit 15 | import retrofit2.converter.gson.GsonConverterFactory 16 | import java.util.concurrent.TimeUnit 17 | import javax.inject.Named 18 | import javax.inject.Singleton 19 | 20 | @Module 21 | @InstallIn(SingletonComponent::class) 22 | class ApiModule { 23 | 24 | @Provides 25 | @Singleton 26 | fun provideAuthApiService(@Named("Auth") retrofit: Retrofit): AuthApiService { 27 | return retrofit.create(AuthApiService::class.java) 28 | } 29 | 30 | @Provides 31 | @Singleton 32 | fun provideRoutineApiService(@Named("Store") retrofit: Retrofit): RoutineApiService { 33 | return retrofit.create(RoutineApiService::class.java) 34 | } 35 | 36 | @Provides 37 | @Singleton 38 | fun provideUserApiService(@Named("Store") retrofit: Retrofit): UserApiService { 39 | return retrofit.create(UserApiService::class.java) 40 | } 41 | 42 | @Provides 43 | @Singleton 44 | @Named("Auth") 45 | fun provideAuthRetrofit( 46 | okHttpClient: OkHttpClient, 47 | gsonConverterFactory: GsonConverterFactory 48 | ): Retrofit { 49 | return Retrofit.Builder() 50 | .baseUrl("https://identitytoolkit.googleapis.com/v1/") 51 | .addConverterFactory(gsonConverterFactory) 52 | .client(okHttpClient) 53 | .build() 54 | } 55 | 56 | @Provides 57 | @Singleton 58 | @Named("Store") 59 | fun provideStoreRetrofit( 60 | okHttpClient: OkHttpClient, 61 | gsonConverterFactory: GsonConverterFactory 62 | ): Retrofit { 63 | return Retrofit.Builder() 64 | .baseUrl("https://firestore.googleapis.com/v1/projects/right-weight/databases/(default)/") 65 | .addConverterFactory(gsonConverterFactory) 66 | .client(okHttpClient) 67 | .build() 68 | } 69 | 70 | @Provides 71 | @Singleton 72 | fun provideGsonConvertFactory(): GsonConverterFactory { 73 | return GsonConverterFactory.create() 74 | } 75 | 76 | @Provides 77 | @Singleton 78 | fun provideOkHttpClient( 79 | customInterceptor: Interceptor 80 | ): OkHttpClient { 81 | val httpLoggingInterceptor = HttpLoggingInterceptor() 82 | if (BuildConfig.DEBUG) { 83 | httpLoggingInterceptor.level = HttpLoggingInterceptor.Level.BODY 84 | } else { 85 | httpLoggingInterceptor.level = HttpLoggingInterceptor.Level.NONE 86 | } 87 | return OkHttpClient.Builder() 88 | .connectTimeout(10, TimeUnit.SECONDS) 89 | .addInterceptor(httpLoggingInterceptor) 90 | .addInterceptor(customInterceptor) 91 | .build() 92 | } 93 | 94 | @Singleton 95 | @Provides 96 | fun provideCustomInterceptor(): Interceptor = Interceptor { chain -> 97 | chain.run { 98 | proceed( 99 | request() 100 | .newBuilder() 101 | .addHeader("Content-Type", "application/json") 102 | .build() 103 | ) 104 | } 105 | } 106 | } -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/di/DatabaseModule.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight.di 2 | 3 | import android.content.Context 4 | import androidx.room.Room 5 | import com.lateinit.rightweight.data.database.AppDatabase 6 | import com.lateinit.rightweight.data.dataStore.AppPreferencesDataStore 7 | import com.lateinit.rightweight.data.database.dao.HistoryDao 8 | import com.lateinit.rightweight.data.database.dao.RoutineDao 9 | import com.lateinit.rightweight.data.database.dao.SharedRoutineDao 10 | import dagger.Module 11 | import dagger.Provides 12 | import dagger.hilt.InstallIn 13 | import dagger.hilt.android.qualifiers.ApplicationContext 14 | import dagger.hilt.components.SingletonComponent 15 | import javax.inject.Singleton 16 | 17 | @Module 18 | @InstallIn(SingletonComponent::class) 19 | class DatabaseModule { 20 | 21 | @Provides 22 | @Singleton 23 | fun provideAppDatabase(@ApplicationContext context: Context): AppDatabase { 24 | return Room.databaseBuilder(context, AppDatabase::class.java, DB_NAME).build() 25 | } 26 | 27 | @Provides 28 | @Singleton 29 | fun provideRoutineDao(appDatabase: AppDatabase): RoutineDao { 30 | return appDatabase.routineDao() 31 | } 32 | 33 | @Provides 34 | @Singleton 35 | fun provideHistoryDao(appDatabase: AppDatabase): HistoryDao { 36 | return appDatabase.historyDao() 37 | } 38 | 39 | @Provides 40 | @Singleton 41 | fun provideSharedRoutineDao(appDatabase: AppDatabase): SharedRoutineDao { 42 | return appDatabase.sharedRoutineDao() 43 | } 44 | 45 | 46 | @Provides 47 | @Singleton 48 | fun provideAppPreferencesDataStore(@ApplicationContext context: Context): AppPreferencesDataStore { 49 | return AppPreferencesDataStore(context) 50 | } 51 | 52 | companion object { 53 | private const val DB_NAME = "right_weight.db" 54 | } 55 | } -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/di/RepositoryModule.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight.di 2 | 3 | import com.lateinit.rightweight.data.datasource.* 4 | import com.lateinit.rightweight.data.datasource.local.HistoryLocalDataSource 5 | import com.lateinit.rightweight.data.datasource.local.RoutineLocalDataSource 6 | import com.lateinit.rightweight.data.datasource.local.SharedRoutineLocalDataSource 7 | import com.lateinit.rightweight.data.datasource.local.UserLocalDataSource 8 | import com.lateinit.rightweight.data.datasource.remote.HistoryRemoteDatasource 9 | import com.lateinit.rightweight.data.datasource.remote.LoginDataSource 10 | import com.lateinit.rightweight.data.datasource.remote.RoutineRemoteDataSource 11 | import com.lateinit.rightweight.data.datasource.remote.SharedRoutineRemoteDataSource 12 | import com.lateinit.rightweight.data.datasource.remote.UserRemoteDataSource 13 | import com.lateinit.rightweight.data.repository.* 14 | import com.lateinit.rightweight.data.repository.impl.HistoryRepositoryImpl 15 | import com.lateinit.rightweight.data.repository.impl.LoginRepositoryImpl 16 | import com.lateinit.rightweight.data.repository.impl.RoutineRepositoryImpl 17 | import com.lateinit.rightweight.data.repository.impl.SharedRoutineRepositoryImpl 18 | import com.lateinit.rightweight.data.repository.impl.UserRepositoryImpl 19 | import dagger.Module 20 | import dagger.Provides 21 | import dagger.hilt.InstallIn 22 | import dagger.hilt.components.SingletonComponent 23 | import javax.inject.Singleton 24 | 25 | @Module 26 | @InstallIn(SingletonComponent::class) 27 | class RepositoryModule { 28 | 29 | @Provides 30 | @Singleton 31 | fun provideLoginRepository( 32 | loginDataSource: LoginDataSource 33 | ): LoginRepository { 34 | return LoginRepositoryImpl(loginDataSource) 35 | } 36 | 37 | @Provides 38 | @Singleton 39 | fun provideRoutineRepository( 40 | routineLocalDataSource: RoutineLocalDataSource, 41 | routineRemoteDataSource: RoutineRemoteDataSource 42 | ): RoutineRepository { 43 | return RoutineRepositoryImpl(routineLocalDataSource, routineRemoteDataSource) 44 | } 45 | 46 | @Provides 47 | @Singleton 48 | fun provideUserRepository( 49 | userLocalDataSource: UserLocalDataSource, 50 | userRemoteDataSource: UserRemoteDataSource 51 | ): UserRepository { 52 | return UserRepositoryImpl(userLocalDataSource, userRemoteDataSource) 53 | } 54 | 55 | @Provides 56 | @Singleton 57 | fun provideHistoryRepository( 58 | historyLocalDataSource: HistoryLocalDataSource, 59 | historyRemoteDatasource: HistoryRemoteDatasource 60 | ): HistoryRepository { 61 | return HistoryRepositoryImpl(historyLocalDataSource, historyRemoteDatasource) 62 | } 63 | 64 | @Provides 65 | @Singleton 66 | fun provideSharedRoutineRepository( 67 | sharedRoutineRemoteDataSource: SharedRoutineRemoteDataSource, 68 | sharedRoutineLocalDataSource: SharedRoutineLocalDataSource 69 | ): SharedRoutineRepository { 70 | return SharedRoutineRepositoryImpl( 71 | sharedRoutineRemoteDataSource, 72 | sharedRoutineLocalDataSource 73 | ) 74 | } 75 | } -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/ui/calendar/CalendarViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight.ui.calendar 2 | 3 | import androidx.lifecycle.ViewModel 4 | import androidx.lifecycle.viewModelScope 5 | import com.lateinit.rightweight.data.repository.HistoryRepository 6 | import com.lateinit.rightweight.ui.model.history.HistoryUiModel 7 | import dagger.hilt.android.lifecycle.HiltViewModel 8 | import kotlinx.coroutines.ExperimentalCoroutinesApi 9 | import kotlinx.coroutines.flow.Flow 10 | import kotlinx.coroutines.flow.MutableStateFlow 11 | import kotlinx.coroutines.flow.SharingStarted 12 | import kotlinx.coroutines.flow.StateFlow 13 | import kotlinx.coroutines.flow.asStateFlow 14 | import kotlinx.coroutines.flow.combine 15 | import kotlinx.coroutines.flow.flatMapLatest 16 | import kotlinx.coroutines.flow.stateIn 17 | import java.time.DayOfWeek 18 | import java.time.LocalDate 19 | import java.time.YearMonth 20 | import javax.inject.Inject 21 | 22 | @HiltViewModel 23 | @OptIn(ExperimentalCoroutinesApi::class) 24 | class CalendarViewModel @Inject constructor( 25 | private val historyRepository: HistoryRepository 26 | ) : ViewModel() { 27 | 28 | private val selectedDay = MutableStateFlow(LocalDate.now()) 29 | private val currentMonth = MutableStateFlow(YearMonth.now()) 30 | 31 | private val _routineTitle = MutableStateFlow(DEFAULT_ROUTINE_TITLE) 32 | val routineTitle: StateFlow = _routineTitle.asStateFlow() 33 | 34 | private val _exerciseTime = MutableStateFlow(DEFAULT_EXERCISE_TIME) 35 | val exerciseTime: StateFlow = _exerciseTime.asStateFlow() 36 | 37 | val dateToExerciseHistories = currentMonth.flatMapLatest { 38 | getHistoryBetweenDate(it) 39 | }.stateIn(viewModelScope, SharingStarted.Lazily, mapOf()) 40 | 41 | val selectedDayInfo = selectedDay.combine(dateToExerciseHistories) { date, _ -> 42 | getSelectedDayInfo(date) 43 | }.stateIn(viewModelScope, SharingStarted.Lazily, null) 44 | 45 | fun selectDay(date: LocalDate) { 46 | selectedDay.value = date 47 | } 48 | 49 | fun changeMonth(date: LocalDate) { 50 | currentMonth.value = YearMonth.from(date) 51 | } 52 | 53 | private fun getSelectedDayInfo(date: LocalDate): HistoryUiModel? { 54 | return dateToExerciseHistories.value[date].also { 55 | _routineTitle.value = it?.routineTitle ?: DEFAULT_ROUTINE_TITLE 56 | _exerciseTime.value = it?.time ?: DEFAULT_EXERCISE_TIME 57 | } 58 | } 59 | 60 | private fun getHistoryBetweenDate( 61 | month: YearMonth 62 | ): Flow> { 63 | val startDay = month.atDay(START_DAY_OF_MONTH) 64 | val endDay = month.atEndOfMonth() 65 | val startDayDiff = 66 | (DayOfWeek.values().size - (DayOfWeek.SUNDAY.value - startDay.dayOfWeek.value)).toLong() 67 | val endDayDiff = MONTH_TILE_COUNT - startDayDiff - endDay.dayOfMonth 68 | 69 | return historyRepository.getHistoryBetweenDate( 70 | startDay.minusDays(startDayDiff), 71 | endDay.plusDays(endDayDiff) 72 | ) 73 | } 74 | 75 | companion object { 76 | private const val START_DAY_OF_MONTH = 1 77 | private const val MONTH_TILE_COUNT = 7 * 6 78 | private const val DEFAULT_ROUTINE_TITLE = "" 79 | private const val DEFAULT_EXERCISE_TIME = "" 80 | } 81 | } -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/ui/calendar/CompletedDayDecorator.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight.ui.calendar 2 | 3 | import android.content.Context 4 | import androidx.appcompat.content.res.AppCompatResources 5 | import com.lateinit.rightweight.R 6 | import com.prolificinteractive.materialcalendarview.CalendarDay 7 | import com.prolificinteractive.materialcalendarview.DayViewDecorator 8 | import com.prolificinteractive.materialcalendarview.DayViewFacade 9 | import java.time.LocalDate 10 | 11 | class CompletedDayDecorator(context: Context) : DayViewDecorator { 12 | 13 | private var completedDates: Set = emptySet() 14 | private val completeHistoryDrawable = 15 | AppCompatResources.getDrawable(context, R.drawable.bg_calendar_day_completed) 16 | 17 | override fun shouldDecorate(day: CalendarDay?): Boolean { 18 | return day?.date in completedDates 19 | } 20 | 21 | override fun decorate(view: DayViewFacade?) { 22 | completeHistoryDrawable ?: return 23 | view?.setBackgroundDrawable(completeHistoryDrawable) 24 | } 25 | 26 | fun changeCompletedDates(completedDates: Set) { 27 | this.completedDates = completedDates 28 | } 29 | } -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/ui/calendar/DayDecorator.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight.ui.calendar 2 | 3 | import android.content.Context 4 | import android.text.style.TextAppearanceSpan 5 | import androidx.appcompat.content.res.AppCompatResources 6 | import com.lateinit.rightweight.R 7 | import com.prolificinteractive.materialcalendarview.CalendarDay 8 | import com.prolificinteractive.materialcalendarview.DayViewDecorator 9 | import com.prolificinteractive.materialcalendarview.DayViewFacade 10 | 11 | class DayDecorator(context: Context) : DayViewDecorator { 12 | 13 | private val selectorDrawable = 14 | AppCompatResources.getDrawable(context, R.drawable.bg_calendar_day) 15 | private val textAppearanceSpan = TextAppearanceSpan(context, R.style.DayTextAppearance) 16 | 17 | override fun shouldDecorate(day: CalendarDay?): Boolean { 18 | return true 19 | } 20 | 21 | override fun decorate(view: DayViewFacade?) { 22 | selectorDrawable ?: return 23 | view?.apply { 24 | setSelectionDrawable(selectorDrawable) 25 | addSpan(textAppearanceSpan) 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/ui/dialog/CommonDialogFragment.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight.ui.dialog 2 | 3 | import android.app.AlertDialog 4 | import android.app.Dialog 5 | import android.os.Bundle 6 | import androidx.annotation.StringRes 7 | import androidx.fragment.app.DialogFragment 8 | import androidx.fragment.app.FragmentManager 9 | import com.lateinit.rightweight.R 10 | 11 | class CommonDialogFragment(private val callback: (String?) -> Unit) : DialogFragment() { 12 | 13 | var messageId: Int = R.string.logout_message 14 | 15 | override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { 16 | return activity?.let { 17 | val builder = AlertDialog.Builder(it) 18 | builder.setMessage(messageId) 19 | .setPositiveButton(R.string.submit) { _, _ -> 20 | callback(this.tag) 21 | } 22 | .setNegativeButton(R.string.cancel) { dialog, _ -> 23 | dialog.cancel() 24 | } 25 | builder.create() 26 | } ?: throw IllegalStateException("Activity cannot be null") 27 | } 28 | 29 | fun show( 30 | manager: FragmentManager, 31 | tag: String?, 32 | @StringRes messageId: Int 33 | ) { 34 | this.messageId = messageId 35 | show(manager, tag) 36 | } 37 | 38 | companion object { 39 | const val BACKUP_USER_INFO_TAG = "BACKUP" 40 | const val LOGOUT_DIALOG_TAG = "LOGOUT" 41 | const val WITHDRAW_DIALOG_TAG = "WITHDRAW" 42 | const val RESET_DIALOG_TAG = "RESET" 43 | const val SELECTED_ROUTINE_REMOVE_DIALOG_TAG = "SELECTED_ROUTINE_REMOVE" 44 | const val ROUTINE_REMOVE_DIALOG_TAG = "ROUTINE_REMOVE" 45 | const val END_EXERCISE_DIALOG_TAG = "END_EXERCISE" 46 | const val EDITOR_BACK_PRESSED_DIALOG_TAG = "EDITOR_BACK_PRESSED" 47 | } 48 | } -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/ui/dialog/LoadingDialog.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight.ui.dialog 2 | 3 | import android.app.Dialog 4 | import android.content.Context 5 | import android.graphics.Color 6 | import android.graphics.drawable.ColorDrawable 7 | import android.view.Window 8 | 9 | 10 | class LoadingDialogProvider { 11 | 12 | fun provideLoadingDialog(context: Context, loadingLayout: Int): Dialog { 13 | return Dialog(context).apply { 14 | requestWindowFeature(Window.FEATURE_NO_TITLE) 15 | setCanceledOnTouchOutside(false) 16 | setCancelable(false) 17 | window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT)) 18 | setContentView(loadingLayout) 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/ui/exercise/HistoryEventListener.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight.ui.exercise 2 | 3 | import com.lateinit.rightweight.ui.model.history.HistoryExerciseSetUiModel 4 | import com.lateinit.rightweight.ui.model.history.HistoryExerciseUiModel 5 | 6 | interface HistoryEventListener { 7 | 8 | fun addHistorySet(historyExerciseId: String) 9 | 10 | fun updateHistorySet(historyExerciseSetUiModel: HistoryExerciseSetUiModel) 11 | 12 | fun updateHistoryExercise(historyExerciseUiModel: HistoryExerciseUiModel) 13 | 14 | fun removeHistorySet(historySetId: String) 15 | 16 | fun removeHistoryExercise(historyExerciseId:String) 17 | } -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/ui/home/ExpandableItemAnimator.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight.ui.home 2 | 3 | import android.animation.Animator 4 | import android.animation.AnimatorListenerAdapter 5 | import android.animation.ObjectAnimator 6 | import android.view.View 7 | import androidx.recyclerview.widget.DefaultItemAnimator 8 | import androidx.recyclerview.widget.RecyclerView 9 | 10 | class ExpandableItemAnimator : DefaultItemAnimator() { 11 | 12 | override fun recordPreLayoutInformation( 13 | state: RecyclerView.State, 14 | viewHolder: RecyclerView.ViewHolder, 15 | changeFlags: Int, 16 | payloads: MutableList 17 | ): ItemHolderInfo { 18 | return if (viewHolder is HomeAdapter.ExerciseViewHolder) { 19 | ExerciseItemInfo().also { 20 | it.setFrom(viewHolder) 21 | } 22 | } else { 23 | super.recordPreLayoutInformation(state, viewHolder, changeFlags, payloads) 24 | } 25 | } 26 | 27 | override fun recordPostLayoutInformation( 28 | state: RecyclerView.State, 29 | viewHolder: RecyclerView.ViewHolder 30 | ): ItemHolderInfo { 31 | return if (viewHolder is HomeAdapter.ExerciseViewHolder) { 32 | ExerciseItemInfo().also { 33 | it.setFrom(viewHolder) 34 | } 35 | } else { 36 | super.recordPostLayoutInformation(state, viewHolder) 37 | } 38 | } 39 | 40 | override fun animateChange( 41 | oldHolder: RecyclerView.ViewHolder, 42 | holder: RecyclerView.ViewHolder, 43 | preInfo: ItemHolderInfo, 44 | postInfo: ItemHolderInfo 45 | ): Boolean { 46 | if (preInfo is ExerciseItemInfo && postInfo is ExerciseItemInfo && holder is HomeAdapter.ExerciseViewHolder) { 47 | ObjectAnimator 48 | .ofFloat( 49 | holder.binding.imageViewExpand, 50 | View.ROTATION, 51 | preInfo.arrowRotation, 52 | postInfo.arrowRotation 53 | ) 54 | .also { 55 | it.addListener(object : AnimatorListenerAdapter() { 56 | override fun onAnimationEnd(animation: Animator?) { 57 | holder.binding.imageViewExpand.rotation = postInfo.arrowRotation 58 | dispatchAnimationFinished(holder) 59 | } 60 | }) 61 | it.start() 62 | } 63 | } 64 | return super.animateChange(oldHolder, holder, preInfo, postInfo) 65 | } 66 | 67 | //It means that for animation we don’t need to have separated objects of ViewHolder (old and new holder) 68 | override fun canReuseUpdatedViewHolder(viewHolder: RecyclerView.ViewHolder): Boolean { 69 | return true 70 | } 71 | } 72 | 73 | class ExerciseItemInfo : RecyclerView.ItemAnimator.ItemHolderInfo() { 74 | 75 | internal var arrowRotation: Float = 0F 76 | 77 | override fun setFrom(holder: RecyclerView.ViewHolder): RecyclerView.ItemAnimator.ItemHolderInfo { 78 | if (holder is HomeAdapter.ExerciseViewHolder) { 79 | arrowRotation = holder.binding.imageViewExpand.rotation 80 | } 81 | return super.setFrom(holder) 82 | } 83 | } -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/ui/home/HomeBindingAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight.ui.home 2 | 3 | import android.widget.ImageView 4 | import androidx.databinding.BindingAdapter 5 | import com.bumptech.glide.Glide 6 | import com.lateinit.rightweight.R 7 | 8 | @BindingAdapter( 9 | value = ["imageUrl"] 10 | ) 11 | fun ImageView.setImage(imageUrl: String?){ 12 | Glide.with(this) 13 | .load(imageUrl) 14 | .error(R.drawable.ic_launcher_foreground) 15 | .into(this) 16 | } -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/ui/login/LoginViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight.ui.login 2 | 3 | import androidx.lifecycle.ViewModel 4 | import androidx.lifecycle.viewModelScope 5 | import com.lateinit.rightweight.data.model.local.User 6 | import com.lateinit.rightweight.data.model.remote.LoginResponse 7 | import com.lateinit.rightweight.data.repository.LoginRepository 8 | import com.lateinit.rightweight.data.repository.UserRepository 9 | import dagger.hilt.android.lifecycle.HiltViewModel 10 | import kotlinx.coroutines.CoroutineExceptionHandler 11 | import kotlinx.coroutines.flow.MutableStateFlow 12 | import kotlinx.coroutines.flow.asStateFlow 13 | import kotlinx.coroutines.launch 14 | import retrofit2.HttpException 15 | import java.net.SocketException 16 | import java.net.UnknownHostException 17 | import javax.inject.Inject 18 | 19 | @HiltViewModel 20 | class LoginViewModel @Inject constructor( 21 | private val loginRepository: LoginRepository, 22 | private val userRepository: UserRepository 23 | ) : ViewModel() { 24 | 25 | private val _networkResult = MutableStateFlow(NetworkState.NO_ERROR) 26 | val networkResult = _networkResult.asStateFlow() 27 | 28 | private val networkExceptionHandler = CoroutineExceptionHandler { _, throwable -> 29 | when (throwable) { 30 | is SocketException -> _networkResult.value = NetworkState.BAD_INTERNET 31 | is HttpException -> _networkResult.value = NetworkState.PARSE_ERROR 32 | is UnknownHostException -> _networkResult.value = NetworkState.WRONG_CONNECTION 33 | else -> _networkResult.value = NetworkState.OTHER_ERROR 34 | } 35 | } 36 | 37 | fun loginToFirebase(key: String, token: String) { 38 | viewModelScope.launch(networkExceptionHandler) { 39 | saveUser(loginRepository.login(key, token)) 40 | _networkResult.value = NetworkState.SUCCESS 41 | } 42 | } 43 | 44 | private suspend fun saveUser(loginResponse: LoginResponse) { 45 | with(loginResponse) { 46 | userRepository.saveUser( 47 | User(localId, "", "", "", email, displayName, photoUrl, idToken) 48 | ) 49 | } 50 | } 51 | } 52 | 53 | enum class NetworkState { 54 | NO_ERROR, BAD_INTERNET, PARSE_ERROR, WRONG_CONNECTION, OTHER_ERROR, SUCCESS 55 | } -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/ui/mapper/ExercisePartType.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight.ui.mapper 2 | 3 | import com.lateinit.rightweight.data.model.local.ExercisePartType 4 | import com.lateinit.rightweight.ui.model.routine.ExercisePartTypeUiModel 5 | 6 | fun ExercisePartType.toExercisePartTypeUiModel(): ExercisePartTypeUiModel { 7 | return when (this) { 8 | ExercisePartType.CHEST -> ExercisePartTypeUiModel.CHEST 9 | ExercisePartType.BACK -> ExercisePartTypeUiModel.BACK 10 | ExercisePartType.LEG -> ExercisePartTypeUiModel.LEG 11 | ExercisePartType.SHOULDER -> ExercisePartTypeUiModel.SHOULDER 12 | ExercisePartType.BICEPS -> ExercisePartTypeUiModel.BICEPS 13 | ExercisePartType.TRICEPS -> ExercisePartTypeUiModel.TRICEPS 14 | ExercisePartType.CORE -> ExercisePartTypeUiModel.CORE 15 | ExercisePartType.FOREARM -> ExercisePartTypeUiModel.FOREARM 16 | ExercisePartType.CARDIO -> ExercisePartTypeUiModel.CARDIO 17 | } 18 | } -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/ui/mapper/HistoryUiModel.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight.ui.mapper 2 | 3 | import com.lateinit.rightweight.data.database.entity.HistorySet 4 | import com.lateinit.rightweight.data.database.intermediate.HistoryExerciseWithHistorySets 5 | import com.lateinit.rightweight.data.database.intermediate.HistoryWithHistoryExercises 6 | import com.lateinit.rightweight.ui.model.history.HistoryExerciseSetUiModel 7 | import com.lateinit.rightweight.ui.model.history.HistoryExerciseUiModel 8 | import com.lateinit.rightweight.ui.model.history.HistoryUiModel 9 | 10 | fun HistoryWithHistoryExercises.toHistoryUiModel(): HistoryUiModel { 11 | return HistoryUiModel( 12 | historyId = history.historyId, 13 | date = history.date, 14 | time = history.time, 15 | routineTitle = history.routineTitle, 16 | order = history.dayOrder, 17 | completed = history.completed, 18 | exercises = historyExercises.map { it.toHistoryExerciseUiModel() } 19 | ) 20 | } 21 | 22 | fun HistoryExerciseWithHistorySets.toHistoryExerciseUiModel(): HistoryExerciseUiModel { 23 | return HistoryExerciseUiModel( 24 | exerciseId = historyExercise.exerciseId, 25 | historyId = historyExercise.historyId, 26 | title = historyExercise.title, 27 | order = historyExercise.order, 28 | part = historyExercise.part.toExercisePartTypeUiModel(), 29 | exerciseSets = historySets.map { it.toHistoryExerciseSetUiModel() } 30 | ) 31 | } 32 | 33 | fun HistorySet.toHistoryExerciseSetUiModel(): HistoryExerciseSetUiModel { 34 | return HistoryExerciseSetUiModel( 35 | setId = setId, 36 | exerciseId = exerciseId, 37 | weight = weight, 38 | count = count, 39 | order = order, 40 | checked = checked 41 | ) 42 | } 43 | -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/ui/mapper/Routine.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight.ui.mapper 2 | 3 | import com.lateinit.rightweight.data.database.entity.Day 4 | import com.lateinit.rightweight.data.database.entity.ExerciseSet 5 | import com.lateinit.rightweight.data.database.entity.Routine 6 | import com.lateinit.rightweight.data.database.intermediate.DayWithExercises 7 | import com.lateinit.rightweight.data.database.intermediate.ExerciseWithSets 8 | import com.lateinit.rightweight.ui.model.routine.DayUiModel 9 | import com.lateinit.rightweight.ui.model.routine.ExerciseSetUiModel 10 | import com.lateinit.rightweight.ui.model.routine.ExerciseUiModel 11 | import com.lateinit.rightweight.ui.model.routine.RoutineUiModel 12 | import com.lateinit.rightweight.util.FIRST_DAY_POSITION 13 | 14 | fun Routine.toRoutineUiModel(): RoutineUiModel { 15 | return RoutineUiModel( 16 | routineId = routineId, 17 | title = title, 18 | author = author, 19 | description = description, 20 | modifiedDate = modifiedDate, 21 | order = order 22 | ) 23 | } 24 | 25 | fun Day.toDayUiModel(index: Int, exerciseWithSets: List): DayUiModel { 26 | return DayUiModel( 27 | dayId = dayId, 28 | routineId = routineId, 29 | order = order, 30 | selected = index == FIRST_DAY_POSITION, 31 | exercises = exerciseWithSets.map { it.toExerciseUiModel() } 32 | ) 33 | } 34 | 35 | fun DayWithExercises.toDayUiModel(): DayUiModel { 36 | return DayUiModel( 37 | dayId = day.dayId, 38 | routineId = day.routineId, 39 | order = day.order, 40 | selected = day.order == FIRST_DAY_POSITION, 41 | exercises = exercises.map { it.toExerciseUiModel() } 42 | ) 43 | } 44 | 45 | fun ExerciseWithSets.toExerciseUiModel(): ExerciseUiModel { 46 | return ExerciseUiModel( 47 | exerciseId = exercise.exerciseId, 48 | dayId = exercise.dayId, 49 | title = exercise.title, 50 | order = exercise.order, 51 | part = exercise.part.toExercisePartTypeUiModel(), 52 | exerciseSets = sets.map { it.toExerciseSetUiModel() } 53 | ) 54 | } 55 | 56 | fun ExerciseSet.toExerciseSetUiModel(): ExerciseSetUiModel { 57 | return ExerciseSetUiModel( 58 | setId = setId, 59 | exerciseId = exerciseId, 60 | weight = weight, 61 | count = count, 62 | order = order 63 | ) 64 | } 65 | 66 | -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/ui/mapper/SharedRoutine.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight.ui.mapper 2 | 3 | import com.lateinit.rightweight.data.database.entity.SharedRoutine 4 | import com.lateinit.rightweight.data.database.entity.SharedRoutineDay 5 | import com.lateinit.rightweight.data.database.entity.SharedRoutineExerciseSet 6 | import com.lateinit.rightweight.data.database.intermediate.SharedRoutineExerciseWithExerciseSets 7 | import com.lateinit.rightweight.ui.model.routine.DayUiModel 8 | import com.lateinit.rightweight.ui.model.routine.ExerciseSetUiModel 9 | import com.lateinit.rightweight.ui.model.routine.ExerciseUiModel 10 | import com.lateinit.rightweight.ui.model.shared.SharedRoutineUiModel 11 | import com.lateinit.rightweight.util.FIRST_DAY_POSITION 12 | 13 | fun SharedRoutine.toSharedRoutineUiModel(): SharedRoutineUiModel { 14 | return SharedRoutineUiModel( 15 | routineId = routineId, 16 | title = title, 17 | author = author, 18 | description = description, 19 | modifiedDate = modifiedDate, 20 | sharedCount = sharedCount 21 | ) 22 | } 23 | 24 | fun SharedRoutineDay.toDayUiModel( 25 | exercises: List 26 | ): DayUiModel { 27 | return DayUiModel( 28 | dayId = dayId, 29 | routineId = routineId, 30 | order = order, 31 | selected = order == FIRST_DAY_POSITION, 32 | exercises = exercises.map { it.toExerciseUiModel() } 33 | ) 34 | } 35 | 36 | fun SharedRoutineExerciseWithExerciseSets.toExerciseUiModel(): ExerciseUiModel { 37 | return ExerciseUiModel( 38 | exerciseId = exercise.exerciseId, 39 | dayId = exercise.dayId, 40 | title = exercise.title, 41 | order = exercise.order, 42 | part = exercise.part.toExercisePartTypeUiModel(), 43 | exerciseSets = sets.map { it.toExerciseSetUiModel() } 44 | ) 45 | } 46 | 47 | fun SharedRoutineExerciseSet.toExerciseSetUiModel(): ExerciseSetUiModel { 48 | return ExerciseSetUiModel( 49 | setId = setId, 50 | exerciseId = exerciseId, 51 | weight = weight, 52 | count = count, 53 | order = order 54 | ) 55 | } -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/ui/model/LoadingState.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight.ui.model 2 | 3 | enum class LoadingState { 4 | NONE, 5 | RESTORE, 6 | GET, 7 | BACKUP, 8 | FAIL, 9 | } -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/ui/model/ParentDayUiModel.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight.ui.model 2 | 3 | abstract class ParentDayUiModel( 4 | open val order: Int, 5 | open val exercises: List 6 | ) 7 | -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/ui/model/ParentExerciseSetUiModel.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight.ui.model 2 | 3 | abstract class ParentExerciseSetUiModel( 4 | open val weight: String, 5 | open val count: String, 6 | open val order: Int 7 | ) -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/ui/model/ParentExerciseUiModel.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight.ui.model 2 | 3 | import com.lateinit.rightweight.ui.model.routine.ExercisePartTypeUiModel 4 | 5 | abstract class ParentExerciseUiModel( 6 | open var title: String, 7 | open val part: ExercisePartTypeUiModel, 8 | open val expanded: Boolean, 9 | open val exerciseSets: List 10 | ) -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/ui/model/ParentRoutineUiModel.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight.ui.model 2 | 3 | import java.time.LocalDateTime 4 | 5 | abstract class ParentRoutineUiModel( 6 | open val routineId: String, 7 | open val title: String, 8 | open val author: String, 9 | open val description: String, 10 | open val modifiedDate: LocalDateTime, 11 | ) -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/ui/model/history/HistoryExerciseSetUiModel.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight.ui.model.history 2 | 3 | import com.lateinit.rightweight.ui.model.ParentExerciseSetUiModel 4 | 5 | data class HistoryExerciseSetUiModel( 6 | val setId: String, 7 | val exerciseId: String, 8 | override var weight: String, 9 | override var count: String, 10 | override val order: Int, 11 | val checked: Boolean 12 | ) : ParentExerciseSetUiModel(weight, count, order) 13 | -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/ui/model/history/HistoryExerciseUiModel.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight.ui.model.history 2 | 3 | import com.lateinit.rightweight.ui.model.ParentExerciseUiModel 4 | import com.lateinit.rightweight.ui.model.routine.ExercisePartTypeUiModel 5 | 6 | data class HistoryExerciseUiModel( 7 | val exerciseId: String, 8 | val historyId: String, 9 | override var title: String, 10 | val order: Int, 11 | override val part: ExercisePartTypeUiModel, 12 | override val expanded: Boolean = true, 13 | override val exerciseSets: List 14 | ) : ParentExerciseUiModel(title, part, expanded, exerciseSets) 15 | -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/ui/model/history/HistoryUiModel.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight.ui.model.history 2 | 3 | import com.lateinit.rightweight.ui.model.ParentDayUiModel 4 | import java.time.LocalDate 5 | 6 | data class HistoryUiModel( 7 | val historyId: String, 8 | val date: LocalDate, 9 | val time: String, 10 | val routineTitle: String, 11 | override val order: Int, 12 | val completed: Boolean, 13 | override val exercises: List 14 | ) : ParentDayUiModel(order, exercises) -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/ui/model/routine/DayUiModel.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight.ui.model.routine 2 | 3 | import com.lateinit.rightweight.ui.model.ParentDayUiModel 4 | 5 | data class DayUiModel( 6 | val dayId: String, 7 | val routineId: String, 8 | override val order: Int, 9 | val selected: Boolean, 10 | override val exercises: List = emptyList() 11 | ) : ParentDayUiModel(order, exercises) -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/ui/model/routine/ExercisePartTypeUiModel.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight.ui.model.routine 2 | 3 | import androidx.annotation.StringRes 4 | import com.lateinit.rightweight.R 5 | 6 | enum class ExercisePartTypeUiModel(@StringRes val partName: Int) { 7 | CHEST(R.string.chest), 8 | BACK(R.string.back), 9 | LEG(R.string.leg), 10 | SHOULDER(R.string.shoulder), 11 | BICEPS(R.string.biceps), 12 | TRICEPS(R.string.triceps), 13 | CORE(R.string.core), 14 | FOREARM(R.string.forearm), 15 | CARDIO(R.string.cardio) 16 | } -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/ui/model/routine/ExerciseSetUiModel.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight.ui.model.routine 2 | 3 | import com.lateinit.rightweight.ui.model.ParentExerciseSetUiModel 4 | 5 | data class ExerciseSetUiModel( 6 | val setId: String, 7 | val exerciseId: String, 8 | override var weight: String = "", 9 | override var count: String = "", 10 | override val order: Int 11 | ) : ParentExerciseSetUiModel(weight, count, order) 12 | -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/ui/model/routine/ExerciseUiModel.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight.ui.model.routine 2 | 3 | import com.lateinit.rightweight.ui.model.ParentExerciseUiModel 4 | 5 | data class ExerciseUiModel( 6 | val exerciseId: String, 7 | val dayId: String, 8 | override var title: String, 9 | val order: Int, 10 | override val part: ExercisePartTypeUiModel = ExercisePartTypeUiModel.CHEST, 11 | override val expanded: Boolean = true, 12 | override val exerciseSets: List = emptyList() 13 | ) : ParentExerciseUiModel(title, part, expanded, exerciseSets) 14 | -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/ui/model/routine/RoutineUiModel.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight.ui.model.routine 2 | 3 | import com.lateinit.rightweight.ui.model.ParentRoutineUiModel 4 | import java.time.LocalDateTime 5 | 6 | data class RoutineUiModel( 7 | override val routineId: String, 8 | override val title: String, 9 | override val author: String, 10 | override val description: String, 11 | override val modifiedDate: LocalDateTime, 12 | val order: Int 13 | ): ParentRoutineUiModel(routineId, title, author, description, modifiedDate) -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/ui/model/shared/SharedRoutineSortTypeUiModel.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight.ui.model.shared 2 | 3 | import androidx.annotation.StringRes 4 | import com.lateinit.rightweight.R 5 | 6 | enum class SharedRoutineSortTypeUiModel(@StringRes val sortTypeName: Int){ 7 | MODIFIED_DATE_FIRST(R.string.modified_date_sort), SHARED_COUNT_FIRST(R.string.shared_count_sort) 8 | } 9 | -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/ui/model/shared/SharedRoutineUiModel.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight.ui.model.shared 2 | 3 | import com.lateinit.rightweight.ui.model.ParentRoutineUiModel 4 | import java.time.LocalDateTime 5 | 6 | data class SharedRoutineUiModel( 7 | override val routineId: String, 8 | override val title: String, 9 | override val author: String, 10 | override val description: String, 11 | override val modifiedDate: LocalDateTime, 12 | val sharedCount: String 13 | ): ParentRoutineUiModel(routineId, title, author, description, modifiedDate) -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/ui/routine/detail/DetailExerciseAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight.ui.routine.detail 2 | 3 | import android.view.LayoutInflater 4 | import android.view.ViewGroup 5 | import androidx.recyclerview.widget.DiffUtil 6 | import androidx.recyclerview.widget.ListAdapter 7 | import androidx.recyclerview.widget.RecyclerView 8 | import com.lateinit.rightweight.R 9 | import com.lateinit.rightweight.databinding.ItemExerciseWithSetsBinding 10 | import com.lateinit.rightweight.ui.model.routine.ExerciseUiModel 11 | 12 | class DetailExerciseAdapter( 13 | private val onClickExercise: (Int) -> Unit 14 | ) : 15 | ListAdapter(diffUtil) { 16 | 17 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DetailExerciseViewHolder { 18 | return DetailExerciseViewHolder(parent, onClickExercise) 19 | } 20 | 21 | override fun onBindViewHolder(holder: DetailExerciseViewHolder, position: Int) { 22 | holder.bind(getItem(position) ?: return) 23 | } 24 | 25 | class DetailExerciseViewHolder( 26 | parent: ViewGroup, 27 | onClickExercise: (Int) -> Unit 28 | ) : RecyclerView.ViewHolder( 29 | LayoutInflater.from(parent.context).inflate(R.layout.item_exercise_with_sets, parent, false) 30 | ) { 31 | private val binding = ItemExerciseWithSetsBinding.bind(itemView) 32 | 33 | private lateinit var exerciseUiModel: ExerciseUiModel 34 | private val detailExerciseSetAdapter = DetailExerciseSetAdapter() 35 | 36 | init { 37 | itemView.setOnClickListener { 38 | if (exerciseUiModel.expanded) { 39 | binding.imageExpandedState.animate().setDuration(200).rotation(0f) 40 | } else { 41 | binding.imageExpandedState.animate().setDuration(200).rotation(180f) 42 | } 43 | onClickExercise(layoutPosition) 44 | } 45 | } 46 | 47 | fun bind(exerciseUiModel: ExerciseUiModel) { 48 | this.exerciseUiModel = exerciseUiModel 49 | binding.exerciseUiModel = exerciseUiModel 50 | 51 | binding.recyclerViewSet.apply { 52 | adapter = detailExerciseSetAdapter 53 | itemAnimator = null 54 | } 55 | val exerciseSets = exerciseUiModel.exerciseSets 56 | detailExerciseSetAdapter.submitList(exerciseSets) 57 | 58 | binding.executePendingBindings() 59 | } 60 | } 61 | 62 | companion object { 63 | val diffUtil = object : DiffUtil.ItemCallback() { 64 | override fun areItemsTheSame( 65 | oldItem: ExerciseUiModel, 66 | newItem: ExerciseUiModel 67 | ): Boolean { 68 | return oldItem.exerciseId == newItem.exerciseId 69 | } 70 | 71 | override fun areContentsTheSame( 72 | oldItem: ExerciseUiModel, 73 | newItem: ExerciseUiModel 74 | ): Boolean { 75 | println() 76 | return false 77 | } 78 | } 79 | } 80 | } -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/ui/routine/detail/DetailExerciseSetAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight.ui.routine.detail 2 | 3 | import android.view.LayoutInflater 4 | import android.view.ViewGroup 5 | import androidx.recyclerview.widget.DiffUtil 6 | import androidx.recyclerview.widget.ListAdapter 7 | import androidx.recyclerview.widget.RecyclerView 8 | import com.lateinit.rightweight.R 9 | import com.lateinit.rightweight.databinding.ItemSetReadBinding 10 | import com.lateinit.rightweight.ui.model.routine.ExerciseSetUiModel 11 | 12 | class DetailExerciseSetAdapter : 13 | ListAdapter(diffUtil) { 14 | 15 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DetailExerciseSetViewHolder { 16 | return DetailExerciseSetViewHolder(parent) 17 | } 18 | 19 | override fun onBindViewHolder(holder: DetailExerciseSetViewHolder, position: Int) { 20 | holder.bind(getItem(position)) 21 | } 22 | 23 | inner class DetailExerciseSetViewHolder(parent: ViewGroup) : RecyclerView.ViewHolder( 24 | LayoutInflater.from(parent.context).inflate(R.layout.item_set_read, parent, false) 25 | ) { 26 | private val binding = ItemSetReadBinding.bind(itemView) 27 | 28 | fun bind(exerciseSetUiModel: ExerciseSetUiModel) { 29 | binding.exerciseSetUiModel = exerciseSetUiModel 30 | binding.executePendingBindings() 31 | } 32 | } 33 | 34 | companion object { 35 | val diffUtil = object : DiffUtil.ItemCallback() { 36 | override fun areItemsTheSame( 37 | oldItem: ExerciseSetUiModel, 38 | newItem: ExerciseSetUiModel 39 | ): Boolean { 40 | return oldItem.setId == newItem.setId 41 | } 42 | 43 | override fun areContentsTheSame( 44 | oldItem: ExerciseSetUiModel, 45 | newItem: ExerciseSetUiModel 46 | ): Boolean { 47 | return oldItem == newItem 48 | } 49 | 50 | } 51 | } 52 | } -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/ui/routine/editor/RoutineDayAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight.ui.routine.editor 2 | 3 | 4 | import android.view.LayoutInflater 5 | import android.view.ViewGroup 6 | import androidx.recyclerview.widget.DiffUtil 7 | import androidx.recyclerview.widget.ListAdapter 8 | import androidx.recyclerview.widget.RecyclerView 9 | import com.lateinit.rightweight.R 10 | import com.lateinit.rightweight.databinding.ItemDayBinding 11 | import com.lateinit.rightweight.ui.model.routine.DayUiModel 12 | 13 | class RoutineDayAdapter( 14 | private val onClickDay: (Int) -> Unit 15 | ) : ListAdapter(diffUtil) { 16 | 17 | override fun onCreateViewHolder( 18 | parent: ViewGroup, 19 | viewType: Int 20 | ): DayViewHolder { 21 | return DayViewHolder(parent) 22 | } 23 | 24 | override fun onBindViewHolder(holder: DayViewHolder, position: Int) { 25 | holder.bind(getItem(position)) 26 | } 27 | 28 | inner class DayViewHolder(parent: ViewGroup) : RecyclerView.ViewHolder( 29 | LayoutInflater.from(parent.context).inflate(R.layout.item_day, parent, false) 30 | ) { 31 | private val binding = ItemDayBinding.bind(itemView) 32 | 33 | init { 34 | itemView.setOnClickListener { 35 | onClickDay(layoutPosition) 36 | } 37 | } 38 | 39 | fun bind(dayUiModel: DayUiModel) { 40 | binding.dayUiModel = dayUiModel 41 | binding.executePendingBindings() 42 | } 43 | 44 | } 45 | 46 | companion object { 47 | val diffUtil = object : DiffUtil.ItemCallback() { 48 | override fun areItemsTheSame(oldItem: DayUiModel, newItem: DayUiModel): Boolean { 49 | return oldItem.dayId == newItem.dayId 50 | } 51 | 52 | override fun areContentsTheSame(oldItem: DayUiModel, newItem: DayUiModel): Boolean { 53 | return oldItem == newItem 54 | } 55 | 56 | } 57 | } 58 | 59 | } -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/ui/routine/editor/RoutineSetAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight.ui.routine.editor 2 | 3 | import android.view.LayoutInflater 4 | import android.view.ViewGroup 5 | import androidx.recyclerview.widget.DiffUtil 6 | import androidx.recyclerview.widget.ListAdapter 7 | import androidx.recyclerview.widget.RecyclerView 8 | import com.lateinit.rightweight.R 9 | import com.lateinit.rightweight.databinding.ItemSetBinding 10 | import com.lateinit.rightweight.ui.model.routine.ExerciseSetUiModel 11 | 12 | class RoutineSetAdapter(val exerciseEventListener: RoutineExerciseAdapter.ExerciseEventListener) : 13 | ListAdapter(diffUtil) { 14 | 15 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SetViewHolder { 16 | return SetViewHolder(parent) 17 | } 18 | 19 | override fun onBindViewHolder(holder: SetViewHolder, position: Int) { 20 | holder.bind(getItem(position)) 21 | } 22 | 23 | inner class SetViewHolder(parent: ViewGroup) : RecyclerView.ViewHolder( 24 | LayoutInflater.from(parent.context).inflate(R.layout.item_set, parent, false) 25 | ) { 26 | private val binding = ItemSetBinding.bind(itemView) 27 | 28 | fun bind(exerciseSetUiModel: ExerciseSetUiModel) { 29 | binding.exerciseSetUiModel = exerciseSetUiModel 30 | binding.executePendingBindings() 31 | binding.buttonSetRemove.setOnClickListener { 32 | exerciseEventListener.onSetRemove(exerciseSetUiModel.exerciseId, layoutPosition) 33 | } 34 | } 35 | } 36 | 37 | companion object { 38 | val diffUtil = object : DiffUtil.ItemCallback() { 39 | override fun areItemsTheSame(oldItem: ExerciseSetUiModel, newItem: ExerciseSetUiModel): Boolean { 40 | return oldItem.setId == newItem.setId 41 | } 42 | 43 | override fun areContentsTheSame(oldItem: ExerciseSetUiModel, newItem: ExerciseSetUiModel): Boolean { 44 | return oldItem == newItem 45 | } 46 | 47 | } 48 | } 49 | } -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/ui/routine/management/RoutineAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight.ui.routine.management 2 | 3 | import android.view.LayoutInflater 4 | import android.view.ViewGroup 5 | import androidx.recyclerview.widget.DiffUtil 6 | import androidx.recyclerview.widget.ListAdapter 7 | import androidx.recyclerview.widget.RecyclerView 8 | import com.lateinit.rightweight.databinding.ItemRoutineBinding 9 | import com.lateinit.rightweight.ui.model.routine.RoutineUiModel 10 | 11 | class RoutineAdapter( 12 | private val routineEventListener: RoutineEventListener 13 | ) : ListAdapter(diffUtil) { 14 | 15 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RoutineViewHolder { 16 | return RoutineViewHolder( 17 | ItemRoutineBinding.inflate( 18 | LayoutInflater.from(parent.context), 19 | parent, 20 | false 21 | ), 22 | routineEventListener 23 | ) 24 | } 25 | 26 | override fun onBindViewHolder(holder: RoutineViewHolder, position: Int) { 27 | holder.bind(getItem(position)) 28 | } 29 | 30 | class RoutineViewHolder( 31 | private val binding: ItemRoutineBinding, 32 | private val routineEventListener: RoutineEventListener 33 | ) : RecyclerView.ViewHolder(binding.root) { 34 | 35 | private lateinit var routineUiModel: RoutineUiModel 36 | 37 | init { 38 | binding.imageButtonUp.setOnClickListener { 39 | routineEventListener.moveUp(layoutPosition) 40 | } 41 | binding.imageButtonDown.setOnClickListener { 42 | routineEventListener.moveDown(layoutPosition) 43 | } 44 | binding.cardViewRoutineItemContainer.setOnClickListener { 45 | routineEventListener.onClick(routineUiModel.routineId) 46 | } 47 | } 48 | 49 | fun bind(routineUiModel: RoutineUiModel) { 50 | this.routineUiModel = routineUiModel 51 | binding.routineUiModel = routineUiModel 52 | } 53 | } 54 | 55 | interface RoutineEventListener { 56 | 57 | fun moveUp(routinePosition: Int) 58 | 59 | fun moveDown(routinePosition: Int) 60 | 61 | fun onClick(routineId: String) 62 | } 63 | 64 | companion object { 65 | private val diffUtil = object : DiffUtil.ItemCallback() { 66 | override fun areItemsTheSame(oldItem: RoutineUiModel, newItem: RoutineUiModel): Boolean { 67 | return oldItem.routineId == newItem.routineId 68 | } 69 | 70 | override fun areContentsTheSame(oldItem: RoutineUiModel, newItem: RoutineUiModel): Boolean { 71 | return oldItem == newItem 72 | } 73 | } 74 | } 75 | } -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/ui/share/SharedRoutineClickHandler.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight.ui.share 2 | 3 | interface SharedRoutineClickHandler { 4 | fun gotoSharedRoutineDetailFragment(routineId: String) 5 | } -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/ui/share/SharedRoutinePagingAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight.ui.share 2 | 3 | import android.view.LayoutInflater 4 | import android.view.ViewGroup 5 | import androidx.paging.PagingDataAdapter 6 | import androidx.recyclerview.widget.DiffUtil 7 | import androidx.recyclerview.widget.RecyclerView 8 | import com.lateinit.rightweight.databinding.ItemSharedRoutineBinding 9 | import com.lateinit.rightweight.ui.model.shared.SharedRoutineUiModel 10 | 11 | class SharedRoutinePagingAdapter( 12 | private val sharedRoutineClickHandler: SharedRoutineClickHandler 13 | ) : PagingDataAdapter( 14 | diffUtil 15 | ) { 16 | 17 | override fun onCreateViewHolder( 18 | parent: ViewGroup, 19 | viewType: Int 20 | ): SharedRoutineViewHolder { 21 | val binding = 22 | ItemSharedRoutineBinding.inflate(LayoutInflater.from(parent.context), parent, false) 23 | return SharedRoutineViewHolder(binding, sharedRoutineClickHandler) 24 | } 25 | 26 | override fun onBindViewHolder(holder: SharedRoutineViewHolder, position: Int) { 27 | getItem(position)?.let { 28 | holder.setItem(getItem(position)) 29 | } 30 | } 31 | 32 | class SharedRoutineViewHolder( 33 | val binding: ItemSharedRoutineBinding, 34 | private val sharedRoutineClickHandler: SharedRoutineClickHandler 35 | ) : RecyclerView.ViewHolder(binding.root) { 36 | fun setItem(item: SharedRoutineUiModel?) { 37 | binding.sharedRoutineUiModel = item 38 | binding.sharedRoutineClickHandler = sharedRoutineClickHandler 39 | } 40 | } 41 | 42 | companion object { 43 | val diffUtil = object : DiffUtil.ItemCallback() { 44 | override fun areItemsTheSame( 45 | oldItem: SharedRoutineUiModel, 46 | newItem: SharedRoutineUiModel 47 | ): Boolean { 48 | return oldItem.routineId == newItem.routineId 49 | } 50 | 51 | override fun areContentsTheSame( 52 | oldItem: SharedRoutineUiModel, 53 | newItem: SharedRoutineUiModel 54 | ): Boolean { 55 | return oldItem == newItem 56 | } 57 | 58 | } 59 | } 60 | 61 | } -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/ui/share/SharedRoutineViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight.ui.share 2 | 3 | import androidx.lifecycle.ViewModel 4 | import androidx.lifecycle.viewModelScope 5 | import androidx.paging.PagingData 6 | import androidx.paging.cachedIn 7 | import androidx.paging.map 8 | import com.lateinit.rightweight.data.mapper.local.toSharedRoutineSortType 9 | import com.lateinit.rightweight.data.repository.SharedRoutineRepository 10 | import com.lateinit.rightweight.ui.login.NetworkState 11 | import com.lateinit.rightweight.ui.mapper.toSharedRoutineUiModel 12 | import com.lateinit.rightweight.ui.model.shared.SharedRoutineSortTypeUiModel 13 | import com.lateinit.rightweight.ui.model.shared.SharedRoutineUiModel 14 | import dagger.hilt.android.lifecycle.HiltViewModel 15 | import kotlinx.coroutines.CoroutineExceptionHandler 16 | import kotlinx.coroutines.flow.MutableStateFlow 17 | import kotlinx.coroutines.flow.StateFlow 18 | import kotlinx.coroutines.launch 19 | import retrofit2.HttpException 20 | import java.net.SocketException 21 | import java.net.UnknownHostException 22 | import javax.inject.Inject 23 | 24 | @HiltViewModel 25 | class SharedRoutineViewModel @Inject constructor( 26 | private val sharedRoutineRepository: SharedRoutineRepository 27 | ) : ViewModel() { 28 | private val _uiState = 29 | MutableStateFlow(LatestSharedRoutineUiState.Success(PagingData.empty())) 30 | val uiState: StateFlow = _uiState 31 | 32 | val networkExceptionHandler = CoroutineExceptionHandler { _, throwable -> 33 | when (throwable) { 34 | is SocketException -> sendNetworkResultEvent(NetworkState.BAD_INTERNET) 35 | is HttpException -> sendNetworkResultEvent(NetworkState.PARSE_ERROR) 36 | is UnknownHostException -> sendNetworkResultEvent(NetworkState.WRONG_CONNECTION) 37 | else -> sendNetworkResultEvent(NetworkState.OTHER_ERROR) 38 | } 39 | } 40 | 41 | init { 42 | getSharedRoutinesByPaging() 43 | } 44 | 45 | private fun sendNetworkResultEvent(state: NetworkState) { 46 | viewModelScope.launch { 47 | _uiState.value = LatestSharedRoutineUiState.Error(state) 48 | } 49 | } 50 | 51 | private fun getSharedRoutinesByPaging() { 52 | viewModelScope.launch(networkExceptionHandler) { 53 | sharedRoutineRepository.getSharedRoutinesByPaging().cachedIn(this) 54 | .collect { sharedRoutinePagingData -> 55 | val sharedRoutines = sharedRoutinePagingData.map { sharedRoutine -> 56 | sharedRoutine.toSharedRoutineUiModel() 57 | } 58 | _uiState.value = LatestSharedRoutineUiState.Success(sharedRoutines) 59 | } 60 | } 61 | } 62 | 63 | fun setSharedRoutineSortType(sortTypeUiModel: SharedRoutineSortTypeUiModel) { 64 | getSharedRoutinesByPaging() 65 | viewModelScope.launch { 66 | sharedRoutineRepository.setSharedRoutineSortType(sortTypeUiModel.toSharedRoutineSortType()) 67 | } 68 | } 69 | 70 | } 71 | 72 | sealed class LatestSharedRoutineUiState { 73 | data class Success(val sharedRoutines: PagingData) : 74 | LatestSharedRoutineUiState() 75 | 76 | data class Error(val state: NetworkState) : LatestSharedRoutineUiState() 77 | } 78 | -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/util/CenterSmoothScroller.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight.util 2 | 3 | import android.content.Context 4 | import androidx.recyclerview.widget.LinearSmoothScroller 5 | 6 | 7 | class CenterSmoothScroller(context: Context?) : LinearSmoothScroller(context) { 8 | override fun calculateDtToFit( 9 | viewStart: Int, 10 | viewEnd: Int, 11 | boxStart: Int, 12 | boxEnd: Int, 13 | snapPreference: Int 14 | ): Int { 15 | return boxStart + (boxEnd - boxStart) / 2 - (viewStart + (viewEnd - viewStart) / 2) 16 | } 17 | } -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/util/Consts.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight.util 2 | 3 | const val FIRST_DAY_POSITION = 0 4 | const val DEFAULT_SET_WEIGHT = "0" 5 | const val DEFAULT_SET_COUNT = "0" 6 | const val DEFAULT_AUTHOR_NAME = "" 7 | const val DEFAULT_ROUTINE_ID = "" -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/util/Extensions.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight.util 2 | 3 | import androidx.lifecycle.Lifecycle 4 | import androidx.lifecycle.LifecycleOwner 5 | import androidx.lifecycle.lifecycleScope 6 | import androidx.lifecycle.repeatOnLifecycle 7 | import kotlinx.coroutines.launch 8 | 9 | fun LifecycleOwner.collectOnLifecycle( 10 | state: Lifecycle.State = Lifecycle.State.STARTED, 11 | block: suspend () -> Unit 12 | ) { 13 | this.lifecycleScope.launch { 14 | repeatOnLifecycle(state) { 15 | block() 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/util/TimeConverter.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight.util 2 | 3 | fun convertTimeStamp(count: Int): String { 4 | val hours: Int = (count / 60) / 60 5 | val minutes: Int = (count / 60) % 60 6 | val seconds: Int = count % 60 7 | 8 | return "${"%02d".format(hours)}:${"%02d".format(minutes)}:${"%02d".format(seconds)}" 9 | } 10 | 11 | fun convertTimeStampToTimeCount(timeStamp: String): Int { 12 | val (hour, minute, second) = timeStamp.split(":").map { it.toInt() } 13 | 14 | return hour * 60 * 60 + minute * 60 + second 15 | } -------------------------------------------------------------------------------- /app/src/main/java/com/lateinit/rightweight/util/UUID.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight.util 2 | 3 | import java.util.UUID 4 | 5 | fun createRandomUUID(): String { 6 | return UUID.randomUUID().toString() 7 | } -------------------------------------------------------------------------------- /app/src/main/res/color/color_completed_day_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/color/color_day_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/color/color_day_text.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 15 | 18 | 21 | 22 | 23 | 24 | 30 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/bg_calendar_day.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/bg_calendar_day_completed.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/bg_day_order.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/bg_day_order_selected.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/bg_exercise_part.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_add.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_arrow_down.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_arrow_up.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_back.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_backup.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_calendar.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_down_arrow.xml: -------------------------------------------------------------------------------- 1 | 6 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_download.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_home.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_logout.xml: -------------------------------------------------------------------------------- 1 | 8 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_more.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_pause.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_play_arrow.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_remove.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_routine_management.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_shared_routine.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_splash_logo.xml: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_up_arrow.xml: -------------------------------------------------------------------------------- 1 | 6 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_withdraw.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_login.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | 11 | 14 | 15 | 25 | 26 | 37 | 38 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 10 | 11 | 15 | 16 | 27 | 28 | 40 | 41 | 50 | 51 | 52 | 60 | 61 | -------------------------------------------------------------------------------- /app/src/main/res/layout/dialog_loading.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 16 | 17 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_shared_routine.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 11 | 12 | 23 | 24 | 34 | 35 | 36 | 44 | 45 | 51 | 52 | 53 | 54 | 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_day.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 11 | 12 | 13 | 17 | 18 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_exercise_part.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_exercise_view_home.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 11 | 12 | 13 | 14 | 18 | 19 | 20 | 38 | 39 | 52 | 53 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_set_read.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 11 | 12 | 15 | 16 | 17 | 18 | 22 | 23 | 35 | 36 | 49 | 50 | 59 | 60 | 71 | 72 | 82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_shared_routine_sort_type.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/src/main/res/layout/layout_day_exercises.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 13 | 14 | 15 | 25 | 26 | 30 | 31 | 42 | 43 | 55 | 56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /app/src/main/res/layout/navigation_header.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 11 | 12 | 13 | 14 | 17 | 18 | 29 | 30 | 39 | 40 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /app/src/main/res/layout/notification_foreground.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 15 | 16 | -------------------------------------------------------------------------------- /app/src/main/res/menu/menu_bottom_navigation.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 9 | 14 | 19 | 24 | -------------------------------------------------------------------------------- /app/src/main/res/menu/menu_navigation_drawer.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 11 | 12 | 16 | -------------------------------------------------------------------------------- /app/src/main/res/menu/menu_routine_detail.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 10 | 11 | 15 | 19 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm-2022/android09-RightWeight/ff6116ee9e49bd8ed493d3c6928d0b9a7f892689/app/src/main/res/mipmap-hdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm-2022/android09-RightWeight/ff6116ee9e49bd8ed493d3c6928d0b9a7f892689/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm-2022/android09-RightWeight/ff6116ee9e49bd8ed493d3c6928d0b9a7f892689/app/src/main/res/mipmap-mdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm-2022/android09-RightWeight/ff6116ee9e49bd8ed493d3c6928d0b9a7f892689/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm-2022/android09-RightWeight/ff6116ee9e49bd8ed493d3c6928d0b9a7f892689/app/src/main/res/mipmap-xhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm-2022/android09-RightWeight/ff6116ee9e49bd8ed493d3c6928d0b9a7f892689/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm-2022/android09-RightWeight/ff6116ee9e49bd8ed493d3c6928d0b9a7f892689/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm-2022/android09-RightWeight/ff6116ee9e49bd8ed493d3c6928d0b9a7f892689/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm-2022/android09-RightWeight/ff6116ee9e49bd8ed493d3c6928d0b9a7f892689/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm-2022/android09-RightWeight/ff6116ee9e49bd8ed493d3c6928d0b9a7f892689/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/values-night/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | #FF000000 5 | #FFFFFFFF 6 | #24292f 7 | #4c5158 8 | #000005 9 | #cfd8dc 10 | #ffffff 11 | #9ea7aa 12 | #ffffff 13 | #000000 14 | #FFBA1B1B 15 | #FFFFDAD4 16 | #FF40C463 17 | #FF1D8F3B 18 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 12 | 13 | 16 | 17 | 21 | 22 | 25 | 26 | 30 | 31 | 34 | 35 | 41 | 42 | 47 | 48 | 51 | -------------------------------------------------------------------------------- /app/src/main/res/values/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16 | 17 | 24 | -------------------------------------------------------------------------------- /app/src/main/res/xml/backup_rules.xml: -------------------------------------------------------------------------------- 1 | 8 | 9 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/xml/data_extraction_rules.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 12 | 13 | 19 | -------------------------------------------------------------------------------- /app/src/test/java/com/lateinit/rightweight/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package com.lateinit.rightweight 2 | 3 | import org.junit.Test 4 | 5 | import org.junit.Assert.* 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * See [testing documentation](http://d.android.com/tools/testing). 11 | */ 12 | class ExampleUnitTest { 13 | @Test 14 | fun addition_isCorrect() { 15 | assertEquals(4, 2 + 2) 16 | } 17 | } -------------------------------------------------------------------------------- /build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("com.android.application") version "7.3.1" apply false 3 | id("com.android.library") version "7.3.1" apply false 4 | id("org.jetbrains.kotlin.android") version "1.7.20" apply false 5 | id("com.google.dagger.hilt.android") version "2.44" apply false 6 | id("com.google.gms.google-services") version "4.3.14" apply false 7 | } 8 | 9 | buildscript { 10 | repositories { 11 | google() 12 | } 13 | dependencies { 14 | val navVersion = "2.5.3" 15 | classpath("androidx.navigation:navigation-safe-args-gradle-plugin:$navVersion") 16 | } 17 | } -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | # AndroidX package structure to make it clearer which packages are bundled with the 15 | # Android operating system, and which are packaged with your app's APK 16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 17 | android.useAndroidX=true 18 | # Kotlin code style for this project: "official" or "obsolete": 19 | kotlin.code.style=official 20 | # Enables namespacing of each library's R class so that its R class includes only the 21 | # resources declared in the library itself and none from the library's dependencies, 22 | # thereby reducing the size of the R class for that library 23 | android.nonTransitiveRClass=true -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm-2022/android09-RightWeight/ff6116ee9e49bd8ed493d3c6928d0b9a7f892689/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Nov 11 11:13:12 KST 2022 2 | distributionBase=GRADLE_USER_HOME 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip 4 | distributionPath=wrapper/dists 5 | zipStorePath=wrapper/dists 6 | zipStoreBase=GRADLE_USER_HOME 7 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | gradlePluginPortal() 4 | google() 5 | mavenCentral() 6 | } 7 | } 8 | dependencyResolutionManagement { 9 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) 10 | repositories { 11 | google() 12 | mavenCentral() 13 | maven { url = uri("https://jitpack.io") } 14 | } 15 | } 16 | rootProject.name = "RightWeight" 17 | include (":app") 18 | --------------------------------------------------------------------------------