A fork of Simple Time Tracker with WearOS support. (Now integrated upstream)
0

Configure Feed

Select the types of activity you want to include in your feed.

add archive restore

razeeman (May 15, 2021, 12:42 PM +0300) a33ddec8 1b8dbecf

+275 -26
+8
app/src/main/java/com/example/util/simpletimetracker/navigation/ScreenResolverImpl.kt
··· 5 5 import androidx.navigation.Navigator 6 6 import androidx.navigation.fragment.FragmentNavigator 7 7 import com.example.util.simpletimetracker.R 8 + import com.example.util.simpletimetracker.feature_archive.dialog.ArchiveDialogFragment 8 9 import com.example.util.simpletimetracker.feature_change_category.view.ChangeCategoryFragment 9 10 import com.example.util.simpletimetracker.feature_change_record.view.ChangeRecordFragment 10 11 import com.example.util.simpletimetracker.feature_change_record_tag.view.ChangeRecordTagFragment ··· 157 158 navController?.navigate( 158 159 R.id.emojiSelectionDialogFragment, 159 160 EmojiSelectionDialogFragment.createBundle(data), 161 + null, 162 + navExtras 163 + ) 164 + Screen.ARCHIVE_DIALOG -> 165 + navController?.navigate( 166 + R.id.archiveDialogFragment, 167 + ArchiveDialogFragment.createBundle(data), 160 168 null, 161 169 navExtras 162 170 )
+5
app/src/main/res/navigation/nav_graph.xml
··· 120 120 android:name="com.example.util.simpletimetracker.feature_dialogs.emojiSelection.view.EmojiSelectionDialogFragment" 121 121 android:label="EmojiSelectionDialogFragment" 122 122 tools:layout="@layout/emoji_selection_dialog_fragment" /> 123 + <dialog 124 + android:id="@+id/archiveDialogFragment" 125 + android:name="com.example.util.simpletimetracker.feature_archive.dialog.ArchiveDialogFragment" 126 + android:label="ArchiveDialogFragment" 127 + tools:layout="@layout/archive_dialog_fragment" /> 123 128 <fragment 124 129 android:id="@+id/recordsAllFragment" 125 130 android:name="com.example.util.simpletimetracker.feature_records_all.view.RecordsAllFragment"
+4
core/src/main/res/values-ru/strings.xml
··· 214 214 <string name="notification_time_started">Начато в %s</string> 215 215 <string name="notification_record_type_goal_time">цель %s</string> 216 216 217 + <!-- Archive --> 218 + <string name="archive_dialog_delete">Удалить</string> 219 + <string name="archive_dialog_restore">Восстановить</string> 220 + 217 221 <!-- Icon emoji category --> 218 222 <string name="emojiGroupSmileys">Смайлы и эмоции</string> 219 223 <string name="emojiGroupPeople">Люди и тело</string>
+4
core/src/main/res/values/strings.xml
··· 224 224 <string name="notification_time_started">Started at %s</string> 225 225 <string name="notification_record_type_goal_time">goal %s</string> 226 226 227 + <!-- Archive --> 228 + <string name="archive_dialog_delete">Delete</string> 229 + <string name="archive_dialog_restore">Restore</string> 230 + 227 231 <!-- Icon emoji category --> 228 232 <string name="emojiGroupSmileys">Smileys &amp; Emotion</string> 229 233 <string name="emojiGroupPeople">People &amp; Body</string>
+9
core/src/main/res/values/styles.xml
··· 101 101 <item name="android:textColor">?colorSecondary</item> 102 102 </style> 103 103 104 + <style name="BottomSheetDialog" parent="Theme.MaterialComponents.Light.BottomSheetDialog"> 105 + <item name="bottomSheetStyle">@style/BottomSheetStyleWrapper</item> 106 + <item name="colorSecondary">@color/colorSecondary</item> 107 + </style> 108 + 109 + <style name="BottomSheetStyleWrapper" parent="Widget.Design.BottomSheet.Modal"> 110 + <item name="android:background">@android:color/transparent</item> 111 + </style> 112 + 104 113 <style name="ChangeRecordTypeInputFieldCard"> 105 114 <item name="cardBackgroundColor">?appBackgroundColor</item> 106 115 <item name="cardPreventCornerOverlap">false</item>
+3
data_local/src/main/java/com/example/util/simpletimetracker/data_local/database/RecordTagDao.kt
··· 24 24 @Query("UPDATE recordTags SET archived = 1 WHERE id = :id") 25 25 suspend fun archive(id: Long) 26 26 27 + @Query("UPDATE recordTags SET archived = 0 WHERE id = :id") 28 + suspend fun restore(id: Long) 29 + 27 30 @Query("DELETE FROM recordTags WHERE id = :id") 28 31 suspend fun delete(id: Long) 29 32
+4 -1
data_local/src/main/java/com/example/util/simpletimetracker/data_local/database/RecordTypeDao.kt
··· 22 22 suspend fun insert(record: RecordTypeDBO): Long 23 23 24 24 @Query("UPDATE recordTypes SET hidden = 1 WHERE id = :id") 25 - suspend fun delete(id: Long) 25 + suspend fun archive(id: Long) 26 + 27 + @Query("UPDATE recordTypes SET hidden = 0 WHERE id = :id") 28 + suspend fun restore(id: Long) 26 29 27 30 @Query("DELETE FROM recordTypes") 28 31 suspend fun clear()
+5
data_local/src/main/java/com/example/util/simpletimetracker/data_local/repo/RecordTagRepoImpl.kt
··· 41 41 dao.archive(id) 42 42 } 43 43 44 + override suspend fun restore(id: Long) = withContext(Dispatchers.IO) { 45 + Timber.d("restore") 46 + dao.restore(id) 47 + } 48 + 44 49 override suspend fun remove(id: Long) = withContext(Dispatchers.IO) { 45 50 Timber.d("remove") 46 51 dao.delete(id)
+8 -3
data_local/src/main/java/com/example/util/simpletimetracker/data_local/repo/RecordTypeRepoImpl.kt
··· 38 38 ) 39 39 } 40 40 41 - override suspend fun remove(id: Long) = withContext(Dispatchers.IO) { 42 - Timber.d("remove") 43 - recordTypeDao.delete(id) 41 + override suspend fun archive(id: Long) = withContext(Dispatchers.IO) { 42 + Timber.d("archive") 43 + recordTypeDao.archive(id) 44 + } 45 + 46 + override suspend fun restore(id: Long) = withContext(Dispatchers.IO) { 47 + Timber.d("restore") 48 + recordTypeDao.restore(id) 44 49 } 45 50 46 51 override suspend fun clear() = withContext(Dispatchers.IO) {
+4
domain/src/main/java/com/example/util/simpletimetracker/domain/interactor/RecordTagInteractor.kt
··· 39 39 suspend fun archive(id: Long) { 40 40 repo.archive(id) 41 41 } 42 + 43 + suspend fun restore(id: Long) { 44 + repo.restore(id) 45 + } 42 46 }
+7 -2
domain/src/main/java/com/example/util/simpletimetracker/domain/interactor/RecordTypeInteractor.kt
··· 38 38 return addedId 39 39 } 40 40 41 - suspend fun remove(id: Long) { 42 - recordTypeRepo.remove(id) 41 + suspend fun archive(id: Long) { 42 + recordTypeRepo.archive(id) 43 + recordTypeCacheRepo.clear() 44 + } 45 + 46 + suspend fun restore(id: Long) { 47 + recordTypeRepo.restore(id) 43 48 recordTypeCacheRepo.clear() 44 49 } 45 50
+2
domain/src/main/java/com/example/util/simpletimetracker/domain/repo/RecordTagRepo.kt
··· 14 14 15 15 suspend fun archive(id: Long) 16 16 17 + suspend fun restore(id: Long) 18 + 17 19 suspend fun remove(id: Long) 18 20 19 21 suspend fun clear()
+3 -1
domain/src/main/java/com/example/util/simpletimetracker/domain/repo/RecordTypeRepo.kt
··· 12 12 13 13 suspend fun add(recordType: RecordType): Long 14 14 15 - suspend fun remove(id: Long) 15 + suspend fun archive(id: Long) 16 + 17 + suspend fun restore(id: Long) 16 18 17 19 suspend fun clear() 18 20 }
+86
feature_archive/src/main/java/com/example/util/simpletimetracker/feature_archive/dialog/ArchiveDialogFragment.kt
··· 1 + package com.example.util.simpletimetracker.feature_archive.dialog 2 + 3 + import android.content.Context 4 + import android.os.Bundle 5 + import android.view.LayoutInflater 6 + import android.view.View 7 + import android.view.ViewGroup 8 + import androidx.appcompat.app.AppCompatActivity 9 + import androidx.fragment.app.DialogFragment 10 + import com.example.util.simpletimetracker.core.extension.getAllFragments 11 + import com.example.util.simpletimetracker.core.extension.setOnClick 12 + import com.example.util.simpletimetracker.core.extension.setSkipCollapsed 13 + import com.example.util.simpletimetracker.feature_archive.R 14 + import com.google.android.material.bottomsheet.BottomSheetDialogFragment 15 + import kotlinx.android.synthetic.main.archive_dialog_fragment.* 16 + 17 + class ArchiveDialogFragment : BottomSheetDialogFragment() { 18 + 19 + private val params: ArchiveDialogParams? by lazy { 20 + arguments?.getParcelable<ArchiveDialogParams>(ARGS_PARAMS) 21 + } 22 + private var archiveDialogListener: ArchiveDialogListener? = null 23 + 24 + override fun onCreate(savedInstanceState: Bundle?) { 25 + super.onCreate(savedInstanceState) 26 + setStyle(DialogFragment.STYLE_NORMAL, R.style.BottomSheetDialog) 27 + } 28 + 29 + override fun onCreateView( 30 + inflater: LayoutInflater, 31 + container: ViewGroup?, 32 + savedInstanceState: Bundle? 33 + ): View? { 34 + return inflater.inflate( 35 + R.layout.archive_dialog_fragment, 36 + container, 37 + false 38 + ) 39 + } 40 + 41 + override fun onActivityCreated(savedInstanceState: Bundle?) { 42 + super.onActivityCreated(savedInstanceState) 43 + initDialog() 44 + initUx() 45 + } 46 + 47 + override fun onAttach(context: Context) { 48 + super.onAttach(context) 49 + when (context) { 50 + is ArchiveDialogListener -> { 51 + archiveDialogListener = context 52 + return 53 + } 54 + is AppCompatActivity -> { 55 + context.getAllFragments() 56 + .firstOrNull { it is ArchiveDialogListener && it.isResumed } 57 + ?.let { archiveDialogListener = it as? ArchiveDialogListener } 58 + } 59 + } 60 + } 61 + 62 + private fun initDialog() { 63 + setSkipCollapsed() 64 + } 65 + 66 + private fun initUx() { 67 + btnArchiveDialogDelete.setOnClick { 68 + archiveDialogListener?.onDeleteClick(params) 69 + dismiss() 70 + } 71 + btnArchiveDialogRestore.setOnClick { 72 + archiveDialogListener?.onRestoreClick(params) 73 + dismiss() 74 + } 75 + } 76 + 77 + companion object { 78 + private const val ARGS_PARAMS = "args_params" 79 + 80 + fun createBundle(data: Any?): Bundle = Bundle().apply { 81 + when (data) { 82 + is ArchiveDialogParams -> putParcelable(ARGS_PARAMS, data) 83 + } 84 + } 85 + } 86 + }
+7
feature_archive/src/main/java/com/example/util/simpletimetracker/feature_archive/dialog/ArchiveDialogListener.kt
··· 1 + package com.example.util.simpletimetracker.feature_archive.dialog 2 + 3 + interface ArchiveDialogListener { 4 + 5 + fun onDeleteClick(params: ArchiveDialogParams?) 6 + fun onRestoreClick(params: ArchiveDialogParams?) 7 + }
+13
feature_archive/src/main/java/com/example/util/simpletimetracker/feature_archive/dialog/ArchiveDialogParams.kt
··· 1 + package com.example.util.simpletimetracker.feature_archive.dialog 2 + 3 + import android.os.Parcelable 4 + import kotlinx.android.parcel.Parcelize 5 + 6 + sealed class ArchiveDialogParams : Parcelable { 7 + 8 + @Parcelize 9 + data class Activity(val id: Long) : ArchiveDialogParams() 10 + 11 + @Parcelize 12 + data class RecordTag(val id: Long) : ArchiveDialogParams() 13 + }
+2
feature_archive/src/main/java/com/example/util/simpletimetracker/feature_archive/interactor/ArchiveViewDataInteractor.kt
··· 63 63 recordTagsViewData.let(result::addAll) 64 64 } 65 65 66 + // TODO show hint if empty 67 + 66 68 return result 67 69 } 68 70 }
+10 -1
feature_archive/src/main/java/com/example/util/simpletimetracker/feature_archive/view/ArchiveFragment.kt
··· 12 12 import com.example.util.simpletimetracker.core.di.BaseViewModelFactory 13 13 import com.example.util.simpletimetracker.feature_archive.R 14 14 import com.example.util.simpletimetracker.feature_archive.di.ArchiveComponentProvider 15 + import com.example.util.simpletimetracker.feature_archive.dialog.ArchiveDialogListener 16 + import com.example.util.simpletimetracker.feature_archive.dialog.ArchiveDialogParams 15 17 import com.example.util.simpletimetracker.feature_archive.viewModel.ArchiveViewModel 16 18 import com.google.android.flexbox.FlexDirection 17 19 import com.google.android.flexbox.FlexWrap ··· 20 22 import kotlinx.android.synthetic.main.archive_fragment.* 21 23 import javax.inject.Inject 22 24 23 - class ArchiveFragment : BaseFragment(R.layout.archive_fragment) { 25 + class ArchiveFragment : BaseFragment(R.layout.archive_fragment), 26 + ArchiveDialogListener { 24 27 25 28 @Inject 26 29 lateinit var viewModelFactory: BaseViewModelFactory<ArchiveViewModel> ··· 59 62 override fun initViewModel(): Unit = with(viewModel) { 60 63 viewData.observe(viewLifecycleOwner, archiveAdapter::replace) 61 64 } 65 + 66 + override fun onDeleteClick(params: ArchiveDialogParams?) = 67 + viewModel.onDeleteClick(params) 68 + 69 + override fun onRestoreClick(params: ArchiveDialogParams?) = 70 + viewModel.onRestoreClick(params) 62 71 63 72 companion object { 64 73 fun newInstance() = ArchiveFragment()
+37 -4
feature_archive/src/main/java/com/example/util/simpletimetracker/feature_archive/viewModel/ArchiveViewModel.kt
··· 9 9 import com.example.util.simpletimetracker.core.adapter.loader.LoaderViewData 10 10 import com.example.util.simpletimetracker.core.extension.set 11 11 import com.example.util.simpletimetracker.core.viewData.RecordTypeViewData 12 + import com.example.util.simpletimetracker.domain.interactor.RecordTagInteractor 13 + import com.example.util.simpletimetracker.domain.interactor.RecordTypeInteractor 14 + import com.example.util.simpletimetracker.domain.model.TagType 15 + import com.example.util.simpletimetracker.feature_archive.dialog.ArchiveDialogParams 12 16 import com.example.util.simpletimetracker.feature_archive.interactor.ArchiveViewDataInteractor 13 17 import com.example.util.simpletimetracker.navigation.Router 18 + import com.example.util.simpletimetracker.navigation.Screen 14 19 import kotlinx.coroutines.launch 15 20 import javax.inject.Inject 16 21 17 22 class ArchiveViewModel @Inject constructor( 18 23 private val router: Router, 19 - private val archiveViewDataInteractor: ArchiveViewDataInteractor 24 + private val archiveViewDataInteractor: ArchiveViewDataInteractor, 25 + private val recordTypeInteractor: RecordTypeInteractor, 26 + private val recordTagInteractor: RecordTagInteractor 20 27 ) : ViewModel() { 21 28 22 29 val viewData: LiveData<List<ViewHolderType>> by lazy { ··· 30 37 } 31 38 32 39 fun onRecordTypeClick(item: RecordTypeViewData) { 33 - // TODO 40 + router.navigate( 41 + screen = Screen.ARCHIVE_DIALOG, 42 + data = ArchiveDialogParams.Activity(item.id) 43 + ) 34 44 } 35 45 36 46 fun onCategoryClick(item: CategoryViewData) { 37 - // TODO 47 + if (item.type == TagType.RECORD) router.navigate( 48 + screen = Screen.ARCHIVE_DIALOG, 49 + data = ArchiveDialogParams.RecordTag(item.id) 50 + ) 51 + } 52 + 53 + fun onDeleteClick(params: ArchiveDialogParams?) { 54 + // TODO show confirmation and delete 38 55 } 39 56 40 - private fun updateViewData() = viewModelScope.launch { 57 + fun onRestoreClick(params: ArchiveDialogParams?) { 58 + viewModelScope.launch { 59 + when (params) { 60 + is ArchiveDialogParams.Activity -> { 61 + recordTypeInteractor.restore(params.id) 62 + // TODO show message 63 + } 64 + is ArchiveDialogParams.RecordTag -> { 65 + recordTagInteractor.restore(params.id) 66 + // TODO show message 67 + } 68 + } 69 + updateViewData() 70 + } 71 + } 72 + 73 + private suspend fun updateViewData() { 41 74 val data = loadViewData() 42 75 viewData.set(data) 43 76 }
+51
feature_archive/src/main/res/layout/archive_dialog_fragment.xml
··· 1 + <?xml version="1.0" encoding="utf-8"?> 2 + <androidx.appcompat.widget.LinearLayoutCompat xmlns:android="http://schemas.android.com/apk/res/android" 3 + xmlns:app="http://schemas.android.com/apk/res-auto" 4 + android:layout_width="match_parent" 5 + android:layout_height="wrap_content" 6 + android:background="?appDialogBackground" 7 + android:orientation="vertical"> 8 + 9 + <androidx.appcompat.widget.AppCompatImageView 10 + android:id="@+id/ivArchiveArrow" 11 + android:layout_width="match_parent" 12 + android:layout_height="wrap_content" 13 + android:layout_gravity="center" 14 + android:layout_marginTop="8dp" 15 + android:src="@drawable/dialog_arrow" 16 + android:tint="?appContrastColor" /> 17 + 18 + <androidx.constraintlayout.widget.ConstraintLayout 19 + android:layout_width="match_parent" 20 + android:layout_height="wrap_content" 21 + android:layout_marginVertical="16dp"> 22 + 23 + <com.google.android.material.button.MaterialButton 24 + android:id="@+id/btnArchiveDialogDelete" 25 + android:layout_width="0dp" 26 + android:layout_height="wrap_content" 27 + android:layout_marginStart="8dp" 28 + android:layout_marginEnd="4dp" 29 + android:text="@string/archive_dialog_delete" 30 + android:textColor="@color/white" 31 + app:backgroundTint="?appActiveColor" 32 + app:layout_constraintBottom_toBottomOf="parent" 33 + app:layout_constraintEnd_toStartOf="@id/btnArchiveDialogRestore" 34 + app:layout_constraintStart_toStartOf="parent" /> 35 + 36 + <com.google.android.material.button.MaterialButton 37 + android:id="@+id/btnArchiveDialogRestore" 38 + android:layout_width="0dp" 39 + android:layout_height="wrap_content" 40 + android:layout_marginStart="4dp" 41 + android:layout_marginEnd="8dp" 42 + android:text="@string/archive_dialog_restore" 43 + android:textColor="@color/white" 44 + app:backgroundTint="?appInactiveColor" 45 + app:layout_constraintBottom_toBottomOf="parent" 46 + app:layout_constraintEnd_toEndOf="parent" 47 + app:layout_constraintStart_toEndOf="@id/btnArchiveDialogDelete" /> 48 + 49 + </androidx.constraintlayout.widget.ConstraintLayout> 50 + 51 + </androidx.appcompat.widget.LinearLayoutCompat>
+1 -1
feature_change_record_type/src/main/java/com/example/util/simpletimetracker/feature_change_record_type/viewModel/ChangeRecordTypeViewModel.kt
··· 278 278 (deleteButtonEnabled as MutableLiveData).value = false 279 279 viewModelScope.launch { 280 280 if (extra.id != 0L) { 281 - recordTypeInteractor.remove(extra.id) 281 + recordTypeInteractor.archive(extra.id) 282 282 runningRecordInteractor.get(extra.id)?.let { runningRecord -> 283 283 recordInteractor.add( 284 284 typeId = runningRecord.id,
feature_dialogs/src/main/res/drawable/dialog_arrow.xml core/src/main/res/drawable/dialog_arrow.xml
-12
feature_dialogs/src/main/res/values/styles.xml
··· 1 - <?xml version="1.0" encoding="utf-8"?> 2 - <resources> 3 - 4 - <style name="BottomSheetDialog" parent="Theme.MaterialComponents.Light.BottomSheetDialog"> 5 - <item name="bottomSheetStyle">@style/BottomSheetStyleWrapper</item> 6 - <item name="colorSecondary">@color/colorSecondary</item> 7 - </style> 8 - 9 - <style name="BottomSheetStyleWrapper" parent="Widget.Design.BottomSheet.Modal"> 10 - <item name="android:background">@android:color/transparent</item> 11 - </style> 12 - </resources>
+2 -1
navigation/src/main/java/com/example/util/simpletimetracker/navigation/Screen.kt
··· 19 19 TYPES_FILTER_DIALOG, 20 20 CARD_SIZE_DIALOG, 21 21 CARD_ORDER_DIALOG, 22 - EMOJI_SELECTION 22 + EMOJI_SELECTION, 23 + ARCHIVE_DIALOG 23 24 }