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.

adding activity filters on main

razeeman (Nov 4, 2022, 11:56 AM +0300) 53f7f993 d3fd39e4

+1016 -17
+1
features/feature_base_adapter/build.gradle.kts
··· 32 32 implementation(Deps.Androidx.recyclerView) 33 33 implementation(Deps.Androidx.cardView) 34 34 implementation(Deps.Androidx.material) 35 + implementation(Deps.Google.flexBox) 35 36 }
+383
data_local/schemas/com.example.util.simpletimetracker.data_local.database.AppDatabase/9.json
··· 1 + { 2 + "formatVersion": 1, 3 + "database": { 4 + "version": 9, 5 + "identityHash": "a164edb0e90c58b0f9cdee8e932e8255", 6 + "entities": [ 7 + { 8 + "tableName": "records", 9 + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `type_id` INTEGER NOT NULL, `time_started` INTEGER NOT NULL, `time_ended` INTEGER NOT NULL, `comment` TEXT NOT NULL, `tag_id` INTEGER NOT NULL)", 10 + "fields": [ 11 + { 12 + "fieldPath": "id", 13 + "columnName": "id", 14 + "affinity": "INTEGER", 15 + "notNull": true 16 + }, 17 + { 18 + "fieldPath": "typeId", 19 + "columnName": "type_id", 20 + "affinity": "INTEGER", 21 + "notNull": true 22 + }, 23 + { 24 + "fieldPath": "timeStarted", 25 + "columnName": "time_started", 26 + "affinity": "INTEGER", 27 + "notNull": true 28 + }, 29 + { 30 + "fieldPath": "timeEnded", 31 + "columnName": "time_ended", 32 + "affinity": "INTEGER", 33 + "notNull": true 34 + }, 35 + { 36 + "fieldPath": "comment", 37 + "columnName": "comment", 38 + "affinity": "TEXT", 39 + "notNull": true 40 + }, 41 + { 42 + "fieldPath": "tagId", 43 + "columnName": "tag_id", 44 + "affinity": "INTEGER", 45 + "notNull": true 46 + } 47 + ], 48 + "primaryKey": { 49 + "columnNames": [ 50 + "id" 51 + ], 52 + "autoGenerate": true 53 + }, 54 + "indices": [], 55 + "foreignKeys": [] 56 + }, 57 + { 58 + "tableName": "recordTypes", 59 + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT NOT NULL, `icon` TEXT NOT NULL, `color` INTEGER NOT NULL, `color_int` TEXT NOT NULL, `hidden` INTEGER NOT NULL, `goal_time` INTEGER NOT NULL)", 60 + "fields": [ 61 + { 62 + "fieldPath": "id", 63 + "columnName": "id", 64 + "affinity": "INTEGER", 65 + "notNull": true 66 + }, 67 + { 68 + "fieldPath": "name", 69 + "columnName": "name", 70 + "affinity": "TEXT", 71 + "notNull": true 72 + }, 73 + { 74 + "fieldPath": "icon", 75 + "columnName": "icon", 76 + "affinity": "TEXT", 77 + "notNull": true 78 + }, 79 + { 80 + "fieldPath": "color", 81 + "columnName": "color", 82 + "affinity": "INTEGER", 83 + "notNull": true 84 + }, 85 + { 86 + "fieldPath": "colorInt", 87 + "columnName": "color_int", 88 + "affinity": "TEXT", 89 + "notNull": true 90 + }, 91 + { 92 + "fieldPath": "hidden", 93 + "columnName": "hidden", 94 + "affinity": "INTEGER", 95 + "notNull": true 96 + }, 97 + { 98 + "fieldPath": "goalTime", 99 + "columnName": "goal_time", 100 + "affinity": "INTEGER", 101 + "notNull": true 102 + } 103 + ], 104 + "primaryKey": { 105 + "columnNames": [ 106 + "id" 107 + ], 108 + "autoGenerate": true 109 + }, 110 + "indices": [], 111 + "foreignKeys": [] 112 + }, 113 + { 114 + "tableName": "runningRecords", 115 + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `time_started` INTEGER NOT NULL, `comment` TEXT NOT NULL, `tag_id` INTEGER NOT NULL, PRIMARY KEY(`id`))", 116 + "fields": [ 117 + { 118 + "fieldPath": "id", 119 + "columnName": "id", 120 + "affinity": "INTEGER", 121 + "notNull": true 122 + }, 123 + { 124 + "fieldPath": "timeStarted", 125 + "columnName": "time_started", 126 + "affinity": "INTEGER", 127 + "notNull": true 128 + }, 129 + { 130 + "fieldPath": "comment", 131 + "columnName": "comment", 132 + "affinity": "TEXT", 133 + "notNull": true 134 + }, 135 + { 136 + "fieldPath": "tagId", 137 + "columnName": "tag_id", 138 + "affinity": "INTEGER", 139 + "notNull": true 140 + } 141 + ], 142 + "primaryKey": { 143 + "columnNames": [ 144 + "id" 145 + ], 146 + "autoGenerate": false 147 + }, 148 + "indices": [], 149 + "foreignKeys": [] 150 + }, 151 + { 152 + "tableName": "categories", 153 + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT NOT NULL, `color` INTEGER NOT NULL, `color_int` TEXT NOT NULL)", 154 + "fields": [ 155 + { 156 + "fieldPath": "id", 157 + "columnName": "id", 158 + "affinity": "INTEGER", 159 + "notNull": true 160 + }, 161 + { 162 + "fieldPath": "name", 163 + "columnName": "name", 164 + "affinity": "TEXT", 165 + "notNull": true 166 + }, 167 + { 168 + "fieldPath": "color", 169 + "columnName": "color", 170 + "affinity": "INTEGER", 171 + "notNull": true 172 + }, 173 + { 174 + "fieldPath": "colorInt", 175 + "columnName": "color_int", 176 + "affinity": "TEXT", 177 + "notNull": true 178 + } 179 + ], 180 + "primaryKey": { 181 + "columnNames": [ 182 + "id" 183 + ], 184 + "autoGenerate": true 185 + }, 186 + "indices": [], 187 + "foreignKeys": [] 188 + }, 189 + { 190 + "tableName": "recordTypeCategory", 191 + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`record_type_id` INTEGER NOT NULL, `category_id` INTEGER NOT NULL, PRIMARY KEY(`record_type_id`, `category_id`))", 192 + "fields": [ 193 + { 194 + "fieldPath": "recordTypeId", 195 + "columnName": "record_type_id", 196 + "affinity": "INTEGER", 197 + "notNull": true 198 + }, 199 + { 200 + "fieldPath": "categoryId", 201 + "columnName": "category_id", 202 + "affinity": "INTEGER", 203 + "notNull": true 204 + } 205 + ], 206 + "primaryKey": { 207 + "columnNames": [ 208 + "record_type_id", 209 + "category_id" 210 + ], 211 + "autoGenerate": false 212 + }, 213 + "indices": [], 214 + "foreignKeys": [] 215 + }, 216 + { 217 + "tableName": "recordTags", 218 + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `type_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `color` INTEGER NOT NULL, `color_int` TEXT NOT NULL, `archived` INTEGER NOT NULL)", 219 + "fields": [ 220 + { 221 + "fieldPath": "id", 222 + "columnName": "id", 223 + "affinity": "INTEGER", 224 + "notNull": true 225 + }, 226 + { 227 + "fieldPath": "typeId", 228 + "columnName": "type_id", 229 + "affinity": "INTEGER", 230 + "notNull": true 231 + }, 232 + { 233 + "fieldPath": "name", 234 + "columnName": "name", 235 + "affinity": "TEXT", 236 + "notNull": true 237 + }, 238 + { 239 + "fieldPath": "color", 240 + "columnName": "color", 241 + "affinity": "INTEGER", 242 + "notNull": true 243 + }, 244 + { 245 + "fieldPath": "colorInt", 246 + "columnName": "color_int", 247 + "affinity": "TEXT", 248 + "notNull": true 249 + }, 250 + { 251 + "fieldPath": "archived", 252 + "columnName": "archived", 253 + "affinity": "INTEGER", 254 + "notNull": true 255 + } 256 + ], 257 + "primaryKey": { 258 + "columnNames": [ 259 + "id" 260 + ], 261 + "autoGenerate": true 262 + }, 263 + "indices": [], 264 + "foreignKeys": [] 265 + }, 266 + { 267 + "tableName": "recordToRecordTag", 268 + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`record_id` INTEGER NOT NULL, `record_tag_id` INTEGER NOT NULL, PRIMARY KEY(`record_id`, `record_tag_id`))", 269 + "fields": [ 270 + { 271 + "fieldPath": "recordId", 272 + "columnName": "record_id", 273 + "affinity": "INTEGER", 274 + "notNull": true 275 + }, 276 + { 277 + "fieldPath": "recordTagId", 278 + "columnName": "record_tag_id", 279 + "affinity": "INTEGER", 280 + "notNull": true 281 + } 282 + ], 283 + "primaryKey": { 284 + "columnNames": [ 285 + "record_id", 286 + "record_tag_id" 287 + ], 288 + "autoGenerate": false 289 + }, 290 + "indices": [], 291 + "foreignKeys": [] 292 + }, 293 + { 294 + "tableName": "runningRecordToRecordTag", 295 + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`running_record_id` INTEGER NOT NULL, `record_tag_id` INTEGER NOT NULL, PRIMARY KEY(`running_record_id`, `record_tag_id`))", 296 + "fields": [ 297 + { 298 + "fieldPath": "runningRecordId", 299 + "columnName": "running_record_id", 300 + "affinity": "INTEGER", 301 + "notNull": true 302 + }, 303 + { 304 + "fieldPath": "recordTagId", 305 + "columnName": "record_tag_id", 306 + "affinity": "INTEGER", 307 + "notNull": true 308 + } 309 + ], 310 + "primaryKey": { 311 + "columnNames": [ 312 + "running_record_id", 313 + "record_tag_id" 314 + ], 315 + "autoGenerate": false 316 + }, 317 + "indices": [], 318 + "foreignKeys": [] 319 + }, 320 + { 321 + "tableName": "activityFilters", 322 + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `selectedIds` TEXT NOT NULL, `type` INTEGER NOT NULL, `name` TEXT NOT NULL, `color` INTEGER NOT NULL, `color_int` TEXT NOT NULL, `selected` INTEGER NOT NULL)", 323 + "fields": [ 324 + { 325 + "fieldPath": "id", 326 + "columnName": "id", 327 + "affinity": "INTEGER", 328 + "notNull": true 329 + }, 330 + { 331 + "fieldPath": "selectedIds", 332 + "columnName": "selectedIds", 333 + "affinity": "TEXT", 334 + "notNull": true 335 + }, 336 + { 337 + "fieldPath": "type", 338 + "columnName": "type", 339 + "affinity": "INTEGER", 340 + "notNull": true 341 + }, 342 + { 343 + "fieldPath": "name", 344 + "columnName": "name", 345 + "affinity": "TEXT", 346 + "notNull": true 347 + }, 348 + { 349 + "fieldPath": "color", 350 + "columnName": "color", 351 + "affinity": "INTEGER", 352 + "notNull": true 353 + }, 354 + { 355 + "fieldPath": "colorInt", 356 + "columnName": "color_int", 357 + "affinity": "TEXT", 358 + "notNull": true 359 + }, 360 + { 361 + "fieldPath": "selected", 362 + "columnName": "selected", 363 + "affinity": "INTEGER", 364 + "notNull": true 365 + } 366 + ], 367 + "primaryKey": { 368 + "columnNames": [ 369 + "id" 370 + ], 371 + "autoGenerate": true 372 + }, 373 + "indices": [], 374 + "foreignKeys": [] 375 + } 376 + ], 377 + "views": [], 378 + "setupQueries": [ 379 + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", 380 + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'a164edb0e90c58b0f9cdee8e932e8255')" 381 + ] 382 + } 383 + }
+9
features/feature_base_adapter/src/main/res/layout/item_activity_filter_layout.xml
··· 1 + <?xml version="1.0" encoding="utf-8"?> 2 + <com.example.util.simpletimetracker.feature_views.ActivityFilterView xmlns:android="http://schemas.android.com/apk/res/android" 3 + xmlns:app="http://schemas.android.com/apk/res-auto" 4 + xmlns:tools="http://schemas.android.com/tools" 5 + android:id="@+id/viewActivityFilterItem" 6 + android:layout_width="wrap_content" 7 + android:layout_height="wrap_content" 8 + app:cardElevation="@dimen/record_type_card_elevation" 9 + tools:itemName="Item name" />
+1 -1
features/feature_base_adapter/src/main/res/layout/item_empty_layout.xml
··· 4 4 android:layout_width="match_parent" 5 5 android:layout_height="wrap_content" 6 6 android:orientation="vertical" 7 - android:paddingHorizontal="8dp" 7 + android:paddingHorizontal="16dp" 8 8 android:paddingVertical="16dp"> 9 9 10 10 <androidx.appcompat.widget.AppCompatTextView
+3
features/feature_running_records/src/main/res/layout/item_running_record_empty_divider.xml
··· 1 + <View xmlns:android="http://schemas.android.com/apk/res/android" 2 + android:layout_width="match_parent" 3 + android:layout_height="0dp" />
+37
features/feature_views/src/main/res/layout/activity_filter_view_layout.xml
··· 1 + <?xml version="1.0" encoding="utf-8"?> 2 + <merge xmlns:android="http://schemas.android.com/apk/res/android" 3 + xmlns:app="http://schemas.android.com/apk/res-auto" 4 + xmlns:tools="http://schemas.android.com/tools" 5 + android:layout_width="wrap_content" 6 + android:layout_height="wrap_content" 7 + app:cardBackgroundColor="@color/black" 8 + app:cardCornerRadius="100dp" 9 + app:cardElevation="@dimen/record_type_card_elevation" 10 + app:cardPreventCornerOverlap="false" 11 + app:cardUseCompatPadding="true" 12 + tools:parentTag="androidx.cardview.widget.CardView"> 13 + 14 + <androidx.constraintlayout.widget.ConstraintLayout 15 + android:layout_width="wrap_content" 16 + android:layout_height="wrap_content" 17 + android:background="?selectableItemBackground"> 18 + 19 + <androidx.appcompat.widget.AppCompatTextView 20 + android:id="@+id/tvActivityFilterItemName" 21 + android:layout_width="wrap_content" 22 + android:layout_height="wrap_content" 23 + android:layout_marginStart="10dp" 24 + android:layout_marginEnd="10dp" 25 + android:paddingVertical="6dp" 26 + android:textColor="?appLightTextColor" 27 + android:textSize="12sp" 28 + app:layout_constrainedWidth="true" 29 + app:layout_constraintBottom_toBottomOf="parent" 30 + app:layout_constraintEnd_toEndOf="parent" 31 + app:layout_constraintStart_toStartOf="parent" 32 + app:layout_constraintTop_toTopOf="parent" 33 + tools:text="Item name" /> 34 + 35 + </androidx.constraintlayout.widget.ConstraintLayout> 36 + 37 + </merge>
+5
features/feature_views/src/main/res/values/attrs.xml
··· 90 90 <attr name="itemIconVisible" /> 91 91 </declare-styleable> 92 92 93 + <declare-styleable name="ActivityFilterView"> 94 + <attr name="itemName" /> 95 + <attr name="itemColor" /> 96 + </declare-styleable> 97 + 93 98 <declare-styleable name="FullWidthSpinner"> 94 99 <attr name="popupUnderSpinner" format="boolean" /> 95 100 <attr name="popupUnderMarginTop" format="dimension" />
+32
data_local/src/main/java/com/example/util/simpletimetracker/data_local/database/ActivityFilterDao.kt
··· 1 + package com.example.util.simpletimetracker.data_local.database 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 com.example.util.simpletimetracker.data_local.model.ActivityFilterDBO 9 + 10 + @Dao 11 + interface ActivityFilterDao { 12 + 13 + @Transaction 14 + @Query("SELECT * FROM activityFilters") 15 + suspend fun getAll(): List<ActivityFilterDBO> 16 + 17 + @Transaction 18 + @Query("SELECT * FROM activityFilters WHERE id = :id LIMIT 1") 19 + suspend fun get(id: Long): ActivityFilterDBO? 20 + 21 + @Insert(onConflict = OnConflictStrategy.REPLACE) 22 + suspend fun insert(activityFilter: ActivityFilterDBO): Long 23 + 24 + @Query("UPDATE activityFilters SET selected =:selected WHERE id = :id") 25 + suspend fun changeSelected(id: Long, selected: Int) 26 + 27 + @Query("DELETE FROM activityFilters WHERE id = :id") 28 + suspend fun delete(id: Long) 29 + 30 + @Query("DELETE FROM activityFilters") 31 + suspend fun clear() 32 + }
+5 -1
data_local/src/main/java/com/example/util/simpletimetracker/data_local/database/AppDatabase.kt
··· 2 2 3 3 import androidx.room.Database 4 4 import androidx.room.RoomDatabase 5 + import com.example.util.simpletimetracker.data_local.model.ActivityFilterDBO 5 6 import com.example.util.simpletimetracker.data_local.model.CategoryDBO 6 7 import com.example.util.simpletimetracker.data_local.model.RecordDBO 7 8 import com.example.util.simpletimetracker.data_local.model.RecordTagDBO ··· 21 22 RecordTagDBO::class, 22 23 RecordToRecordTagDBO::class, 23 24 RunningRecordToRecordTagDBO::class, 25 + ActivityFilterDBO::class, 24 26 ], 25 - version = 8, 27 + version = 9, 26 28 exportSchema = true, 27 29 ) 28 30 abstract class AppDatabase : RoomDatabase() { ··· 42 44 abstract fun recordToRecordTagDao(): RecordToRecordTagDao 43 45 44 46 abstract fun runningRecordToRecordTagDao(): RunningRecordToRecordTagDao 47 + 48 + abstract fun activityFilterDao(): ActivityFilterDao 45 49 46 50 companion object { 47 51 const val DATABASE_NAME = "simpleTimeTrackerDB"
+8
data_local/src/main/java/com/example/util/simpletimetracker/data_local/database/AppDatabaseMigrations.kt
··· 100 100 ) 101 101 } 102 102 } 103 + 104 + val migration_8_9 = object : Migration(8, 9) { 105 + override fun migrate(database: SupportSQLiteDatabase) { 106 + database.execSQL( 107 + "CREATE TABLE IF NOT EXISTS `activityFilters` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `selectedIds` TEXT NOT NULL, `type` INTEGER NOT NULL, `name` TEXT NOT NULL, `color` INTEGER NOT NULL, `color_int` TEXT NOT NULL, `selected` INTEGER NOT NULL)" 108 + ) 109 + } 110 + } 103 111 } 104 112 }
+9 -1
data_local/src/main/java/com/example/util/simpletimetracker/data_local/di/DataLocalModule.kt
··· 4 4 import android.content.SharedPreferences 5 5 import androidx.room.Room 6 6 import com.example.util.simpletimetracker.core.extension.allowDiskWrite 7 + import com.example.util.simpletimetracker.data_local.database.ActivityFilterDao 7 8 import com.example.util.simpletimetracker.data_local.database.AppDatabase 8 9 import com.example.util.simpletimetracker.data_local.database.AppDatabaseMigrations 9 10 import com.example.util.simpletimetracker.data_local.database.CategoryDao ··· 45 46 AppDatabaseMigrations.migration_5_6, 46 47 AppDatabaseMigrations.migration_6_7, 47 48 AppDatabaseMigrations.migration_7_8, 49 + AppDatabaseMigrations.migration_8_9, 48 50 ) 49 51 .build() 50 52 } ··· 101 103 102 104 @Provides 103 105 @Singleton 104 - fun getRunningRecordToRecordTagDap(database: AppDatabase): RunningRecordToRecordTagDao { 106 + fun getRunningRecordToRecordTagDao(database: AppDatabase): RunningRecordToRecordTagDao { 105 107 return database.runningRecordToRecordTagDao() 108 + } 109 + 110 + @Provides 111 + @Singleton 112 + fun getActivityFilterDao(database: AppDatabase): ActivityFilterDao { 113 + return database.activityFilterDao() 106 114 } 107 115 }
+6
data_local/src/main/java/com/example/util/simpletimetracker/data_local/di/DataLocalModuleBinds.kt
··· 1 1 package com.example.util.simpletimetracker.data_local.di 2 2 3 + import com.example.util.simpletimetracker.data_local.repo.ActivityFilterRepoImpl 3 4 import com.example.util.simpletimetracker.data_local.repo.CategoryRepoImpl 4 5 import com.example.util.simpletimetracker.data_local.repo.PrefsRepoImpl 5 6 import com.example.util.simpletimetracker.data_local.repo.RecordRepoImpl ··· 11 12 import com.example.util.simpletimetracker.data_local.repo.RunningRecordToRecordTagRepoImpl 12 13 import com.example.util.simpletimetracker.data_local.resolver.BackupRepoImpl 13 14 import com.example.util.simpletimetracker.data_local.resolver.CsvRepoImpl 15 + import com.example.util.simpletimetracker.domain.repo.ActivityFilterRepo 14 16 import com.example.util.simpletimetracker.domain.repo.CategoryRepo 15 17 import com.example.util.simpletimetracker.domain.repo.PrefsRepo 16 18 import com.example.util.simpletimetracker.domain.repo.RecordRepo ··· 75 77 @Binds 76 78 @Singleton 77 79 fun RunningRecordToRecordTagRepoImpl.bindRunningRecordToRecordTagRepo(): RunningRecordToRecordTagRepo 80 + 81 + @Binds 82 + @Singleton 83 + fun ActivityFilterRepoImpl.bindActivityFilterRepo(): ActivityFilterRepo 78 84 }
+42
data_local/src/main/java/com/example/util/simpletimetracker/data_local/mapper/ActivityFilterDataLocalMapper.kt
··· 1 + package com.example.util.simpletimetracker.data_local.mapper 2 + 3 + import com.example.util.simpletimetracker.data_local.model.ActivityFilterDBO 4 + import com.example.util.simpletimetracker.domain.model.ActivityFilter 5 + import com.example.util.simpletimetracker.domain.model.AppColor 6 + import javax.inject.Inject 7 + 8 + class ActivityFilterDataLocalMapper @Inject constructor() { 9 + 10 + fun map(dbo: ActivityFilterDBO): ActivityFilter { 11 + return ActivityFilter( 12 + id = dbo.id, 13 + selectedIds = dbo.selectedIds.split(',').mapNotNull(String::toLongOrNull), 14 + type = when (dbo.type) { 15 + 0L -> ActivityFilter.Type.Activity 16 + 1L -> ActivityFilter.Type.ActivityTag 17 + else -> ActivityFilter.Type.Activity 18 + }, 19 + name = dbo.name, 20 + color = AppColor( 21 + colorId = dbo.color, 22 + colorInt = dbo.colorInt 23 + ), 24 + selected = dbo.selected, 25 + ) 26 + } 27 + 28 + fun map(domain: ActivityFilter): ActivityFilterDBO { 29 + return ActivityFilterDBO( 30 + id = domain.id, 31 + selectedIds = domain.selectedIds.joinToString(separator = ","), 32 + type = when (domain.type) { 33 + is ActivityFilter.Type.Activity -> 0L 34 + is ActivityFilter.Type.ActivityTag -> 1L 35 + }, 36 + name = domain.name, 37 + color = domain.color.colorId, 38 + colorInt = domain.color.colorInt, 39 + selected = domain.selected, 40 + ) 41 + } 42 + }
+32
data_local/src/main/java/com/example/util/simpletimetracker/data_local/model/ActivityFilterDBO.kt
··· 1 + package com.example.util.simpletimetracker.data_local.model 2 + 3 + import androidx.room.ColumnInfo 4 + import androidx.room.Entity 5 + import androidx.room.PrimaryKey 6 + 7 + @Entity(tableName = "activityFilters") 8 + data class ActivityFilterDBO( 9 + @PrimaryKey(autoGenerate = true) 10 + @ColumnInfo(name = "id") 11 + val id: Long, 12 + 13 + // Longs stored in string comma separated 14 + @ColumnInfo(name = "selectedIds") 15 + val selectedIds: String, 16 + 17 + @ColumnInfo(name = "type") 18 + val type: Long, 19 + 20 + @ColumnInfo(name = "name") 21 + val name: String, 22 + 23 + @ColumnInfo(name = "color") 24 + val color: Int, 25 + 26 + // If not empty - take color from here, custom colorInt stored as text. 27 + @ColumnInfo(name = "color_int") 28 + val colorInt: String, 29 + 30 + @ColumnInfo(name = "selected") 31 + val selected: Boolean, 32 + )
+52
data_local/src/main/java/com/example/util/simpletimetracker/data_local/repo/ActivityFilterRepoImpl.kt
··· 1 + package com.example.util.simpletimetracker.data_local.repo 2 + 3 + import com.example.util.simpletimetracker.data_local.database.ActivityFilterDao 4 + import com.example.util.simpletimetracker.data_local.mapper.ActivityFilterDataLocalMapper 5 + import com.example.util.simpletimetracker.domain.model.ActivityFilter 6 + import com.example.util.simpletimetracker.domain.repo.ActivityFilterRepo 7 + import kotlinx.coroutines.Dispatchers 8 + import kotlinx.coroutines.withContext 9 + import timber.log.Timber 10 + import javax.inject.Inject 11 + import javax.inject.Singleton 12 + 13 + @Singleton 14 + class ActivityFilterRepoImpl @Inject constructor( 15 + private val activityFilterDao: ActivityFilterDao, 16 + private val activityFilterDataLocalMapper: ActivityFilterDataLocalMapper, 17 + ) : ActivityFilterRepo { 18 + 19 + override suspend fun getAll(): List<ActivityFilter> = withContext(Dispatchers.IO) { 20 + Timber.d("getAll") 21 + activityFilterDao.getAll() 22 + .map(activityFilterDataLocalMapper::map) 23 + } 24 + 25 + override suspend fun get(id: Long): ActivityFilter? = withContext(Dispatchers.IO) { 26 + Timber.d("get") 27 + activityFilterDao.get(id) 28 + ?.let(activityFilterDataLocalMapper::map) 29 + } 30 + 31 + override suspend fun add(activityFilter: ActivityFilter): Long = withContext(Dispatchers.IO) { 32 + Timber.d("add") 33 + return@withContext activityFilterDao.insert( 34 + activityFilter.let(activityFilterDataLocalMapper::map) 35 + ) 36 + } 37 + 38 + override suspend fun changeSelected(id: Long, selected: Boolean) = withContext(Dispatchers.IO) { 39 + Timber.d("changeSelected") 40 + activityFilterDao.changeSelected(id, if (selected) 1 else 0) 41 + } 42 + 43 + override suspend fun remove(id: Long) = withContext(Dispatchers.IO) { 44 + Timber.d("remove") 45 + activityFilterDao.delete(id) 46 + } 47 + 48 + override suspend fun clear() = withContext(Dispatchers.IO) { 49 + Timber.d("clear") 50 + activityFilterDao.clear() 51 + } 52 + }
+34
domain/src/main/java/com/example/util/simpletimetracker/domain/interactor/ActivityFilterInteractor.kt
··· 1 + package com.example.util.simpletimetracker.domain.interactor 2 + 3 + import com.example.util.simpletimetracker.domain.model.ActivityFilter 4 + import com.example.util.simpletimetracker.domain.repo.ActivityFilterRepo 5 + import javax.inject.Inject 6 + 7 + class ActivityFilterInteractor @Inject constructor( 8 + private val activityFilterRepo: ActivityFilterRepo, 9 + ) { 10 + 11 + suspend fun getAll(): List<ActivityFilter> { 12 + return activityFilterRepo.getAll() 13 + } 14 + 15 + suspend fun get(id: Long): ActivityFilter? { 16 + return activityFilterRepo.get(id) 17 + } 18 + 19 + suspend fun add(record: ActivityFilter) { 20 + activityFilterRepo.add(record) 21 + } 22 + 23 + suspend fun changeSelected(id: Long, selected: Boolean) { 24 + activityFilterRepo.changeSelected(id, selected) 25 + } 26 + 27 + suspend fun remove(id: Long) { 28 + activityFilterRepo.remove(id) 29 + } 30 + 31 + suspend fun clear() { 32 + activityFilterRepo.clear() 33 + } 34 + }
+16
domain/src/main/java/com/example/util/simpletimetracker/domain/model/ActivityFilter.kt
··· 1 + package com.example.util.simpletimetracker.domain.model 2 + 3 + data class ActivityFilter( 4 + val id: Long = 0, 5 + val selectedIds: List<Long>, 6 + val type: Type, 7 + val name: String, 8 + val color: AppColor, 9 + val selected: Boolean, 10 + ) { 11 + 12 + sealed interface Type { 13 + object ActivityTag: Type 14 + object Activity: Type 15 + } 16 + }
+18
domain/src/main/java/com/example/util/simpletimetracker/domain/repo/ActivityFilterRepo.kt
··· 1 + package com.example.util.simpletimetracker.domain.repo 2 + 3 + import com.example.util.simpletimetracker.domain.model.ActivityFilter 4 + 5 + interface ActivityFilterRepo { 6 + 7 + suspend fun getAll(): List<ActivityFilter> 8 + 9 + suspend fun get(id: Long): ActivityFilter? 10 + 11 + suspend fun add(activityFilter: ActivityFilter): Long 12 + 13 + suspend fun changeSelected(id: Long, selected: Boolean) 14 + 15 + suspend fun remove(id: Long) 16 + 17 + suspend fun clear() 18 + }
+57
features/feature_views/src/main/java/com/example/util/simpletimetracker/feature_views/ActivityFilterView.kt
··· 1 + package com.example.util.simpletimetracker.feature_views 2 + 3 + import android.content.Context 4 + import android.graphics.Color 5 + import android.util.AttributeSet 6 + import android.view.LayoutInflater 7 + import androidx.cardview.widget.CardView 8 + import androidx.core.content.ContextCompat 9 + import com.example.util.simpletimetracker.feature_views.databinding.ActivityFilterViewLayoutBinding 10 + import com.example.util.simpletimetracker.feature_views.extension.dpToPx 11 + 12 + class ActivityFilterView @JvmOverloads constructor( 13 + context: Context, 14 + attrs: AttributeSet? = null, 15 + defStyleAttr: Int = 0, 16 + ) : CardView( 17 + context, 18 + attrs, 19 + defStyleAttr 20 + ) { 21 + 22 + private val binding: ActivityFilterViewLayoutBinding = ActivityFilterViewLayoutBinding 23 + .inflate(LayoutInflater.from(context), this) 24 + 25 + init { 26 + ContextCompat.getColor(context, R.color.black).let(::setCardBackgroundColor) 27 + radius = 100.dpToPx().toFloat() 28 + // TODO doesn't work here for some reason, need to set in the layout 29 + cardElevation = resources.getDimensionPixelOffset(R.dimen.record_type_card_elevation) 30 + .toFloat() 31 + preventCornerOverlap = false 32 + useCompatPadding = true 33 + 34 + context.obtainStyledAttributes(attrs, R.styleable.ActivityFilterView, defStyleAttr, 0) 35 + .run { 36 + if (hasValue(R.styleable.ActivityFilterView_itemName)) itemName = 37 + getString(R.styleable.ActivityFilterView_itemName).orEmpty() 38 + 39 + if (hasValue(R.styleable.ActivityFilterView_itemColor)) itemColor = 40 + getColor(R.styleable.ActivityFilterView_itemColor, Color.BLACK) 41 + 42 + recycle() 43 + } 44 + } 45 + 46 + var itemName: String = "" 47 + set(value) { 48 + binding.tvActivityFilterItemName.text = value 49 + field = value 50 + } 51 + 52 + var itemColor: Int = 0 53 + set(value) { 54 + setCardBackgroundColor(value) 55 + field = value 56 + } 57 + }
+1
features/feature_views/src/main/java/com/example/util/simpletimetracker/feature_views/TransitionNames.kt
··· 8 8 const val STATISTICS_DETAIL = "statistics_detail_transition_name" 9 9 const val ACTIVITY_TAG = "activity_tag_transition_name" 10 10 const val RECORD_TAG = "record_tag_transition_name" 11 + const val ACTIVITY_FILTER_TAG = "activity_filter_tag_transition_name" 11 12 }
+31
features/feature_base_adapter/src/main/java/com/example/util/simpletimetracker/feature_base_adapter/activityFilter/ActivityFilterAdapterDelegate.kt
··· 1 + package com.example.util.simpletimetracker.feature_base_adapter.activityFilter 2 + 3 + import com.example.util.simpletimetracker.feature_base_adapter.activityFilter.ActivityFilterViewData as ViewData 4 + import com.example.util.simpletimetracker.feature_base_adapter.databinding.ItemActivityFilterLayoutBinding as Binding 5 + import androidx.core.view.ViewCompat 6 + import com.example.util.simpletimetracker.feature_base_adapter.createRecyclerBindingAdapterDelegate 7 + import com.example.util.simpletimetracker.feature_views.TransitionNames 8 + import com.example.util.simpletimetracker.feature_views.extension.setOnClickWith 9 + import com.example.util.simpletimetracker.feature_views.extension.setOnLongClick 10 + 11 + fun createActivityFilterAdapterDelegate( 12 + onClick: ((ViewData) -> Unit), 13 + onLongClick: ((ViewData, Pair<Any, String>) -> Unit), 14 + ) = createRecyclerBindingAdapterDelegate<ViewData, Binding>( 15 + Binding::inflate 16 + ) { binding, item, _ -> 17 + 18 + with(binding.viewActivityFilterItem) { 19 + item as ViewData 20 + 21 + val transitionName = TransitionNames.ACTIVITY_FILTER_TAG + item.id 22 + val sharedElements: Pair<Any, String> = this to transitionName 23 + ViewCompat.setTransitionName(this, transitionName) 24 + 25 + itemColor = item.color 26 + itemName = item.name 27 + 28 + setOnClickWith(item, onClick) 29 + setOnLongClick { onLongClick(item, sharedElements) } 30 + } 31 + }
+21
features/feature_base_adapter/src/main/java/com/example/util/simpletimetracker/feature_base_adapter/activityFilter/ActivityFilterAddAdapterDelegate.kt
··· 1 + package com.example.util.simpletimetracker.feature_base_adapter.activityFilter 2 + 3 + import com.example.util.simpletimetracker.feature_base_adapter.activityFilter.ActivityFilterAddViewData as ViewData 4 + import com.example.util.simpletimetracker.feature_base_adapter.databinding.ItemActivityFilterLayoutBinding as Binding 5 + import com.example.util.simpletimetracker.feature_base_adapter.createRecyclerBindingAdapterDelegate 6 + import com.example.util.simpletimetracker.feature_views.extension.setOnClick 7 + 8 + fun createActivityFilterAddAdapterDelegate( 9 + onItemClick: (() -> Unit), 10 + ) = createRecyclerBindingAdapterDelegate<ViewData, Binding>( 11 + Binding::inflate 12 + ) { binding, item, _ -> 13 + 14 + with(binding.viewActivityFilterItem) { 15 + item as ViewData 16 + 17 + itemColor = item.color 18 + itemName = item.name 19 + setOnClick(onItemClick) 20 + } 21 + }
+16
features/feature_base_adapter/src/main/java/com/example/util/simpletimetracker/feature_base_adapter/activityFilter/ActivityFilterAddViewData.kt
··· 1 + package com.example.util.simpletimetracker.feature_base_adapter.activityFilter 2 + 3 + import androidx.annotation.ColorInt 4 + import com.example.util.simpletimetracker.feature_base_adapter.ViewHolderType 5 + 6 + class ActivityFilterAddViewData( 7 + val name: String, 8 + @ColorInt val color: Int 9 + ) : ViewHolderType { 10 + 11 + // Only one add item on screen 12 + override fun getUniqueId(): Long = 1L 13 + 14 + override fun isValidType(other: ViewHolderType): Boolean = 15 + other is ActivityFilterAddViewData 16 + }
+18
features/feature_base_adapter/src/main/java/com/example/util/simpletimetracker/feature_base_adapter/activityFilter/ActivityFilterViewData.kt
··· 1 + package com.example.util.simpletimetracker.feature_base_adapter.activityFilter 2 + 3 + import androidx.annotation.ColorInt 4 + import com.example.util.simpletimetracker.feature_base_adapter.ViewHolderType 5 + 6 + data class ActivityFilterViewData( 7 + val id: Long, 8 + val name: String, 9 + @ColorInt val iconColor: Int, 10 + @ColorInt val color: Int, 11 + val selected: Boolean, 12 + ) : ViewHolderType { 13 + 14 + override fun getUniqueId(): Long = id 15 + 16 + override fun isValidType(other: ViewHolderType): Boolean = 17 + other is ActivityFilterViewData 18 + }
+12
features/feature_running_records/src/main/java/com/example/util/simpletimetracker/feature_running_records/adapter/RunningRecordEmptyDividerAdapterDelegate.kt
··· 1 + package com.example.util.simpletimetracker.feature_running_records.adapter 2 + 3 + import com.example.util.simpletimetracker.feature_running_records.databinding.ItemRunningRecordEmptyDividerBinding as Binding 4 + import com.example.util.simpletimetracker.feature_running_records.viewData.RunningRecordEmptyDividerViewData as ViewData 5 + import com.example.util.simpletimetracker.feature_base_adapter.createRecyclerBindingAdapterDelegate 6 + 7 + fun createRunningRecordEmptyDividerAdapterDelegate() = createRecyclerBindingAdapterDelegate<ViewData, Binding>( 8 + Binding::inflate 9 + ) { _, _, _ -> 10 + 11 + // Nothing to bind 12 + }
+67 -4
features/feature_running_records/src/main/java/com/example/util/simpletimetracker/feature_running_records/interactor/RunningRecordsViewDataInteractor.kt
··· 1 1 package com.example.util.simpletimetracker.feature_running_records.interactor 2 2 3 - import com.example.util.simpletimetracker.feature_base_adapter.ViewHolderType 4 - import com.example.util.simpletimetracker.feature_base_adapter.divider.DividerViewData 3 + import com.example.util.simpletimetracker.domain.interactor.ActivityFilterInteractor 5 4 import com.example.util.simpletimetracker.domain.interactor.PrefsInteractor 6 5 import com.example.util.simpletimetracker.domain.interactor.RecordTagInteractor 6 + import com.example.util.simpletimetracker.domain.interactor.RecordTypeCategoryInteractor 7 7 import com.example.util.simpletimetracker.domain.interactor.RecordTypeInteractor 8 8 import com.example.util.simpletimetracker.domain.interactor.RunningRecordInteractor 9 + import com.example.util.simpletimetracker.domain.model.ActivityFilter 10 + import com.example.util.simpletimetracker.feature_base_adapter.ViewHolderType 11 + import com.example.util.simpletimetracker.feature_base_adapter.divider.DividerViewData 9 12 import com.example.util.simpletimetracker.feature_running_records.mapper.RunningRecordViewDataMapper 13 + import com.example.util.simpletimetracker.feature_running_records.viewData.RunningRecordEmptyDividerViewData 10 14 import javax.inject.Inject 11 15 12 16 class RunningRecordsViewDataInteractor @Inject constructor( ··· 14 18 private val recordTypeInteractor: RecordTypeInteractor, 15 19 private val recordTagInteractor: RecordTagInteractor, 16 20 private val runningRecordInteractor: RunningRecordInteractor, 17 - private val mapper: RunningRecordViewDataMapper 21 + private val activityFilterInteractor: ActivityFilterInteractor, 22 + private val recordTypeCategoryInteractor: RecordTypeCategoryInteractor, 23 + private val mapper: RunningRecordViewDataMapper, 18 24 ) { 19 25 20 26 suspend fun getViewData(): List<ViewHolderType> { 21 27 val recordTypes = recordTypeInteractor.getAll() 22 - val recordTypesMap = recordTypes.map { it.id to it }.toMap() 28 + val recordTypesMap = recordTypes.associateBy { it.id } 23 29 val recordTags = recordTagInteractor.getAll() 24 30 val runningRecords = runningRecordInteractor.getAll() 25 31 val recordTypesRunning = runningRecords.map { it.id } 32 + val activityFilters = activityFilterInteractor.getAll() 26 33 val numberOfCards = prefsInteractor.getNumberOfCards() 27 34 val isDarkTheme = prefsInteractor.getDarkMode() 28 35 val useMilitaryTime = prefsInteractor.getUseMilitaryTimeFormat() ··· 48 55 } 49 56 } 50 57 58 + val filtersViewData = activityFilters 59 + .map { 60 + mapper.map( 61 + filter = it, 62 + isDarkTheme = isDarkTheme, 63 + ) 64 + } 65 + .plus( 66 + mapper.mapToActivityFilterAddItem( 67 + isDarkTheme = isDarkTheme, 68 + ) 69 + ) 70 + .plus( 71 + RunningRecordEmptyDividerViewData(1) 72 + ) 73 + 74 + val anyFiltersSelected = activityFilters.any { it.selected } 75 + val selectedTypes = getSelectedTypeIds(anyFiltersSelected, activityFilters) 51 76 val recordTypesViewData = recordTypes 52 77 .filterNot { 53 78 it.hidden 79 + } 80 + .let { list -> 81 + if (anyFiltersSelected) list.filter { it.id in selectedTypes } 82 + else list 54 83 } 55 84 .map { 56 85 mapper.map( ··· 69 98 70 99 return runningRecordsViewData + 71 100 listOf(DividerViewData(1)) + 101 + filtersViewData + 72 102 recordTypesViewData 103 + } 104 + 105 + private suspend fun getSelectedTypeIds( 106 + anyFiltersSelected: Boolean, 107 + filters: List<ActivityFilter>, 108 + ): List<Long> { 109 + if (!anyFiltersSelected) return emptyList() 110 + 111 + val selectedFilters = filters.filter { it.selected } 112 + 113 + val activityIds: List<Long> = selectedFilters 114 + .filter { it.type is ActivityFilter.Type.Activity } 115 + .map { it.selectedIds } 116 + .flatten() 117 + 118 + val fromCategoryIds: List<Long> = selectedFilters 119 + .filter { it.type is ActivityFilter.Type.ActivityTag } 120 + .map { it.selectedIds } 121 + .flatten() 122 + .takeUnless { it.isEmpty() } 123 + ?.let { tagIds -> 124 + val recordTypeCategories = recordTypeCategoryInteractor.getAll() 125 + .groupBy { it.categoryId } 126 + .mapValues { (_, value) -> value.map { it.recordTypeId } } 127 + tagIds.mapNotNull { tagId -> recordTypeCategories[tagId] }.flatten() 128 + } 129 + .orEmpty() 130 + 131 + return when { 132 + fromCategoryIds.isEmpty() -> activityIds 133 + activityIds.isEmpty() -> fromCategoryIds 134 + else -> activityIds + fromCategoryIds 135 + } 73 136 } 74 137 }
+42 -8
features/feature_running_records/src/main/java/com/example/util/simpletimetracker/feature_running_records/mapper/RunningRecordViewDataMapper.kt
··· 1 1 package com.example.util.simpletimetracker.feature_running_records.mapper 2 2 3 - import com.example.util.simpletimetracker.feature_base_adapter.ViewHolderType 4 - import com.example.util.simpletimetracker.feature_base_adapter.empty.EmptyViewData 5 3 import com.example.util.simpletimetracker.core.mapper.ColorMapper 6 4 import com.example.util.simpletimetracker.core.mapper.IconMapper 7 5 import com.example.util.simpletimetracker.core.mapper.RecordTypeCardSizeMapper ··· 9 7 import com.example.util.simpletimetracker.core.mapper.TimeMapper 10 8 import com.example.util.simpletimetracker.core.repo.ResourceRepo 11 9 import com.example.util.simpletimetracker.domain.extension.getFullName 12 - import com.example.util.simpletimetracker.feature_views.viewData.RecordTypeIcon 13 - import com.example.util.simpletimetracker.feature_base_adapter.recordType.RecordTypeViewData 10 + import com.example.util.simpletimetracker.domain.model.ActivityFilter 14 11 import com.example.util.simpletimetracker.domain.model.RecordTag 15 12 import com.example.util.simpletimetracker.domain.model.RecordType 16 13 import com.example.util.simpletimetracker.domain.model.RunningRecord 14 + import com.example.util.simpletimetracker.feature_base_adapter.ViewHolderType 15 + import com.example.util.simpletimetracker.feature_base_adapter.activityFilter.ActivityFilterAddViewData 16 + import com.example.util.simpletimetracker.feature_base_adapter.activityFilter.ActivityFilterViewData 17 + import com.example.util.simpletimetracker.feature_base_adapter.empty.EmptyViewData 18 + import com.example.util.simpletimetracker.feature_base_adapter.recordType.RecordTypeViewData 17 19 import com.example.util.simpletimetracker.feature_running_records.R 18 20 import com.example.util.simpletimetracker.feature_running_records.viewData.RunningRecordTypeAddViewData 19 21 import com.example.util.simpletimetracker.feature_running_records.viewData.RunningRecordViewData 22 + import com.example.util.simpletimetracker.feature_views.viewData.RecordTypeIcon 20 23 import javax.inject.Inject 21 24 22 25 class RunningRecordViewDataMapper @Inject constructor( ··· 25 28 private val resourceRepo: ResourceRepo, 26 29 private val timeMapper: TimeMapper, 27 30 private val recordTypeViewDataMapper: RecordTypeViewDataMapper, 28 - private val recordTypeCardSizeMapper: RecordTypeCardSizeMapper 31 + private val recordTypeCardSizeMapper: RecordTypeCardSizeMapper, 29 32 ) { 30 33 31 34 fun map( ··· 33 36 recordType: RecordType, 34 37 recordTags: List<RecordTag>, 35 38 isDarkTheme: Boolean, 36 - useMilitaryTime: Boolean 39 + useMilitaryTime: Boolean, 37 40 ): RunningRecordViewData { 38 41 return RunningRecordViewData( 39 42 id = runningRecord.id, ··· 61 64 recordType: RecordType, 62 65 isFiltered: Boolean, 63 66 numberOfCards: Int, 64 - isDarkTheme: Boolean 67 + isDarkTheme: Boolean, 65 68 ): RecordTypeViewData { 66 69 return recordTypeViewDataMapper.mapFiltered( 67 70 recordType, 68 71 numberOfCards, 69 72 isDarkTheme, 70 73 isFiltered 74 + ) 75 + } 76 + 77 + fun map( 78 + filter: ActivityFilter, 79 + isDarkTheme: Boolean, 80 + ): ActivityFilterViewData { 81 + val selected = filter.selected 82 + return ActivityFilterViewData( 83 + id = filter.id, 84 + name = filter.name, 85 + iconColor = if (selected) { 86 + colorMapper.toIconColor(isDarkTheme) 87 + } else { 88 + colorMapper.toFilteredIconColor(isDarkTheme) 89 + }, 90 + color = if (selected) { 91 + colorMapper.mapToColorInt(filter.color, isDarkTheme) 92 + } else { 93 + colorMapper.toFilteredColor(isDarkTheme) 94 + }, 95 + selected = selected, 71 96 ) 72 97 } 73 98 ··· 86 111 87 112 fun mapToAddItem( 88 113 numberOfCards: Int, 89 - isDarkTheme: Boolean 114 + isDarkTheme: Boolean, 90 115 ): RunningRecordTypeAddViewData { 91 116 return RunningRecordTypeAddViewData( 92 117 name = R.string.running_records_add_type.let(resourceRepo::getString), ··· 95 120 width = recordTypeCardSizeMapper.toCardWidth(numberOfCards), 96 121 height = recordTypeCardSizeMapper.toCardHeight(numberOfCards), 97 122 asRow = recordTypeCardSizeMapper.toCardAsRow(numberOfCards) 123 + ) 124 + } 125 + 126 + fun mapToActivityFilterAddItem( 127 + isDarkTheme: Boolean, 128 + ): ActivityFilterAddViewData { 129 + return ActivityFilterAddViewData( 130 + name = "Add filter", 131 + color = colorMapper.toInactiveColor(isDarkTheme) 98 132 ) 99 133 } 100 134 }
+7 -1
features/feature_running_records/src/main/java/com/example/util/simpletimetracker/feature_running_records/view/RunningRecordsFragment.kt
··· 21 21 import dagger.hilt.android.AndroidEntryPoint 22 22 import javax.inject.Inject 23 23 import com.example.util.simpletimetracker.feature_running_records.databinding.RunningRecordsFragmentBinding as Binding 24 + import com.example.util.simpletimetracker.feature_base_adapter.activityFilter.createActivityFilterAdapterDelegate 25 + import com.example.util.simpletimetracker.feature_base_adapter.activityFilter.createActivityFilterAddAdapterDelegate 26 + import com.example.util.simpletimetracker.feature_running_records.adapter.createRunningRecordEmptyDividerAdapterDelegate 24 27 25 28 @AndroidEntryPoint 26 29 class RunningRecordsFragment : ··· 44 47 createDividerAdapterDelegate(), 45 48 createRunningRecordAdapterDelegate(viewModel::onRunningRecordClick, viewModel::onRunningRecordLongClick), 46 49 createRunningRecordTypeAdapterDelegate(viewModel::onRecordTypeClick, viewModel::onRecordTypeLongClick), 47 - createRunningRecordTypeAddAdapterDelegate(viewModel::onAddRecordTypeClick) 50 + createRunningRecordTypeAddAdapterDelegate(viewModel::onAddRecordTypeClick), 51 + createRunningRecordEmptyDividerAdapterDelegate(), 52 + createActivityFilterAdapterDelegate(viewModel::onActivityFilterClick, viewModel::onActivityFilterLongClick), 53 + createActivityFilterAddAdapterDelegate(viewModel::onAddActivityFilterClick), 48 54 ) 49 55 } 50 56
+13
features/feature_running_records/src/main/java/com/example/util/simpletimetracker/feature_running_records/viewData/RunningRecordEmptyDividerViewData.kt
··· 1 + package com.example.util.simpletimetracker.feature_running_records.viewData 2 + 3 + import com.example.util.simpletimetracker.feature_base_adapter.ViewHolderType 4 + 5 + data class RunningRecordEmptyDividerViewData( 6 + val id: Long, 7 + ) : ViewHolderType { 8 + 9 + override fun getUniqueId(): Long = id 10 + 11 + override fun isValidType(other: ViewHolderType): Boolean = 12 + other is RunningRecordEmptyDividerViewData 13 + }
+38 -1
features/feature_running_records/src/main/java/com/example/util/simpletimetracker/feature_running_records/viewModel/RunningRecordsViewModel.kt
··· 7 7 import com.example.util.simpletimetracker.core.extension.toParams 8 8 import com.example.util.simpletimetracker.core.interactor.AddRunningRecordMediator 9 9 import com.example.util.simpletimetracker.core.interactor.RemoveRunningRecordMediator 10 + import com.example.util.simpletimetracker.domain.interactor.ActivityFilterInteractor 11 + import com.example.util.simpletimetracker.domain.interactor.RecordTypeInteractor 10 12 import com.example.util.simpletimetracker.domain.interactor.RunningRecordInteractor 13 + import com.example.util.simpletimetracker.domain.model.ActivityFilter 14 + import com.example.util.simpletimetracker.domain.model.AppColor 11 15 import com.example.util.simpletimetracker.feature_base_adapter.ViewHolderType 16 + import com.example.util.simpletimetracker.feature_base_adapter.activityFilter.ActivityFilterViewData 12 17 import com.example.util.simpletimetracker.feature_base_adapter.loader.LoaderViewData 13 18 import com.example.util.simpletimetracker.feature_base_adapter.recordType.RecordTypeViewData 14 19 import com.example.util.simpletimetracker.feature_running_records.interactor.RunningRecordsViewDataInteractor ··· 31 36 private val addRunningRecordMediator: AddRunningRecordMediator, 32 37 private val removeRunningRecordMediator: RemoveRunningRecordMediator, 33 38 private val runningRecordInteractor: RunningRecordInteractor, 34 - private val runningRecordsViewDataInteractor: RunningRecordsViewDataInteractor 39 + private val runningRecordsViewDataInteractor: RunningRecordsViewDataInteractor, 40 + private val recordTypeInteractor: RecordTypeInteractor, 41 + private val activityFilterInteractor: ActivityFilterInteractor, 35 42 ) : ViewModel() { 36 43 37 44 val runningRecords: LiveData<List<ViewHolderType>> by lazy { ··· 107 114 ), 108 115 sharedElements = sharedElements 109 116 ) 117 + } 118 + 119 + fun onActivityFilterClick(item: ActivityFilterViewData) = viewModelScope.launch { 120 + activityFilterInteractor.changeSelected(item.id, !item.selected) 121 + updateRunningRecords() 122 + } 123 + 124 + fun onActivityFilterLongClick(item: ActivityFilterViewData, sharedElements: Pair<Any, String>) { 125 + // TODO navigate 126 + viewModelScope.launch { 127 + activityFilterInteractor.remove(item.id) 128 + updateRunningRecords() 129 + } 130 + } 131 + 132 + fun onAddActivityFilterClick() = viewModelScope.launch { 133 + // TODO navigate 134 + activityFilterInteractor.add( 135 + ActivityFilter( 136 + selectedIds = recordTypeInteractor.getAll().map { it.id }.shuffled().take(3), 137 + type = ActivityFilter.Type.Activity, 138 + name = "Test ${(1..100).random()}", 139 + color = AppColor( 140 + colorId = 2, 141 + colorInt = "", 142 + ), 143 + selected = true, 144 + ) 145 + ) 146 + updateRunningRecords() 110 147 } 111 148 112 149 fun onVisible() {