From 53f7f9930f68157e50dd98ebf28cff971bbcf6a1 Mon Sep 17 00:00:00 2001 From: razeeman Date: Fri, 4 Nov 2022 11:56:24 +0300 Subject: [PATCH] adding activity filters on main --- .../9.json | 383 ++++++++++++++++++ .../data_local/database/ActivityFilterDao.kt | 32 ++ .../data_local/database/AppDatabase.kt | 6 +- .../database/AppDatabaseMigrations.kt | 8 + .../data_local/di/DataLocalModule.kt | 10 +- .../data_local/di/DataLocalModuleBinds.kt | 6 + .../mapper/ActivityFilterDataLocalMapper.kt | 42 ++ .../data_local/model/ActivityFilterDBO.kt | 32 ++ .../data_local/repo/ActivityFilterRepoImpl.kt | 52 +++ .../interactor/ActivityFilterInteractor.kt | 34 ++ .../domain/model/ActivityFilter.kt | 16 + .../domain/repo/ActivityFilterRepo.kt | 18 + .../feature_base_adapter/build.gradle.kts | 1 + .../ActivityFilterAdapterDelegate.kt | 31 ++ .../ActivityFilterAddAdapterDelegate.kt | 21 + .../ActivityFilterAddViewData.kt | 16 + .../activityFilter/ActivityFilterViewData.kt | 18 + .../layout/item_activity_filter_layout.xml | 9 + .../src/main/res/layout/item_empty_layout.xml | 2 +- ...unningRecordEmptyDividerAdapterDelegate.kt | 12 + .../RunningRecordsViewDataInteractor.kt | 71 +++- .../mapper/RunningRecordViewDataMapper.kt | 50 ++- .../view/RunningRecordsFragment.kt | 8 +- .../RunningRecordEmptyDividerViewData.kt | 13 + .../viewModel/RunningRecordsViewModel.kt | 39 +- .../item_running_record_empty_divider.xml | 3 + .../feature_views/ActivityFilterView.kt | 57 +++ .../feature_views/TransitionNames.kt | 1 + .../layout/activity_filter_view_layout.xml | 37 ++ .../src/main/res/values/attrs.xml | 5 + 30 files changed, 1016 insertions(+), 17 deletions(-) create mode 100644 data_local/schemas/com.example.util.simpletimetracker.data_local.database.AppDatabase/9.json create mode 100644 data_local/src/main/java/com/example/util/simpletimetracker/data_local/database/ActivityFilterDao.kt create mode 100644 data_local/src/main/java/com/example/util/simpletimetracker/data_local/mapper/ActivityFilterDataLocalMapper.kt create mode 100644 data_local/src/main/java/com/example/util/simpletimetracker/data_local/model/ActivityFilterDBO.kt create mode 100644 data_local/src/main/java/com/example/util/simpletimetracker/data_local/repo/ActivityFilterRepoImpl.kt create mode 100644 domain/src/main/java/com/example/util/simpletimetracker/domain/interactor/ActivityFilterInteractor.kt create mode 100644 domain/src/main/java/com/example/util/simpletimetracker/domain/model/ActivityFilter.kt create mode 100644 domain/src/main/java/com/example/util/simpletimetracker/domain/repo/ActivityFilterRepo.kt create mode 100644 features/feature_base_adapter/src/main/java/com/example/util/simpletimetracker/feature_base_adapter/activityFilter/ActivityFilterAdapterDelegate.kt create mode 100644 features/feature_base_adapter/src/main/java/com/example/util/simpletimetracker/feature_base_adapter/activityFilter/ActivityFilterAddAdapterDelegate.kt create mode 100644 features/feature_base_adapter/src/main/java/com/example/util/simpletimetracker/feature_base_adapter/activityFilter/ActivityFilterAddViewData.kt create mode 100644 features/feature_base_adapter/src/main/java/com/example/util/simpletimetracker/feature_base_adapter/activityFilter/ActivityFilterViewData.kt create mode 100644 features/feature_base_adapter/src/main/res/layout/item_activity_filter_layout.xml create mode 100644 features/feature_running_records/src/main/java/com/example/util/simpletimetracker/feature_running_records/adapter/RunningRecordEmptyDividerAdapterDelegate.kt create mode 100644 features/feature_running_records/src/main/java/com/example/util/simpletimetracker/feature_running_records/viewData/RunningRecordEmptyDividerViewData.kt create mode 100644 features/feature_running_records/src/main/res/layout/item_running_record_empty_divider.xml create mode 100644 features/feature_views/src/main/java/com/example/util/simpletimetracker/feature_views/ActivityFilterView.kt create mode 100644 features/feature_views/src/main/res/layout/activity_filter_view_layout.xml diff --git a/data_local/schemas/com.example.util.simpletimetracker.data_local.database.AppDatabase/9.json b/data_local/schemas/com.example.util.simpletimetracker.data_local.database.AppDatabase/9.json new file mode 100644 index 00000000..aa58dcc6 --- /dev/null +++ b/data_local/schemas/com.example.util.simpletimetracker.data_local.database.AppDatabase/9.json @@ -0,0 +1,383 @@ +{ + "formatVersion": 1, + "database": { + "version": 9, + "identityHash": "a164edb0e90c58b0f9cdee8e932e8255", + "entities": [ + { + "tableName": "records", + "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)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "typeId", + "columnName": "type_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "timeStarted", + "columnName": "time_started", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "timeEnded", + "columnName": "time_ended", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "comment", + "columnName": "comment", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "tagId", + "columnName": "tag_id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "recordTypes", + "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)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "icon", + "columnName": "icon", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "color", + "columnName": "color", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "colorInt", + "columnName": "color_int", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "hidden", + "columnName": "hidden", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "goalTime", + "columnName": "goal_time", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "runningRecords", + "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`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "timeStarted", + "columnName": "time_started", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "comment", + "columnName": "comment", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "tagId", + "columnName": "tag_id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "categories", + "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)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "color", + "columnName": "color", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "colorInt", + "columnName": "color_int", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "recordTypeCategory", + "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`))", + "fields": [ + { + "fieldPath": "recordTypeId", + "columnName": "record_type_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "categoryId", + "columnName": "category_id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "record_type_id", + "category_id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "recordTags", + "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)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "typeId", + "columnName": "type_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "color", + "columnName": "color", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "colorInt", + "columnName": "color_int", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "archived", + "columnName": "archived", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "recordToRecordTag", + "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`))", + "fields": [ + { + "fieldPath": "recordId", + "columnName": "record_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "recordTagId", + "columnName": "record_tag_id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "record_id", + "record_tag_id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "runningRecordToRecordTag", + "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`))", + "fields": [ + { + "fieldPath": "runningRecordId", + "columnName": "running_record_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "recordTagId", + "columnName": "record_tag_id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "running_record_id", + "record_tag_id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "activityFilters", + "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)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "selectedIds", + "columnName": "selectedIds", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "color", + "columnName": "color", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "colorInt", + "columnName": "color_int", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "selected", + "columnName": "selected", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'a164edb0e90c58b0f9cdee8e932e8255')" + ] + } +} \ No newline at end of file diff --git a/data_local/src/main/java/com/example/util/simpletimetracker/data_local/database/ActivityFilterDao.kt b/data_local/src/main/java/com/example/util/simpletimetracker/data_local/database/ActivityFilterDao.kt new file mode 100644 index 00000000..bc369a2b --- /dev/null +++ b/data_local/src/main/java/com/example/util/simpletimetracker/data_local/database/ActivityFilterDao.kt @@ -0,0 +1,32 @@ +package com.example.util.simpletimetracker.data_local.database + +import androidx.room.Dao +import androidx.room.Insert +import androidx.room.OnConflictStrategy +import androidx.room.Query +import androidx.room.Transaction +import com.example.util.simpletimetracker.data_local.model.ActivityFilterDBO + +@Dao +interface ActivityFilterDao { + + @Transaction + @Query("SELECT * FROM activityFilters") + suspend fun getAll(): List + + @Transaction + @Query("SELECT * FROM activityFilters WHERE id = :id LIMIT 1") + suspend fun get(id: Long): ActivityFilterDBO? + + @Insert(onConflict = OnConflictStrategy.REPLACE) + suspend fun insert(activityFilter: ActivityFilterDBO): Long + + @Query("UPDATE activityFilters SET selected =:selected WHERE id = :id") + suspend fun changeSelected(id: Long, selected: Int) + + @Query("DELETE FROM activityFilters WHERE id = :id") + suspend fun delete(id: Long) + + @Query("DELETE FROM activityFilters") + suspend fun clear() +} \ No newline at end of file diff --git a/data_local/src/main/java/com/example/util/simpletimetracker/data_local/database/AppDatabase.kt b/data_local/src/main/java/com/example/util/simpletimetracker/data_local/database/AppDatabase.kt index 0e020c00..cc76fbff 100644 --- a/data_local/src/main/java/com/example/util/simpletimetracker/data_local/database/AppDatabase.kt +++ b/data_local/src/main/java/com/example/util/simpletimetracker/data_local/database/AppDatabase.kt @@ -2,6 +2,7 @@ package com.example.util.simpletimetracker.data_local.database import androidx.room.Database import androidx.room.RoomDatabase +import com.example.util.simpletimetracker.data_local.model.ActivityFilterDBO import com.example.util.simpletimetracker.data_local.model.CategoryDBO import com.example.util.simpletimetracker.data_local.model.RecordDBO import com.example.util.simpletimetracker.data_local.model.RecordTagDBO @@ -21,8 +22,9 @@ import com.example.util.simpletimetracker.data_local.model.RunningRecordToRecord RecordTagDBO::class, RecordToRecordTagDBO::class, RunningRecordToRecordTagDBO::class, + ActivityFilterDBO::class, ], - version = 8, + version = 9, exportSchema = true, ) abstract class AppDatabase : RoomDatabase() { @@ -43,6 +45,8 @@ abstract class AppDatabase : RoomDatabase() { abstract fun runningRecordToRecordTagDao(): RunningRecordToRecordTagDao + abstract fun activityFilterDao(): ActivityFilterDao + companion object { const val DATABASE_NAME = "simpleTimeTrackerDB" } diff --git a/data_local/src/main/java/com/example/util/simpletimetracker/data_local/database/AppDatabaseMigrations.kt b/data_local/src/main/java/com/example/util/simpletimetracker/data_local/database/AppDatabaseMigrations.kt index 43615348..9b8b19a4 100644 --- a/data_local/src/main/java/com/example/util/simpletimetracker/data_local/database/AppDatabaseMigrations.kt +++ b/data_local/src/main/java/com/example/util/simpletimetracker/data_local/database/AppDatabaseMigrations.kt @@ -100,5 +100,13 @@ class AppDatabaseMigrations { ) } } + + val migration_8_9 = object : Migration(8, 9) { + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL( + "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)" + ) + } + } } } \ No newline at end of file diff --git a/data_local/src/main/java/com/example/util/simpletimetracker/data_local/di/DataLocalModule.kt b/data_local/src/main/java/com/example/util/simpletimetracker/data_local/di/DataLocalModule.kt index 3c6e9eaa..91d293e8 100644 --- a/data_local/src/main/java/com/example/util/simpletimetracker/data_local/di/DataLocalModule.kt +++ b/data_local/src/main/java/com/example/util/simpletimetracker/data_local/di/DataLocalModule.kt @@ -4,6 +4,7 @@ import android.content.Context import android.content.SharedPreferences import androidx.room.Room import com.example.util.simpletimetracker.core.extension.allowDiskWrite +import com.example.util.simpletimetracker.data_local.database.ActivityFilterDao import com.example.util.simpletimetracker.data_local.database.AppDatabase import com.example.util.simpletimetracker.data_local.database.AppDatabaseMigrations import com.example.util.simpletimetracker.data_local.database.CategoryDao @@ -45,6 +46,7 @@ class DataLocalModule { AppDatabaseMigrations.migration_5_6, AppDatabaseMigrations.migration_6_7, AppDatabaseMigrations.migration_7_8, + AppDatabaseMigrations.migration_8_9, ) .build() } @@ -101,7 +103,13 @@ class DataLocalModule { @Provides @Singleton - fun getRunningRecordToRecordTagDap(database: AppDatabase): RunningRecordToRecordTagDao { + fun getRunningRecordToRecordTagDao(database: AppDatabase): RunningRecordToRecordTagDao { return database.runningRecordToRecordTagDao() } + + @Provides + @Singleton + fun getActivityFilterDao(database: AppDatabase): ActivityFilterDao { + return database.activityFilterDao() + } } \ No newline at end of file diff --git a/data_local/src/main/java/com/example/util/simpletimetracker/data_local/di/DataLocalModuleBinds.kt b/data_local/src/main/java/com/example/util/simpletimetracker/data_local/di/DataLocalModuleBinds.kt index 6a2dff97..3f9608a4 100644 --- a/data_local/src/main/java/com/example/util/simpletimetracker/data_local/di/DataLocalModuleBinds.kt +++ b/data_local/src/main/java/com/example/util/simpletimetracker/data_local/di/DataLocalModuleBinds.kt @@ -1,5 +1,6 @@ package com.example.util.simpletimetracker.data_local.di +import com.example.util.simpletimetracker.data_local.repo.ActivityFilterRepoImpl import com.example.util.simpletimetracker.data_local.repo.CategoryRepoImpl import com.example.util.simpletimetracker.data_local.repo.PrefsRepoImpl import com.example.util.simpletimetracker.data_local.repo.RecordRepoImpl @@ -11,6 +12,7 @@ import com.example.util.simpletimetracker.data_local.repo.RunningRecordRepoImpl import com.example.util.simpletimetracker.data_local.repo.RunningRecordToRecordTagRepoImpl import com.example.util.simpletimetracker.data_local.resolver.BackupRepoImpl import com.example.util.simpletimetracker.data_local.resolver.CsvRepoImpl +import com.example.util.simpletimetracker.domain.repo.ActivityFilterRepo import com.example.util.simpletimetracker.domain.repo.CategoryRepo import com.example.util.simpletimetracker.domain.repo.PrefsRepo import com.example.util.simpletimetracker.domain.repo.RecordRepo @@ -75,4 +77,8 @@ interface DataLocalModuleBinds { @Binds @Singleton fun RunningRecordToRecordTagRepoImpl.bindRunningRecordToRecordTagRepo(): RunningRecordToRecordTagRepo + + @Binds + @Singleton + fun ActivityFilterRepoImpl.bindActivityFilterRepo(): ActivityFilterRepo } \ No newline at end of file diff --git a/data_local/src/main/java/com/example/util/simpletimetracker/data_local/mapper/ActivityFilterDataLocalMapper.kt b/data_local/src/main/java/com/example/util/simpletimetracker/data_local/mapper/ActivityFilterDataLocalMapper.kt new file mode 100644 index 00000000..c3923f45 --- /dev/null +++ b/data_local/src/main/java/com/example/util/simpletimetracker/data_local/mapper/ActivityFilterDataLocalMapper.kt @@ -0,0 +1,42 @@ +package com.example.util.simpletimetracker.data_local.mapper + +import com.example.util.simpletimetracker.data_local.model.ActivityFilterDBO +import com.example.util.simpletimetracker.domain.model.ActivityFilter +import com.example.util.simpletimetracker.domain.model.AppColor +import javax.inject.Inject + +class ActivityFilterDataLocalMapper @Inject constructor() { + + fun map(dbo: ActivityFilterDBO): ActivityFilter { + return ActivityFilter( + id = dbo.id, + selectedIds = dbo.selectedIds.split(',').mapNotNull(String::toLongOrNull), + type = when (dbo.type) { + 0L -> ActivityFilter.Type.Activity + 1L -> ActivityFilter.Type.ActivityTag + else -> ActivityFilter.Type.Activity + }, + name = dbo.name, + color = AppColor( + colorId = dbo.color, + colorInt = dbo.colorInt + ), + selected = dbo.selected, + ) + } + + fun map(domain: ActivityFilter): ActivityFilterDBO { + return ActivityFilterDBO( + id = domain.id, + selectedIds = domain.selectedIds.joinToString(separator = ","), + type = when (domain.type) { + is ActivityFilter.Type.Activity -> 0L + is ActivityFilter.Type.ActivityTag -> 1L + }, + name = domain.name, + color = domain.color.colorId, + colorInt = domain.color.colorInt, + selected = domain.selected, + ) + } +} \ No newline at end of file diff --git a/data_local/src/main/java/com/example/util/simpletimetracker/data_local/model/ActivityFilterDBO.kt b/data_local/src/main/java/com/example/util/simpletimetracker/data_local/model/ActivityFilterDBO.kt new file mode 100644 index 00000000..eb39cafa --- /dev/null +++ b/data_local/src/main/java/com/example/util/simpletimetracker/data_local/model/ActivityFilterDBO.kt @@ -0,0 +1,32 @@ +package com.example.util.simpletimetracker.data_local.model + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.PrimaryKey + +@Entity(tableName = "activityFilters") +data class ActivityFilterDBO( + @PrimaryKey(autoGenerate = true) + @ColumnInfo(name = "id") + val id: Long, + + // Longs stored in string comma separated + @ColumnInfo(name = "selectedIds") + val selectedIds: String, + + @ColumnInfo(name = "type") + val type: Long, + + @ColumnInfo(name = "name") + val name: String, + + @ColumnInfo(name = "color") + val color: Int, + + // If not empty - take color from here, custom colorInt stored as text. + @ColumnInfo(name = "color_int") + val colorInt: String, + + @ColumnInfo(name = "selected") + val selected: Boolean, +) \ No newline at end of file diff --git a/data_local/src/main/java/com/example/util/simpletimetracker/data_local/repo/ActivityFilterRepoImpl.kt b/data_local/src/main/java/com/example/util/simpletimetracker/data_local/repo/ActivityFilterRepoImpl.kt new file mode 100644 index 00000000..9e1f1b7e --- /dev/null +++ b/data_local/src/main/java/com/example/util/simpletimetracker/data_local/repo/ActivityFilterRepoImpl.kt @@ -0,0 +1,52 @@ +package com.example.util.simpletimetracker.data_local.repo + +import com.example.util.simpletimetracker.data_local.database.ActivityFilterDao +import com.example.util.simpletimetracker.data_local.mapper.ActivityFilterDataLocalMapper +import com.example.util.simpletimetracker.domain.model.ActivityFilter +import com.example.util.simpletimetracker.domain.repo.ActivityFilterRepo +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import timber.log.Timber +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class ActivityFilterRepoImpl @Inject constructor( + private val activityFilterDao: ActivityFilterDao, + private val activityFilterDataLocalMapper: ActivityFilterDataLocalMapper, +) : ActivityFilterRepo { + + override suspend fun getAll(): List = withContext(Dispatchers.IO) { + Timber.d("getAll") + activityFilterDao.getAll() + .map(activityFilterDataLocalMapper::map) + } + + override suspend fun get(id: Long): ActivityFilter? = withContext(Dispatchers.IO) { + Timber.d("get") + activityFilterDao.get(id) + ?.let(activityFilterDataLocalMapper::map) + } + + override suspend fun add(activityFilter: ActivityFilter): Long = withContext(Dispatchers.IO) { + Timber.d("add") + return@withContext activityFilterDao.insert( + activityFilter.let(activityFilterDataLocalMapper::map) + ) + } + + override suspend fun changeSelected(id: Long, selected: Boolean) = withContext(Dispatchers.IO) { + Timber.d("changeSelected") + activityFilterDao.changeSelected(id, if (selected) 1 else 0) + } + + override suspend fun remove(id: Long) = withContext(Dispatchers.IO) { + Timber.d("remove") + activityFilterDao.delete(id) + } + + override suspend fun clear() = withContext(Dispatchers.IO) { + Timber.d("clear") + activityFilterDao.clear() + } +} \ No newline at end of file diff --git a/domain/src/main/java/com/example/util/simpletimetracker/domain/interactor/ActivityFilterInteractor.kt b/domain/src/main/java/com/example/util/simpletimetracker/domain/interactor/ActivityFilterInteractor.kt new file mode 100644 index 00000000..0800e13d --- /dev/null +++ b/domain/src/main/java/com/example/util/simpletimetracker/domain/interactor/ActivityFilterInteractor.kt @@ -0,0 +1,34 @@ +package com.example.util.simpletimetracker.domain.interactor + +import com.example.util.simpletimetracker.domain.model.ActivityFilter +import com.example.util.simpletimetracker.domain.repo.ActivityFilterRepo +import javax.inject.Inject + +class ActivityFilterInteractor @Inject constructor( + private val activityFilterRepo: ActivityFilterRepo, +) { + + suspend fun getAll(): List { + return activityFilterRepo.getAll() + } + + suspend fun get(id: Long): ActivityFilter? { + return activityFilterRepo.get(id) + } + + suspend fun add(record: ActivityFilter) { + activityFilterRepo.add(record) + } + + suspend fun changeSelected(id: Long, selected: Boolean) { + activityFilterRepo.changeSelected(id, selected) + } + + suspend fun remove(id: Long) { + activityFilterRepo.remove(id) + } + + suspend fun clear() { + activityFilterRepo.clear() + } +} \ No newline at end of file diff --git a/domain/src/main/java/com/example/util/simpletimetracker/domain/model/ActivityFilter.kt b/domain/src/main/java/com/example/util/simpletimetracker/domain/model/ActivityFilter.kt new file mode 100644 index 00000000..715708ed --- /dev/null +++ b/domain/src/main/java/com/example/util/simpletimetracker/domain/model/ActivityFilter.kt @@ -0,0 +1,16 @@ +package com.example.util.simpletimetracker.domain.model + +data class ActivityFilter( + val id: Long = 0, + val selectedIds: List, + val type: Type, + val name: String, + val color: AppColor, + val selected: Boolean, +) { + + sealed interface Type { + object ActivityTag: Type + object Activity: Type + } +} \ No newline at end of file diff --git a/domain/src/main/java/com/example/util/simpletimetracker/domain/repo/ActivityFilterRepo.kt b/domain/src/main/java/com/example/util/simpletimetracker/domain/repo/ActivityFilterRepo.kt new file mode 100644 index 00000000..8986615b --- /dev/null +++ b/domain/src/main/java/com/example/util/simpletimetracker/domain/repo/ActivityFilterRepo.kt @@ -0,0 +1,18 @@ +package com.example.util.simpletimetracker.domain.repo + +import com.example.util.simpletimetracker.domain.model.ActivityFilter + +interface ActivityFilterRepo { + + suspend fun getAll(): List + + suspend fun get(id: Long): ActivityFilter? + + suspend fun add(activityFilter: ActivityFilter): Long + + suspend fun changeSelected(id: Long, selected: Boolean) + + suspend fun remove(id: Long) + + suspend fun clear() +} \ No newline at end of file diff --git a/features/feature_base_adapter/build.gradle.kts b/features/feature_base_adapter/build.gradle.kts index 0e59a73c..fb8b4bce 100644 --- a/features/feature_base_adapter/build.gradle.kts +++ b/features/feature_base_adapter/build.gradle.kts @@ -32,4 +32,5 @@ dependencies { implementation(Deps.Androidx.recyclerView) implementation(Deps.Androidx.cardView) implementation(Deps.Androidx.material) + implementation(Deps.Google.flexBox) } diff --git a/features/feature_base_adapter/src/main/java/com/example/util/simpletimetracker/feature_base_adapter/activityFilter/ActivityFilterAdapterDelegate.kt b/features/feature_base_adapter/src/main/java/com/example/util/simpletimetracker/feature_base_adapter/activityFilter/ActivityFilterAdapterDelegate.kt new file mode 100644 index 00000000..1efb22d1 --- /dev/null +++ b/features/feature_base_adapter/src/main/java/com/example/util/simpletimetracker/feature_base_adapter/activityFilter/ActivityFilterAdapterDelegate.kt @@ -0,0 +1,31 @@ +package com.example.util.simpletimetracker.feature_base_adapter.activityFilter + +import com.example.util.simpletimetracker.feature_base_adapter.activityFilter.ActivityFilterViewData as ViewData +import com.example.util.simpletimetracker.feature_base_adapter.databinding.ItemActivityFilterLayoutBinding as Binding +import androidx.core.view.ViewCompat +import com.example.util.simpletimetracker.feature_base_adapter.createRecyclerBindingAdapterDelegate +import com.example.util.simpletimetracker.feature_views.TransitionNames +import com.example.util.simpletimetracker.feature_views.extension.setOnClickWith +import com.example.util.simpletimetracker.feature_views.extension.setOnLongClick + +fun createActivityFilterAdapterDelegate( + onClick: ((ViewData) -> Unit), + onLongClick: ((ViewData, Pair) -> Unit), +) = createRecyclerBindingAdapterDelegate( + Binding::inflate +) { binding, item, _ -> + + with(binding.viewActivityFilterItem) { + item as ViewData + + val transitionName = TransitionNames.ACTIVITY_FILTER_TAG + item.id + val sharedElements: Pair = this to transitionName + ViewCompat.setTransitionName(this, transitionName) + + itemColor = item.color + itemName = item.name + + setOnClickWith(item, onClick) + setOnLongClick { onLongClick(item, sharedElements) } + } +} \ No newline at end of file diff --git a/features/feature_base_adapter/src/main/java/com/example/util/simpletimetracker/feature_base_adapter/activityFilter/ActivityFilterAddAdapterDelegate.kt b/features/feature_base_adapter/src/main/java/com/example/util/simpletimetracker/feature_base_adapter/activityFilter/ActivityFilterAddAdapterDelegate.kt new file mode 100644 index 00000000..ab51b58a --- /dev/null +++ b/features/feature_base_adapter/src/main/java/com/example/util/simpletimetracker/feature_base_adapter/activityFilter/ActivityFilterAddAdapterDelegate.kt @@ -0,0 +1,21 @@ +package com.example.util.simpletimetracker.feature_base_adapter.activityFilter + +import com.example.util.simpletimetracker.feature_base_adapter.activityFilter.ActivityFilterAddViewData as ViewData +import com.example.util.simpletimetracker.feature_base_adapter.databinding.ItemActivityFilterLayoutBinding as Binding +import com.example.util.simpletimetracker.feature_base_adapter.createRecyclerBindingAdapterDelegate +import com.example.util.simpletimetracker.feature_views.extension.setOnClick + +fun createActivityFilterAddAdapterDelegate( + onItemClick: (() -> Unit), +) = createRecyclerBindingAdapterDelegate( + Binding::inflate +) { binding, item, _ -> + + with(binding.viewActivityFilterItem) { + item as ViewData + + itemColor = item.color + itemName = item.name + setOnClick(onItemClick) + } +} \ No newline at end of file diff --git a/features/feature_base_adapter/src/main/java/com/example/util/simpletimetracker/feature_base_adapter/activityFilter/ActivityFilterAddViewData.kt b/features/feature_base_adapter/src/main/java/com/example/util/simpletimetracker/feature_base_adapter/activityFilter/ActivityFilterAddViewData.kt new file mode 100644 index 00000000..fcdc2346 --- /dev/null +++ b/features/feature_base_adapter/src/main/java/com/example/util/simpletimetracker/feature_base_adapter/activityFilter/ActivityFilterAddViewData.kt @@ -0,0 +1,16 @@ +package com.example.util.simpletimetracker.feature_base_adapter.activityFilter + +import androidx.annotation.ColorInt +import com.example.util.simpletimetracker.feature_base_adapter.ViewHolderType + +class ActivityFilterAddViewData( + val name: String, + @ColorInt val color: Int +) : ViewHolderType { + + // Only one add item on screen + override fun getUniqueId(): Long = 1L + + override fun isValidType(other: ViewHolderType): Boolean = + other is ActivityFilterAddViewData +} \ No newline at end of file diff --git a/features/feature_base_adapter/src/main/java/com/example/util/simpletimetracker/feature_base_adapter/activityFilter/ActivityFilterViewData.kt b/features/feature_base_adapter/src/main/java/com/example/util/simpletimetracker/feature_base_adapter/activityFilter/ActivityFilterViewData.kt new file mode 100644 index 00000000..cbf81461 --- /dev/null +++ b/features/feature_base_adapter/src/main/java/com/example/util/simpletimetracker/feature_base_adapter/activityFilter/ActivityFilterViewData.kt @@ -0,0 +1,18 @@ +package com.example.util.simpletimetracker.feature_base_adapter.activityFilter + +import androidx.annotation.ColorInt +import com.example.util.simpletimetracker.feature_base_adapter.ViewHolderType + +data class ActivityFilterViewData( + val id: Long, + val name: String, + @ColorInt val iconColor: Int, + @ColorInt val color: Int, + val selected: Boolean, +) : ViewHolderType { + + override fun getUniqueId(): Long = id + + override fun isValidType(other: ViewHolderType): Boolean = + other is ActivityFilterViewData +} \ No newline at end of file diff --git a/features/feature_base_adapter/src/main/res/layout/item_activity_filter_layout.xml b/features/feature_base_adapter/src/main/res/layout/item_activity_filter_layout.xml new file mode 100644 index 00000000..660668fb --- /dev/null +++ b/features/feature_base_adapter/src/main/res/layout/item_activity_filter_layout.xml @@ -0,0 +1,9 @@ + + \ No newline at end of file diff --git a/features/feature_base_adapter/src/main/res/layout/item_empty_layout.xml b/features/feature_base_adapter/src/main/res/layout/item_empty_layout.xml index 0c7c00a2..a6cea10a 100644 --- a/features/feature_base_adapter/src/main/res/layout/item_empty_layout.xml +++ b/features/feature_base_adapter/src/main/res/layout/item_empty_layout.xml @@ -4,7 +4,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" - android:paddingHorizontal="8dp" + android:paddingHorizontal="16dp" android:paddingVertical="16dp"> ( + Binding::inflate +) { _, _, _ -> + + // Nothing to bind +} \ No newline at end of file diff --git a/features/feature_running_records/src/main/java/com/example/util/simpletimetracker/feature_running_records/interactor/RunningRecordsViewDataInteractor.kt b/features/feature_running_records/src/main/java/com/example/util/simpletimetracker/feature_running_records/interactor/RunningRecordsViewDataInteractor.kt index 0cba5c34..72d3874c 100644 --- a/features/feature_running_records/src/main/java/com/example/util/simpletimetracker/feature_running_records/interactor/RunningRecordsViewDataInteractor.kt +++ b/features/feature_running_records/src/main/java/com/example/util/simpletimetracker/feature_running_records/interactor/RunningRecordsViewDataInteractor.kt @@ -1,12 +1,16 @@ package com.example.util.simpletimetracker.feature_running_records.interactor -import com.example.util.simpletimetracker.feature_base_adapter.ViewHolderType -import com.example.util.simpletimetracker.feature_base_adapter.divider.DividerViewData +import com.example.util.simpletimetracker.domain.interactor.ActivityFilterInteractor import com.example.util.simpletimetracker.domain.interactor.PrefsInteractor import com.example.util.simpletimetracker.domain.interactor.RecordTagInteractor +import com.example.util.simpletimetracker.domain.interactor.RecordTypeCategoryInteractor import com.example.util.simpletimetracker.domain.interactor.RecordTypeInteractor import com.example.util.simpletimetracker.domain.interactor.RunningRecordInteractor +import com.example.util.simpletimetracker.domain.model.ActivityFilter +import com.example.util.simpletimetracker.feature_base_adapter.ViewHolderType +import com.example.util.simpletimetracker.feature_base_adapter.divider.DividerViewData import com.example.util.simpletimetracker.feature_running_records.mapper.RunningRecordViewDataMapper +import com.example.util.simpletimetracker.feature_running_records.viewData.RunningRecordEmptyDividerViewData import javax.inject.Inject class RunningRecordsViewDataInteractor @Inject constructor( @@ -14,15 +18,18 @@ class RunningRecordsViewDataInteractor @Inject constructor( private val recordTypeInteractor: RecordTypeInteractor, private val recordTagInteractor: RecordTagInteractor, private val runningRecordInteractor: RunningRecordInteractor, - private val mapper: RunningRecordViewDataMapper + private val activityFilterInteractor: ActivityFilterInteractor, + private val recordTypeCategoryInteractor: RecordTypeCategoryInteractor, + private val mapper: RunningRecordViewDataMapper, ) { suspend fun getViewData(): List { val recordTypes = recordTypeInteractor.getAll() - val recordTypesMap = recordTypes.map { it.id to it }.toMap() + val recordTypesMap = recordTypes.associateBy { it.id } val recordTags = recordTagInteractor.getAll() val runningRecords = runningRecordInteractor.getAll() val recordTypesRunning = runningRecords.map { it.id } + val activityFilters = activityFilterInteractor.getAll() val numberOfCards = prefsInteractor.getNumberOfCards() val isDarkTheme = prefsInteractor.getDarkMode() val useMilitaryTime = prefsInteractor.getUseMilitaryTimeFormat() @@ -48,10 +55,32 @@ class RunningRecordsViewDataInteractor @Inject constructor( } } + val filtersViewData = activityFilters + .map { + mapper.map( + filter = it, + isDarkTheme = isDarkTheme, + ) + } + .plus( + mapper.mapToActivityFilterAddItem( + isDarkTheme = isDarkTheme, + ) + ) + .plus( + RunningRecordEmptyDividerViewData(1) + ) + + val anyFiltersSelected = activityFilters.any { it.selected } + val selectedTypes = getSelectedTypeIds(anyFiltersSelected, activityFilters) val recordTypesViewData = recordTypes .filterNot { it.hidden } + .let { list -> + if (anyFiltersSelected) list.filter { it.id in selectedTypes } + else list + } .map { mapper.map( recordType = it, @@ -69,6 +98,40 @@ class RunningRecordsViewDataInteractor @Inject constructor( return runningRecordsViewData + listOf(DividerViewData(1)) + + filtersViewData + recordTypesViewData } + + private suspend fun getSelectedTypeIds( + anyFiltersSelected: Boolean, + filters: List, + ): List { + if (!anyFiltersSelected) return emptyList() + + val selectedFilters = filters.filter { it.selected } + + val activityIds: List = selectedFilters + .filter { it.type is ActivityFilter.Type.Activity } + .map { it.selectedIds } + .flatten() + + val fromCategoryIds: List = selectedFilters + .filter { it.type is ActivityFilter.Type.ActivityTag } + .map { it.selectedIds } + .flatten() + .takeUnless { it.isEmpty() } + ?.let { tagIds -> + val recordTypeCategories = recordTypeCategoryInteractor.getAll() + .groupBy { it.categoryId } + .mapValues { (_, value) -> value.map { it.recordTypeId } } + tagIds.mapNotNull { tagId -> recordTypeCategories[tagId] }.flatten() + } + .orEmpty() + + return when { + fromCategoryIds.isEmpty() -> activityIds + activityIds.isEmpty() -> fromCategoryIds + else -> activityIds + fromCategoryIds + } + } } \ No newline at end of file diff --git a/features/feature_running_records/src/main/java/com/example/util/simpletimetracker/feature_running_records/mapper/RunningRecordViewDataMapper.kt b/features/feature_running_records/src/main/java/com/example/util/simpletimetracker/feature_running_records/mapper/RunningRecordViewDataMapper.kt index 57aeb551..47fe0179 100644 --- a/features/feature_running_records/src/main/java/com/example/util/simpletimetracker/feature_running_records/mapper/RunningRecordViewDataMapper.kt +++ b/features/feature_running_records/src/main/java/com/example/util/simpletimetracker/feature_running_records/mapper/RunningRecordViewDataMapper.kt @@ -1,7 +1,5 @@ package com.example.util.simpletimetracker.feature_running_records.mapper -import com.example.util.simpletimetracker.feature_base_adapter.ViewHolderType -import com.example.util.simpletimetracker.feature_base_adapter.empty.EmptyViewData import com.example.util.simpletimetracker.core.mapper.ColorMapper import com.example.util.simpletimetracker.core.mapper.IconMapper import com.example.util.simpletimetracker.core.mapper.RecordTypeCardSizeMapper @@ -9,14 +7,19 @@ import com.example.util.simpletimetracker.core.mapper.RecordTypeViewDataMapper import com.example.util.simpletimetracker.core.mapper.TimeMapper import com.example.util.simpletimetracker.core.repo.ResourceRepo import com.example.util.simpletimetracker.domain.extension.getFullName -import com.example.util.simpletimetracker.feature_views.viewData.RecordTypeIcon -import com.example.util.simpletimetracker.feature_base_adapter.recordType.RecordTypeViewData +import com.example.util.simpletimetracker.domain.model.ActivityFilter import com.example.util.simpletimetracker.domain.model.RecordTag import com.example.util.simpletimetracker.domain.model.RecordType import com.example.util.simpletimetracker.domain.model.RunningRecord +import com.example.util.simpletimetracker.feature_base_adapter.ViewHolderType +import com.example.util.simpletimetracker.feature_base_adapter.activityFilter.ActivityFilterAddViewData +import com.example.util.simpletimetracker.feature_base_adapter.activityFilter.ActivityFilterViewData +import com.example.util.simpletimetracker.feature_base_adapter.empty.EmptyViewData +import com.example.util.simpletimetracker.feature_base_adapter.recordType.RecordTypeViewData import com.example.util.simpletimetracker.feature_running_records.R import com.example.util.simpletimetracker.feature_running_records.viewData.RunningRecordTypeAddViewData import com.example.util.simpletimetracker.feature_running_records.viewData.RunningRecordViewData +import com.example.util.simpletimetracker.feature_views.viewData.RecordTypeIcon import javax.inject.Inject class RunningRecordViewDataMapper @Inject constructor( @@ -25,7 +28,7 @@ class RunningRecordViewDataMapper @Inject constructor( private val resourceRepo: ResourceRepo, private val timeMapper: TimeMapper, private val recordTypeViewDataMapper: RecordTypeViewDataMapper, - private val recordTypeCardSizeMapper: RecordTypeCardSizeMapper + private val recordTypeCardSizeMapper: RecordTypeCardSizeMapper, ) { fun map( @@ -33,7 +36,7 @@ class RunningRecordViewDataMapper @Inject constructor( recordType: RecordType, recordTags: List, isDarkTheme: Boolean, - useMilitaryTime: Boolean + useMilitaryTime: Boolean, ): RunningRecordViewData { return RunningRecordViewData( id = runningRecord.id, @@ -61,7 +64,7 @@ class RunningRecordViewDataMapper @Inject constructor( recordType: RecordType, isFiltered: Boolean, numberOfCards: Int, - isDarkTheme: Boolean + isDarkTheme: Boolean, ): RecordTypeViewData { return recordTypeViewDataMapper.mapFiltered( recordType, @@ -71,6 +74,28 @@ class RunningRecordViewDataMapper @Inject constructor( ) } + fun map( + filter: ActivityFilter, + isDarkTheme: Boolean, + ): ActivityFilterViewData { + val selected = filter.selected + return ActivityFilterViewData( + id = filter.id, + name = filter.name, + iconColor = if (selected) { + colorMapper.toIconColor(isDarkTheme) + } else { + colorMapper.toFilteredIconColor(isDarkTheme) + }, + color = if (selected) { + colorMapper.mapToColorInt(filter.color, isDarkTheme) + } else { + colorMapper.toFilteredColor(isDarkTheme) + }, + selected = selected, + ) + } + fun mapToTypesEmpty(): ViewHolderType { return EmptyViewData( message = R.string.running_records_types_empty.let(resourceRepo::getString) @@ -86,7 +111,7 @@ class RunningRecordViewDataMapper @Inject constructor( fun mapToAddItem( numberOfCards: Int, - isDarkTheme: Boolean + isDarkTheme: Boolean, ): RunningRecordTypeAddViewData { return RunningRecordTypeAddViewData( name = R.string.running_records_add_type.let(resourceRepo::getString), @@ -97,4 +122,13 @@ class RunningRecordViewDataMapper @Inject constructor( asRow = recordTypeCardSizeMapper.toCardAsRow(numberOfCards) ) } + + fun mapToActivityFilterAddItem( + isDarkTheme: Boolean, + ): ActivityFilterAddViewData { + return ActivityFilterAddViewData( + name = "Add filter", + color = colorMapper.toInactiveColor(isDarkTheme) + ) + } } \ No newline at end of file diff --git a/features/feature_running_records/src/main/java/com/example/util/simpletimetracker/feature_running_records/view/RunningRecordsFragment.kt b/features/feature_running_records/src/main/java/com/example/util/simpletimetracker/feature_running_records/view/RunningRecordsFragment.kt index 260df5b9..802e6c5e 100644 --- a/features/feature_running_records/src/main/java/com/example/util/simpletimetracker/feature_running_records/view/RunningRecordsFragment.kt +++ b/features/feature_running_records/src/main/java/com/example/util/simpletimetracker/feature_running_records/view/RunningRecordsFragment.kt @@ -21,6 +21,9 @@ import com.google.android.flexbox.JustifyContent import dagger.hilt.android.AndroidEntryPoint import javax.inject.Inject import com.example.util.simpletimetracker.feature_running_records.databinding.RunningRecordsFragmentBinding as Binding +import com.example.util.simpletimetracker.feature_base_adapter.activityFilter.createActivityFilterAdapterDelegate +import com.example.util.simpletimetracker.feature_base_adapter.activityFilter.createActivityFilterAddAdapterDelegate +import com.example.util.simpletimetracker.feature_running_records.adapter.createRunningRecordEmptyDividerAdapterDelegate @AndroidEntryPoint class RunningRecordsFragment : @@ -44,7 +47,10 @@ class RunningRecordsFragment : createDividerAdapterDelegate(), createRunningRecordAdapterDelegate(viewModel::onRunningRecordClick, viewModel::onRunningRecordLongClick), createRunningRecordTypeAdapterDelegate(viewModel::onRecordTypeClick, viewModel::onRecordTypeLongClick), - createRunningRecordTypeAddAdapterDelegate(viewModel::onAddRecordTypeClick) + createRunningRecordTypeAddAdapterDelegate(viewModel::onAddRecordTypeClick), + createRunningRecordEmptyDividerAdapterDelegate(), + createActivityFilterAdapterDelegate(viewModel::onActivityFilterClick, viewModel::onActivityFilterLongClick), + createActivityFilterAddAdapterDelegate(viewModel::onAddActivityFilterClick), ) } diff --git a/features/feature_running_records/src/main/java/com/example/util/simpletimetracker/feature_running_records/viewData/RunningRecordEmptyDividerViewData.kt b/features/feature_running_records/src/main/java/com/example/util/simpletimetracker/feature_running_records/viewData/RunningRecordEmptyDividerViewData.kt new file mode 100644 index 00000000..58a20e3f --- /dev/null +++ b/features/feature_running_records/src/main/java/com/example/util/simpletimetracker/feature_running_records/viewData/RunningRecordEmptyDividerViewData.kt @@ -0,0 +1,13 @@ +package com.example.util.simpletimetracker.feature_running_records.viewData + +import com.example.util.simpletimetracker.feature_base_adapter.ViewHolderType + +data class RunningRecordEmptyDividerViewData( + val id: Long, +) : ViewHolderType { + + override fun getUniqueId(): Long = id + + override fun isValidType(other: ViewHolderType): Boolean = + other is RunningRecordEmptyDividerViewData +} \ No newline at end of file diff --git a/features/feature_running_records/src/main/java/com/example/util/simpletimetracker/feature_running_records/viewModel/RunningRecordsViewModel.kt b/features/feature_running_records/src/main/java/com/example/util/simpletimetracker/feature_running_records/viewModel/RunningRecordsViewModel.kt index fc308088..d9e06231 100644 --- a/features/feature_running_records/src/main/java/com/example/util/simpletimetracker/feature_running_records/viewModel/RunningRecordsViewModel.kt +++ b/features/feature_running_records/src/main/java/com/example/util/simpletimetracker/feature_running_records/viewModel/RunningRecordsViewModel.kt @@ -7,8 +7,13 @@ import androidx.lifecycle.viewModelScope import com.example.util.simpletimetracker.core.extension.toParams import com.example.util.simpletimetracker.core.interactor.AddRunningRecordMediator import com.example.util.simpletimetracker.core.interactor.RemoveRunningRecordMediator +import com.example.util.simpletimetracker.domain.interactor.ActivityFilterInteractor +import com.example.util.simpletimetracker.domain.interactor.RecordTypeInteractor import com.example.util.simpletimetracker.domain.interactor.RunningRecordInteractor +import com.example.util.simpletimetracker.domain.model.ActivityFilter +import com.example.util.simpletimetracker.domain.model.AppColor import com.example.util.simpletimetracker.feature_base_adapter.ViewHolderType +import com.example.util.simpletimetracker.feature_base_adapter.activityFilter.ActivityFilterViewData import com.example.util.simpletimetracker.feature_base_adapter.loader.LoaderViewData import com.example.util.simpletimetracker.feature_base_adapter.recordType.RecordTypeViewData import com.example.util.simpletimetracker.feature_running_records.interactor.RunningRecordsViewDataInteractor @@ -31,7 +36,9 @@ class RunningRecordsViewModel @Inject constructor( private val addRunningRecordMediator: AddRunningRecordMediator, private val removeRunningRecordMediator: RemoveRunningRecordMediator, private val runningRecordInteractor: RunningRecordInteractor, - private val runningRecordsViewDataInteractor: RunningRecordsViewDataInteractor + private val runningRecordsViewDataInteractor: RunningRecordsViewDataInteractor, + private val recordTypeInteractor: RecordTypeInteractor, + private val activityFilterInteractor: ActivityFilterInteractor, ) : ViewModel() { val runningRecords: LiveData> by lazy { @@ -109,6 +116,36 @@ class RunningRecordsViewModel @Inject constructor( ) } + fun onActivityFilterClick(item: ActivityFilterViewData) = viewModelScope.launch { + activityFilterInteractor.changeSelected(item.id, !item.selected) + updateRunningRecords() + } + + fun onActivityFilterLongClick(item: ActivityFilterViewData, sharedElements: Pair) { + // TODO navigate + viewModelScope.launch { + activityFilterInteractor.remove(item.id) + updateRunningRecords() + } + } + + fun onAddActivityFilterClick() = viewModelScope.launch { + // TODO navigate + activityFilterInteractor.add( + ActivityFilter( + selectedIds = recordTypeInteractor.getAll().map { it.id }.shuffled().take(3), + type = ActivityFilter.Type.Activity, + name = "Test ${(1..100).random()}", + color = AppColor( + colorId = 2, + colorInt = "", + ), + selected = true, + ) + ) + updateRunningRecords() + } + fun onVisible() { startUpdate() } diff --git a/features/feature_running_records/src/main/res/layout/item_running_record_empty_divider.xml b/features/feature_running_records/src/main/res/layout/item_running_record_empty_divider.xml new file mode 100644 index 00000000..590f583c --- /dev/null +++ b/features/feature_running_records/src/main/res/layout/item_running_record_empty_divider.xml @@ -0,0 +1,3 @@ + diff --git a/features/feature_views/src/main/java/com/example/util/simpletimetracker/feature_views/ActivityFilterView.kt b/features/feature_views/src/main/java/com/example/util/simpletimetracker/feature_views/ActivityFilterView.kt new file mode 100644 index 00000000..1c4b7826 --- /dev/null +++ b/features/feature_views/src/main/java/com/example/util/simpletimetracker/feature_views/ActivityFilterView.kt @@ -0,0 +1,57 @@ +package com.example.util.simpletimetracker.feature_views + +import android.content.Context +import android.graphics.Color +import android.util.AttributeSet +import android.view.LayoutInflater +import androidx.cardview.widget.CardView +import androidx.core.content.ContextCompat +import com.example.util.simpletimetracker.feature_views.databinding.ActivityFilterViewLayoutBinding +import com.example.util.simpletimetracker.feature_views.extension.dpToPx + +class ActivityFilterView @JvmOverloads constructor( + context: Context, + attrs: AttributeSet? = null, + defStyleAttr: Int = 0, +) : CardView( + context, + attrs, + defStyleAttr +) { + + private val binding: ActivityFilterViewLayoutBinding = ActivityFilterViewLayoutBinding + .inflate(LayoutInflater.from(context), this) + + init { + ContextCompat.getColor(context, R.color.black).let(::setCardBackgroundColor) + radius = 100.dpToPx().toFloat() + // TODO doesn't work here for some reason, need to set in the layout + cardElevation = resources.getDimensionPixelOffset(R.dimen.record_type_card_elevation) + .toFloat() + preventCornerOverlap = false + useCompatPadding = true + + context.obtainStyledAttributes(attrs, R.styleable.ActivityFilterView, defStyleAttr, 0) + .run { + if (hasValue(R.styleable.ActivityFilterView_itemName)) itemName = + getString(R.styleable.ActivityFilterView_itemName).orEmpty() + + if (hasValue(R.styleable.ActivityFilterView_itemColor)) itemColor = + getColor(R.styleable.ActivityFilterView_itemColor, Color.BLACK) + + recycle() + } + } + + var itemName: String = "" + set(value) { + binding.tvActivityFilterItemName.text = value + field = value + } + + var itemColor: Int = 0 + set(value) { + setCardBackgroundColor(value) + field = value + } +} \ No newline at end of file diff --git a/features/feature_views/src/main/java/com/example/util/simpletimetracker/feature_views/TransitionNames.kt b/features/feature_views/src/main/java/com/example/util/simpletimetracker/feature_views/TransitionNames.kt index f00590c4..65b09081 100644 --- a/features/feature_views/src/main/java/com/example/util/simpletimetracker/feature_views/TransitionNames.kt +++ b/features/feature_views/src/main/java/com/example/util/simpletimetracker/feature_views/TransitionNames.kt @@ -8,4 +8,5 @@ object TransitionNames { const val STATISTICS_DETAIL = "statistics_detail_transition_name" const val ACTIVITY_TAG = "activity_tag_transition_name" const val RECORD_TAG = "record_tag_transition_name" + const val ACTIVITY_FILTER_TAG = "activity_filter_tag_transition_name" } \ No newline at end of file diff --git a/features/feature_views/src/main/res/layout/activity_filter_view_layout.xml b/features/feature_views/src/main/res/layout/activity_filter_view_layout.xml new file mode 100644 index 00000000..35dee102 --- /dev/null +++ b/features/feature_views/src/main/res/layout/activity_filter_view_layout.xml @@ -0,0 +1,37 @@ + + + + + + + + + + \ No newline at end of file diff --git a/features/feature_views/src/main/res/values/attrs.xml b/features/feature_views/src/main/res/values/attrs.xml index e0795b92..1e29e121 100644 --- a/features/feature_views/src/main/res/values/attrs.xml +++ b/features/feature_views/src/main/res/values/attrs.xml @@ -90,6 +90,11 @@ + + + + + -- 2.51.2