blob: 5520c62f10a826bfe654388f45e08ecf53a3d3df [file] [log] [blame]
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.healthconnect.controller.recentaccess
import android.content.Intent
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.FrameLayout
import androidx.core.os.bundleOf
import androidx.core.view.isVisible
import androidx.fragment.app.viewModels
import androidx.navigation.fragment.findNavController
import androidx.preference.Preference
import androidx.preference.PreferenceGroup
import androidx.recyclerview.widget.RecyclerView
import com.android.healthconnect.controller.R
import com.android.healthconnect.controller.permissions.shared.Constants
import com.android.healthconnect.controller.recentaccess.RecentAccessViewModel.RecentAccessState
import com.android.healthconnect.controller.shared.preference.HealthPreferenceFragment
import com.android.healthconnect.controller.utils.logging.HealthConnectLogger
import com.android.healthconnect.controller.utils.logging.PageName
import com.android.healthconnect.controller.utils.logging.RecentAccessElement
import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject
/** Recent access fragment showing a timeline of apps that have recently accessed Health Connect. */
@AndroidEntryPoint(HealthPreferenceFragment::class)
class RecentAccessFragment : Hilt_RecentAccessFragment() {
companion object {
private const val RECENT_ACCESS_TODAY_KEY = "recent_access_today"
private const val RECENT_ACCESS_YESTERDAY_KEY = "recent_access_yesterday"
private const val RECENT_ACCESS_NO_DATA_KEY = "no_data"
}
init {
this.setPageName(PageName.RECENT_ACCESS_PAGE)
}
@Inject lateinit var logger: HealthConnectLogger
private val viewModel: RecentAccessViewModel by viewModels()
private lateinit var contentParent: FrameLayout
private lateinit var fab: ExtendedFloatingActionButton
private var recyclerView: RecyclerView? = null
private val mRecentAccessTodayPreferenceGroup: PreferenceGroup? by lazy {
preferenceScreen.findPreference(RECENT_ACCESS_TODAY_KEY)
}
private val mRecentAccessYesterdayPreferenceGroup: PreferenceGroup? by lazy {
preferenceScreen.findPreference(RECENT_ACCESS_YESTERDAY_KEY)
}
private val mRecentAccessNoDataPreference: Preference? by lazy {
preferenceScreen.findPreference(RECENT_ACCESS_NO_DATA_KEY)
}
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
super.onCreatePreferences(savedInstanceState, rootKey)
setPreferencesFromResource(R.xml.recent_access_preference_screen, rootKey)
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val rootView = super.onCreateView(inflater, container, savedInstanceState)
contentParent = requireActivity().findViewById(android.R.id.content)
inflater.inflate(R.layout.widget_floating_action_button, contentParent)
fab = contentParent.findViewById(R.id.extended_fab)
fab.isVisible = true
recyclerView = rootView?.findViewById(R.id.recycler_view)
val bottomPadding =
resources.getDimensionPixelSize(R.dimen.recent_access_fab_bottom_padding)
recyclerView?.setPadding(0, 0, 0, bottomPadding)
return rootView
}
override fun onPause() {
// Prevents FAB from being permanently attached to the activity layout
contentParent.removeView(fab)
super.onPause()
}
override fun onResume() {
super.onResume()
viewModel.loadRecentAccessApps()
if (fab.parent == null) {
contentParent.addView(fab)
}
logger.logImpression(RecentAccessElement.MANAGE_PERMISSIONS_FAB)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewModel.loadRecentAccessApps()
viewModel.recentAccessApps.observe(viewLifecycleOwner) { state ->
when (state) {
is RecentAccessState.Loading -> {
setLoading(true)
}
is RecentAccessState.Error -> {
setError(true)
}
is RecentAccessState.WithData -> {
setLoading(false)
updateRecentApps(state.recentAccessEntries)
}
}
}
}
private fun updateRecentApps(recentAppsList: List<RecentAccessEntry>) {
mRecentAccessTodayPreferenceGroup?.removeAll()
mRecentAccessYesterdayPreferenceGroup?.removeAll()
mRecentAccessNoDataPreference?.isVisible = false
if (recentAppsList.isEmpty()) {
mRecentAccessYesterdayPreferenceGroup?.isVisible = false
mRecentAccessTodayPreferenceGroup?.isVisible = false
mRecentAccessNoDataPreference?.isVisible = true
fab.isVisible = false
} else {
// if the first entry is yesterday, we don't need the 'Today' section
mRecentAccessTodayPreferenceGroup?.isVisible = recentAppsList[0].isToday
// if the last entry is today, we don't need the 'Yesterday' section
mRecentAccessYesterdayPreferenceGroup?.isVisible = !recentAppsList.last().isToday
fab.setOnClickListener {
logger.logInteraction(RecentAccessElement.MANAGE_PERMISSIONS_FAB)
findNavController()
.navigate(R.id.action_recentAccessFragment_to_connectedAppsFragment)
}
recentAppsList.forEachIndexed { index, recentApp ->
val isLastUsage =
(index == recentAppsList.size - 1) ||
(recentApp.isToday &&
index < recentAppsList.size - 1 &&
!recentAppsList[index + 1].isToday)
val newPreference =
RecentAccessPreference(requireContext(), recentApp, true).also {
if (!recentApp.isInactive) {
// Do not set click listeners for inactive apps
it.setOnPreferenceClickListener {
findNavController()
.navigate(
R.id.action_recentAccessFragment_to_connectedAppFragment,
bundleOf(
Intent.EXTRA_PACKAGE_NAME to
recentApp.metadata.packageName,
Constants.EXTRA_APP_NAME to recentApp.metadata.appName))
true
}
}
}
if (recentApp.isToday) {
mRecentAccessTodayPreferenceGroup?.addPreference(newPreference)
if (!isLastUsage) {
mRecentAccessTodayPreferenceGroup?.addPreference(
DividerPreference(requireContext()))
}
} else {
mRecentAccessYesterdayPreferenceGroup?.addPreference(newPreference)
if (!isLastUsage) {
mRecentAccessYesterdayPreferenceGroup?.addPreference(
DividerPreference(requireContext()))
}
}
}
}
}
}