1 /* <lambda>null2 * Copyright (C) 2022 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.healthconnect.controller.recentaccess 18 19 import android.content.Intent 20 import android.os.Bundle 21 import android.view.LayoutInflater 22 import android.view.View 23 import android.view.ViewGroup 24 import android.widget.FrameLayout 25 import androidx.core.os.bundleOf 26 import androidx.core.view.isVisible 27 import androidx.fragment.app.viewModels 28 import androidx.navigation.fragment.findNavController 29 import androidx.preference.Preference 30 import androidx.preference.PreferenceGroup 31 import androidx.recyclerview.widget.RecyclerView 32 import com.android.healthconnect.controller.R 33 import com.android.healthconnect.controller.permissions.shared.Constants 34 import com.android.healthconnect.controller.recentaccess.RecentAccessViewModel.RecentAccessState 35 import com.android.healthconnect.controller.shared.preference.HealthPreferenceFragment 36 import com.android.healthconnect.controller.utils.logging.HealthConnectLogger 37 import com.android.healthconnect.controller.utils.logging.PageName 38 import com.android.healthconnect.controller.utils.logging.RecentAccessElement 39 import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton 40 import dagger.hilt.android.AndroidEntryPoint 41 import javax.inject.Inject 42 43 /** Recent access fragment showing a timeline of apps that have recently accessed Health Connect. */ 44 @AndroidEntryPoint(HealthPreferenceFragment::class) 45 class RecentAccessFragment : Hilt_RecentAccessFragment() { 46 47 companion object { 48 private const val RECENT_ACCESS_TODAY_KEY = "recent_access_today" 49 private const val RECENT_ACCESS_YESTERDAY_KEY = "recent_access_yesterday" 50 private const val RECENT_ACCESS_NO_DATA_KEY = "no_data" 51 } 52 53 init { 54 this.setPageName(PageName.RECENT_ACCESS_PAGE) 55 } 56 57 @Inject lateinit var logger: HealthConnectLogger 58 59 private val viewModel: RecentAccessViewModel by viewModels() 60 private lateinit var contentParent: FrameLayout 61 private lateinit var fab: ExtendedFloatingActionButton 62 private var recyclerView: RecyclerView? = null 63 64 private val mRecentAccessTodayPreferenceGroup: PreferenceGroup? by lazy { 65 preferenceScreen.findPreference(RECENT_ACCESS_TODAY_KEY) 66 } 67 68 private val mRecentAccessYesterdayPreferenceGroup: PreferenceGroup? by lazy { 69 preferenceScreen.findPreference(RECENT_ACCESS_YESTERDAY_KEY) 70 } 71 72 private val mRecentAccessNoDataPreference: Preference? by lazy { 73 preferenceScreen.findPreference(RECENT_ACCESS_NO_DATA_KEY) 74 } 75 76 override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { 77 super.onCreatePreferences(savedInstanceState, rootKey) 78 setPreferencesFromResource(R.xml.recent_access_preference_screen, rootKey) 79 } 80 81 override fun onCreateView( 82 inflater: LayoutInflater, 83 container: ViewGroup?, 84 savedInstanceState: Bundle? 85 ): View? { 86 val rootView = super.onCreateView(inflater, container, savedInstanceState) 87 88 contentParent = requireActivity().findViewById(android.R.id.content) 89 inflater.inflate(R.layout.widget_floating_action_button, contentParent) 90 91 fab = contentParent.findViewById(R.id.extended_fab) 92 fab.isVisible = true 93 94 recyclerView = rootView?.findViewById(R.id.recycler_view) 95 val bottomPadding = 96 resources.getDimensionPixelSize(R.dimen.recent_access_fab_bottom_padding) 97 recyclerView?.setPadding(0, 0, 0, bottomPadding) 98 99 return rootView 100 } 101 102 override fun onPause() { 103 // Prevents FAB from being permanently attached to the activity layout 104 contentParent.removeView(fab) 105 super.onPause() 106 } 107 108 override fun onResume() { 109 super.onResume() 110 viewModel.loadRecentAccessApps() 111 112 if (fab.parent == null) { 113 contentParent.addView(fab) 114 } 115 logger.logImpression(RecentAccessElement.MANAGE_PERMISSIONS_FAB) 116 } 117 118 override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 119 super.onViewCreated(view, savedInstanceState) 120 121 viewModel.loadRecentAccessApps() 122 viewModel.recentAccessApps.observe(viewLifecycleOwner) { state -> 123 when (state) { 124 is RecentAccessState.Loading -> { 125 setLoading(true) 126 } 127 is RecentAccessState.Error -> { 128 setError(true) 129 } 130 is RecentAccessState.WithData -> { 131 setLoading(false) 132 updateRecentApps(state.recentAccessEntries) 133 } 134 } 135 } 136 } 137 138 private fun updateRecentApps(recentAppsList: List<RecentAccessEntry>) { 139 mRecentAccessTodayPreferenceGroup?.removeAll() 140 mRecentAccessYesterdayPreferenceGroup?.removeAll() 141 mRecentAccessNoDataPreference?.isVisible = false 142 143 if (recentAppsList.isEmpty()) { 144 mRecentAccessYesterdayPreferenceGroup?.isVisible = false 145 mRecentAccessTodayPreferenceGroup?.isVisible = false 146 mRecentAccessNoDataPreference?.isVisible = true 147 fab.isVisible = false 148 } else { 149 // if the first entry is yesterday, we don't need the 'Today' section 150 mRecentAccessTodayPreferenceGroup?.isVisible = recentAppsList[0].isToday 151 152 // if the last entry is today, we don't need the 'Yesterday' section 153 mRecentAccessYesterdayPreferenceGroup?.isVisible = !recentAppsList.last().isToday 154 155 fab.setOnClickListener { 156 logger.logInteraction(RecentAccessElement.MANAGE_PERMISSIONS_FAB) 157 findNavController() 158 .navigate(R.id.action_recentAccessFragment_to_connectedAppsFragment) 159 } 160 161 recentAppsList.forEachIndexed { index, recentApp -> 162 val isLastUsage = 163 (index == recentAppsList.size - 1) || 164 (recentApp.isToday && 165 index < recentAppsList.size - 1 && 166 !recentAppsList[index + 1].isToday) 167 val newPreference = 168 RecentAccessPreference(requireContext(), recentApp, true).also { 169 if (!recentApp.isInactive) { 170 // Do not set click listeners for inactive apps 171 it.setOnPreferenceClickListener { 172 findNavController() 173 .navigate( 174 R.id.action_recentAccessFragment_to_connectedAppFragment, 175 bundleOf( 176 Intent.EXTRA_PACKAGE_NAME to 177 recentApp.metadata.packageName, 178 Constants.EXTRA_APP_NAME to recentApp.metadata.appName)) 179 true 180 } 181 } 182 } 183 184 if (recentApp.isToday) { 185 mRecentAccessTodayPreferenceGroup?.addPreference(newPreference) 186 if (!isLastUsage) { 187 mRecentAccessTodayPreferenceGroup?.addPreference( 188 DividerPreference(requireContext())) 189 } 190 } else { 191 mRecentAccessYesterdayPreferenceGroup?.addPreference(newPreference) 192 if (!isLastUsage) { 193 mRecentAccessYesterdayPreferenceGroup?.addPreference( 194 DividerPreference(requireContext())) 195 } 196 } 197 } 198 } 199 } 200 } 201