From 1f7f41a531e9352c9afd4e45a32710e1e907fed9 Mon Sep 17 00:00:00 2001 From: Amanpal Singh <87360222+aman-alfresco@users.noreply.github.com> Date: Fri, 15 Jul 2022 11:43:13 +0530 Subject: [PATCH] Mobileapps 1295 (#91) * Mobileapps 1272 (#137) * added task navigation * Mobileapps 1294 (#135) * changed analytics name as per client feedback * updated analytics name * code merging --- .../alfresco/content/browse/BrowseFragment.kt | 2 +- .../content/browse/tasks/TasksFragment.kt | 7 ++ .../content/browse/tasks/TasksViewModel.kt | 5 + .../content/browse/tasks/TasksViewState.kt | 109 +++++++++++++++++- .../alfresco/content/data/AnalyticsManager.kt | 35 +++--- .../content/data/AnalyticsRepository.kt | 99 +++++++--------- .../com/alfresco/content/data/TaskEntry.kt | 15 ++- .../content/listview/tasks/ListViewTaskRow.kt | 2 +- .../listview/tasks/TaskListFragment.kt | 16 ++- 9 files changed, 205 insertions(+), 85 deletions(-) diff --git a/browse/src/main/kotlin/com/alfresco/content/browse/BrowseFragment.kt b/browse/src/main/kotlin/com/alfresco/content/browse/BrowseFragment.kt index 902d18777..1bca0eae2 100644 --- a/browse/src/main/kotlin/com/alfresco/content/browse/BrowseFragment.kt +++ b/browse/src/main/kotlin/com/alfresco/content/browse/BrowseFragment.kt @@ -170,7 +170,7 @@ class BrowseFragment : ListFragment() { if (entry.isTrashed) return if (entry.isFolder) - AnalyticsManager().screenViewEvent(PageView.FolderName, folderName = "PageView_${entry.name}") + AnalyticsManager().screenViewEvent(PageView.PersonalFiles) if (entry.isUpload) entry.mimeType?.let { diff --git a/browse/src/main/kotlin/com/alfresco/content/browse/tasks/TasksFragment.kt b/browse/src/main/kotlin/com/alfresco/content/browse/tasks/TasksFragment.kt index c4ebe333f..9af8b81be 100644 --- a/browse/src/main/kotlin/com/alfresco/content/browse/tasks/TasksFragment.kt +++ b/browse/src/main/kotlin/com/alfresco/content/browse/tasks/TasksFragment.kt @@ -3,6 +3,8 @@ package com.alfresco.content.browse.tasks import android.os.Bundle import android.view.View import com.airbnb.mvrx.fragmentViewModel +import com.alfresco.content.data.AnalyticsManager +import com.alfresco.content.data.PageView import com.alfresco.content.listview.tasks.TaskListFragment /** @@ -19,4 +21,9 @@ class TasksFragment : TaskListFragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) } + + override fun onResume() { + super.onResume() + AnalyticsManager().screenViewEvent(PageView.Tasks) + } } diff --git a/browse/src/main/kotlin/com/alfresco/content/browse/tasks/TasksViewModel.kt b/browse/src/main/kotlin/com/alfresco/content/browse/tasks/TasksViewModel.kt index ad4e290f3..dc3f786c7 100644 --- a/browse/src/main/kotlin/com/alfresco/content/browse/tasks/TasksViewModel.kt +++ b/browse/src/main/kotlin/com/alfresco/content/browse/tasks/TasksViewModel.kt @@ -27,6 +27,11 @@ class TasksViewModel( init { fetchInitial() + withState { + if (it.sortOrder == TasksViewState.SortOrder.ByModifiedDate) { + TasksViewState.ModifiedGroup.prepare(context) + } + } } override fun refresh() = fetchInitial() diff --git a/browse/src/main/kotlin/com/alfresco/content/browse/tasks/TasksViewState.kt b/browse/src/main/kotlin/com/alfresco/content/browse/tasks/TasksViewState.kt index 3c232d5d7..15bd1a2f3 100644 --- a/browse/src/main/kotlin/com/alfresco/content/browse/tasks/TasksViewState.kt +++ b/browse/src/main/kotlin/com/alfresco/content/browse/tasks/TasksViewState.kt @@ -1,13 +1,17 @@ package com.alfresco.content.browse.tasks +import android.content.Context import com.airbnb.mvrx.Async import com.airbnb.mvrx.Uninitialized +import com.alfresco.content.browse.R import com.alfresco.content.data.Entry import com.alfresco.content.data.ResponseList import com.alfresco.content.data.TaskEntry import com.alfresco.content.data.Tasks import com.alfresco.content.data.Tasks.Active import com.alfresco.content.listview.tasks.TaskListViewState +import java.time.ZonedDateTime +import java.time.temporal.ChronoField /** * Marked as TasksViewState class @@ -17,6 +21,7 @@ data class TasksViewState( override val taskEntries: List = emptyList(), override val hasMoreItems: Boolean = false, override val request: Async = Uninitialized, + val baseTaskEntries: List = emptyList(), val displayTask: Tasks = Active, val loadItemsCount: Int = 0, val page: Int = 0 @@ -40,16 +45,114 @@ data class TasksViewState( val newTaskEntries = if (response.start != 0) { totalLoadCount = loadItemsCount.plus(response.size) - taskEntries + taskPageEntries + baseTaskEntries + taskPageEntries } else { totalLoadCount = response.size taskPageEntries } - return copy( - taskEntries = newTaskEntries, + return copyUpdatingEntries(newTaskEntries).copy( + baseTaskEntries = taskPageEntries, loadItemsCount = totalLoadCount, hasMoreItems = totalLoadCount < response.total ) } + + private fun copyUpdatingEntries(newEntries: List) = + when (sortOrder) { + SortOrder.ByModifiedDate -> groupByModifiedDateReducer(newEntries) + else -> defaultReducer(newEntries) + } + + /** + * set sort order depends on the different views + */ + val sortOrder: SortOrder + get() = SortOrder.ByModifiedDate + + private fun defaultReducer(newEntries: List): TasksViewState = + copy( + taskEntries = newEntries + ) + + private fun groupByModifiedDateReducer(newEntries: List): TasksViewState { + val now = ZonedDateTime.now() + val startOfDay = now.toLocalDate().atStartOfDay(now.zone) + val startOfYesterday = startOfDay.minusDays(1) + val firstOfWeek = startOfDay.with(ChronoField.DAY_OF_WEEK, 1) + val firstOfLastWeek = firstOfWeek.minusWeeks(1) + + var currentGroup = ModifiedGroup.None + val groupedList = mutableListOf() + for (entry in newEntries) { + val modified = entry.created ?: startOfDay + + val targetGroup = when { + modified >= startOfDay -> ModifiedGroup.Today + modified >= startOfYesterday -> ModifiedGroup.Yesterday + modified >= firstOfWeek -> ModifiedGroup.ThisWeek + modified >= firstOfLastWeek -> ModifiedGroup.LastWeek + else -> ModifiedGroup.Older + } + + if (currentGroup != targetGroup) { + currentGroup = targetGroup + + groupedList.add( + TaskEntry( + id = currentGroup.title(), + type = TaskEntry.Type.GROUP, + name = currentGroup.title() + ) + ) + } + groupedList.add(entry) + } + + return copy( + taskEntries = groupedList + ) + } + + /** + * Marked as ModifiedGroup enum class + */ + enum class ModifiedGroup { + Today, + Yesterday, + ThisWeek, + LastWeek, + Older, + None; + + /** + * set default value for title + */ + fun title(): String { + return valueMap[this] ?: "" + } + + companion object { + private val valueMap = HashMap() + + /** + * fetching the sort title values from string.xml + */ + fun prepare(context: Context) { + valueMap[Today] = context.getString(R.string.modified_group_today) + valueMap[Yesterday] = context.getString(R.string.modified_group_yesterday) + valueMap[ThisWeek] = context.getString(R.string.modified_group_this_week) + valueMap[LastWeek] = context.getString(R.string.modified_group_last_week) + valueMap[Older] = context.getString(R.string.modified_group_older) + } + } + } + + /** + * Marked as SortOrder enum class + */ + enum class SortOrder { + ByModifiedDate, + Default + } } diff --git a/data/src/main/kotlin/com/alfresco/content/data/AnalyticsManager.kt b/data/src/main/kotlin/com/alfresco/content/data/AnalyticsManager.kt index 46eaff116..f59a904c3 100644 --- a/data/src/main/kotlin/com/alfresco/content/data/AnalyticsManager.kt +++ b/data/src/main/kotlin/com/alfresco/content/data/AnalyticsManager.kt @@ -18,8 +18,8 @@ class AnalyticsManager(val session: Session = SessionManager.requireSession) { params.putString(Parameters.FileMimeType.value, fileMimeType) params.putString(Parameters.FileExtension.value, fileExtension) params.putBoolean(Parameters.Success.value, success) - params.putString(Parameters.EventName.value, EventName.FilePreview.value) - repository.logEvent(EventType.ActionEvent, params) + params.putString(Parameters.EventName.value, EventName.FilePreview.value.lowercase()) + repository.logEvent(EventName.FilePreview.value.lowercase(), params) } /** @@ -29,8 +29,8 @@ class AnalyticsManager(val session: Session = SessionManager.requireSession) { val params = repository.defaultParams() params.putString(Parameters.FileMimeType.value, fileMimeType) params.putString(Parameters.FileExtension.value, fileExtension) - params.putString(Parameters.EventName.value, eventName.value) - repository.logEvent(EventType.ActionEvent, params) + params.putString(Parameters.EventName.value, eventName.value.lowercase()) + repository.logEvent(eventName.value.lowercase(), params) } /** @@ -39,8 +39,8 @@ class AnalyticsManager(val session: Session = SessionManager.requireSession) { fun theme(name: String) { val params = repository.defaultParams() params.putString(Parameters.ThemeName.value, name) - params.putString(Parameters.EventName.value, EventName.ChangeTheme.value) - repository.logEvent(EventType.ActionEvent, params) + params.putString(Parameters.EventName.value, EventName.ChangeTheme.value.lowercase()) + repository.logEvent(EventName.ChangeTheme.value.lowercase(), params) } /** @@ -48,8 +48,8 @@ class AnalyticsManager(val session: Session = SessionManager.requireSession) { */ fun appLaunch() { val params = repository.defaultParams() - params.putString(Parameters.EventName.value, EventName.AppLaunched.value) - repository.logEvent(EventType.ActionEvent, params) + params.putString(Parameters.EventName.value, EventName.AppLaunched.value.lowercase()) + repository.logEvent(EventName.AppLaunched.value.lowercase(), params) } /** @@ -58,8 +58,10 @@ class AnalyticsManager(val session: Session = SessionManager.requireSession) { fun searchFacets(name: String) { val params = repository.defaultParams() params.putString(Parameters.FacetName.value, name) + params.putString(Parameters.EventName.value, EventName.SearchFacets.value.lowercase()) + repository.logEvent(EventName.SearchFacets.value.lowercase(), params) params.putString(Parameters.EventName.value, EventName.SearchFacets.value) - repository.logEvent(EventType.ActionEvent, params) + repository.logEvent(EventName.SearchFacets.value, params) } /** @@ -68,23 +70,20 @@ class AnalyticsManager(val session: Session = SessionManager.requireSession) { fun discardCapture() { val params = repository.defaultParams() params.putString(Parameters.EventName.value, EventName.DISCARD_CAPTURE.value) - repository.logEvent(EventType.ActionEvent, params) + repository.logEvent(EventName.DISCARD_CAPTURE.value, params) } /** * analytics for multiple screen view */ - fun screenViewEvent(pageViewName: PageView, noOfFiles: Int = -1, folderName: String = "") { + fun screenViewEvent(pageViewName: PageView, noOfFiles: Int = -1) { val params = repository.defaultParams() if (noOfFiles > -1) params.putInt(Parameters.NumberOfFiles.value, noOfFiles) - if (pageViewName == PageView.FolderName) - params.putString(Parameters.EventName.value, folderName) - else - params.putString(Parameters.EventName.value, pageViewName.value) - repository.logEvent(EventType.ScreenView, params) + params.putString(Parameters.EventName.value, pageViewName.value.lowercase()) + repository.logEvent(pageViewName.value.lowercase(), params) } /** @@ -92,10 +91,10 @@ class AnalyticsManager(val session: Session = SessionManager.requireSession) { */ fun apiTracker(apiName: APIEvent, status: Boolean = false, size: String = "") { val params = repository.defaultParams() - params.putString(Parameters.EventName.value, apiName.value) + params.putString(Parameters.EventName.value, apiName.value.lowercase()) params.putBoolean(Parameters.Success.value, status) if (size.isNotEmpty()) params.putString(Parameters.FileSize.value, size) - repository.logEvent(EventType.ApiTracker, params) + repository.logEvent(apiName.value.lowercase(), params) } } diff --git a/data/src/main/kotlin/com/alfresco/content/data/AnalyticsRepository.kt b/data/src/main/kotlin/com/alfresco/content/data/AnalyticsRepository.kt index 7ac0d5805..2849d1e0f 100644 --- a/data/src/main/kotlin/com/alfresco/content/data/AnalyticsRepository.kt +++ b/data/src/main/kotlin/com/alfresco/content/data/AnalyticsRepository.kt @@ -33,9 +33,9 @@ class AnalyticsRepository(val session: Session) { } private fun deviceOS() = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { - "Android: ${Build.VERSION.RELEASE_OR_CODENAME}" + "android: ${Build.VERSION.RELEASE_OR_CODENAME}" } else { - "Android: ${Build.VERSION.RELEASE}" + "android: ${Build.VERSION.RELEASE}" } private fun deviceNetwork(): String { @@ -44,9 +44,9 @@ class AnalyticsRepository(val session: Session) { val actNw = cm.getNetworkCapabilities(nw) ?: return NetworkStatus.NOT_CONNECTED.name return when { - actNw.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) -> NetworkStatus.WIFI.name - actNw.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) -> NetworkStatus.CELLULAR.name - else -> NetworkStatus.NOT_CONNECTED.name + actNw.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) -> NetworkStatus.WIFI.name.lowercase() + actNw.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) -> NetworkStatus.CELLULAR.name.lowercase() + else -> NetworkStatus.NOT_CONNECTED.name.lowercase() } } @@ -73,9 +73,8 @@ class AnalyticsRepository(val session: Session) { /** * It will get triggered to log analytics on firebase console */ - fun logEvent(type: EventType, params: Bundle) { - - firebaseAnalytics.logEvent(type.value, params) + fun logEvent(type: String, params: Bundle) { + firebaseAnalytics.logEvent(type, params) } } @@ -83,45 +82,45 @@ class AnalyticsRepository(val session: Session) { * Marked as EventName enum */ enum class EventName(val value: String) { - FilePreview("Event_filePreview"), - Download("Event_download"), - AddFavorite("Event_Add to Favorites"), - RemoveFavorite("Event_Remove from Favorite"), - RenameNode("Event_Rename"), - MoveToFolder("Event_Move"), - MarkOffline("Event_Make available offline"), - RemoveOffline("Event_Remove offline"), - MoveTrash("Event_Move to Trash"), - ChangeTheme("Event_changeTheme"), - CreateFolder("Event_New folder"), - UploadMedia("Event_Upload photos or videos"), - CreateMedia("Event_Take a photo or video"), - UploadFiles("Event_Upload files"), - AppLaunched("Event_appLaunched"), - SearchFacets("Event_searchFacets"), - PermanentlyDelete("Event_Permanently Delete"), - Restore("Event_Restore"), - OpenWith("Event_Open with"), - SCAN_DOCUMENTS("Event_Scan documents"), - DISCARD_CAPTURE("Event_discardCapture") + FilePreview("event_file_preview"), + Download("event_download"), + AddFavorite("event_add_to_favorites"), + RemoveFavorite("event_remove_from_favorite"), + RenameNode("event_rename"), + MoveToFolder("event_move"), + MarkOffline("event_Make_available_offline"), + RemoveOffline("event_remove_offline"), + MoveTrash("event_move_to_trash"), + ChangeTheme("event_change_theme"), + CreateFolder("event_new_folder"), + UploadMedia("event_upload_photos_or_videos"), + CreateMedia("event_take_a_photo_or_video"), + UploadFiles("event_upload_files"), + AppLaunched("event_app_launched"), + SearchFacets("event_search_facets"), + PermanentlyDelete("event_permanently_delete"), + Restore("event_restore"), + OpenWith("event_open_with"), + SCAN_DOCUMENTS("event_scan_documents"), + DISCARD_CAPTURE("event_discard_capture") } /** * Marked as PageView enum */ enum class PageView(val value: String) { - Recent("PageView_Recent"), - Favorites("PageView_Favorites"), - Offline("PageView_Offline"), - Browse("PageView_Browse"), - PersonalFiles("PageView_Personal files"), - FolderName("PageView_FolderName"), - MyLibraries("PageView_My libraries"), - Shared("PageView_Shared"), - Trash("PageView_Trash"), - Search("PageView_Search"), - ShareExtension("PageView_ShareExtension"), - Transfers("PageView_Transfers"), + Recent("page_view_recent"), + Favorites("page_view_favorites"), + Tasks("page_view_tasks"), + Offline("page_view_offline"), + Browse("page_view_browse"), + PersonalFiles("page_view_personal_files"), + MyLibraries("page_view_my_libraries"), + Shared("page_view_shared"), + Trash("page_view_trash"), + Search("page_view_search"), + ShareExtension("page_view_share_extension"), + Transfers("page_view_transfers"), None("none") } @@ -129,18 +128,9 @@ enum class PageView(val value: String) { * Marked as APIEvent enum */ enum class APIEvent(val value: String) { - NewFolder("Event_API_NewFolder"), - UploadFiles("Event_API_UploadFiles"), - Login("Event_API_Login") -} - -/** - * Marked as EventType enum - */ -enum class EventType(val value: String) { - ScreenView("screen_views"), - ActionEvent("action_event"), - ApiTracker("api_tracker") + NewFolder("event_api_new_folder"), + UploadFiles("event_api_upload_files"), + Login("event_api_login") } /** @@ -172,11 +162,8 @@ enum class Parameters(val value: String) { FileMimeType("file_mimetype"), FileExtension("file_extension"), FileSize("file_size"), - FolderName("folder_name"), ThemeName("theme_name"), NumberOfFiles("number_of_files"), FacetName("facet_name"), - NumberOfAssets("number_of_assets"), - IsFile("is_file"), Success("success") } diff --git a/data/src/main/kotlin/com/alfresco/content/data/TaskEntry.kt b/data/src/main/kotlin/com/alfresco/content/data/TaskEntry.kt index d49a8dd7f..1fc6bd7e1 100644 --- a/data/src/main/kotlin/com/alfresco/content/data/TaskEntry.kt +++ b/data/src/main/kotlin/com/alfresco/content/data/TaskEntry.kt @@ -3,6 +3,7 @@ package com.alfresco.content.data import android.os.Parcelable import com.alfresco.process.models.AssigneeInfo import com.alfresco.process.models.TaskDataEntry +import java.time.ZonedDateTime import kotlinx.parcelize.Parcelize /** @@ -12,8 +13,10 @@ import kotlinx.parcelize.Parcelize data class TaskEntry( val id: String = "", val name: String = "", - val assignee: Assignee, - val priority: String = "" + val assignee: Assignee? = null, + val priority: String = "", + val created: ZonedDateTime? = null, + val type: Type = Type.UNKNOWN ) : Parcelable { companion object { @@ -29,6 +32,14 @@ data class TaskEntry( ) } } + + /** + * Marked as Type enum class + */ + enum class Type { + GROUP, + UNKNOWN + } } /** diff --git a/listview/src/main/kotlin/com/alfresco/content/listview/tasks/ListViewTaskRow.kt b/listview/src/main/kotlin/com/alfresco/content/listview/tasks/ListViewTaskRow.kt index 390fd9b26..e8455247e 100644 --- a/listview/src/main/kotlin/com/alfresco/content/listview/tasks/ListViewTaskRow.kt +++ b/listview/src/main/kotlin/com/alfresco/content/listview/tasks/ListViewTaskRow.kt @@ -32,7 +32,7 @@ class ListViewTaskRow @JvmOverloads constructor( @ModelProp fun setData(entry: TaskEntry) { binding.title.text = entry.name - binding.subtitle.text = entry.assignee.name + binding.subtitle.text = entry.assignee?.name updatePriority(entry.priority) } diff --git a/listview/src/main/kotlin/com/alfresco/content/listview/tasks/TaskListFragment.kt b/listview/src/main/kotlin/com/alfresco/content/listview/tasks/TaskListFragment.kt index b041f03a7..5fe21bd9b 100644 --- a/listview/src/main/kotlin/com/alfresco/content/listview/tasks/TaskListFragment.kt +++ b/listview/src/main/kotlin/com/alfresco/content/listview/tasks/TaskListFragment.kt @@ -21,6 +21,7 @@ import com.airbnb.mvrx.withState import com.alfresco.content.data.ResponseList import com.alfresco.content.data.TaskEntry import com.alfresco.content.listview.R +import com.alfresco.content.listview.listViewGroupHeader import com.alfresco.content.listview.listViewMessage import com.alfresco.content.listview.listViewPageBoundary import com.alfresco.content.listview.listViewPageLoading @@ -123,10 +124,17 @@ abstract class TaskListFragment, S : TaskListViewState } } else if (state.taskEntries.isNotEmpty()) { state.taskEntries.forEach { - listViewTaskRow { - id(it.id) - data(it) - compact(state.isCompact) + if (it.type == TaskEntry.Type.GROUP) { + listViewGroupHeader { + id(it.name) + title(it.name) + } + } else { + listViewTaskRow { + id(it.id) + data(it) + compact(state.isCompact) + } } } }