Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add troubleshooting tool: location history #3886

Merged
merged 7 commits into from
Sep 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,8 @@ dependencies {
implementation(libs.iconics.compose)
implementation(libs.community.material.typeface)

implementation(libs.bundles.paging)

implementation(libs.reorderable)
implementation(libs.changeLog)

Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@ import androidx.fragment.app.commit
import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
import dagger.hilt.android.AndroidEntryPoint
import io.homeassistant.companion.android.BuildConfig
import io.homeassistant.companion.android.R
import io.homeassistant.companion.android.common.data.servers.ServerManager
import io.homeassistant.companion.android.settings.developer.location.LocationTrackingFragment
import io.homeassistant.companion.android.settings.log.LogFragment
import io.homeassistant.companion.android.settings.server.ServerChooserFragment
import javax.inject.Inject
Expand Down Expand Up @@ -45,6 +47,17 @@ class DeveloperSettingsFragment : DeveloperSettingsView, PreferenceFragmentCompa
return@setOnPreferenceClickListener true
}

findPreference<Preference>("location_tracking")?.let {
it.isVisible = BuildConfig.FLAVOR == "full"
it.setOnPreferenceClickListener {
parentFragmentManager.commit {
replace(R.id.content, LocationTrackingFragment::class.java, null)
addToBackStack(getString(io.homeassistant.companion.android.common.R.string.location_tracking))
}
return@setOnPreferenceClickListener true
}
}

findPreference<Preference>("thread_debug")?.let {
it.isVisible = presenter.appSupportsThread()
it.setOnPreferenceClickListener {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package io.homeassistant.companion.android.settings.developer.location

import android.content.Intent
import android.os.Bundle
import android.view.LayoutInflater
import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import androidx.compose.ui.platform.ComposeView
import androidx.core.net.toUri
import androidx.core.view.MenuHost
import androidx.core.view.MenuProvider
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import androidx.lifecycle.Lifecycle
import com.google.accompanist.themeadapter.material.MdcTheme
import dagger.hilt.android.AndroidEntryPoint
import io.homeassistant.companion.android.R
import io.homeassistant.companion.android.common.data.servers.ServerManager
import io.homeassistant.companion.android.settings.developer.location.views.LocationTrackingView
import javax.inject.Inject
import io.homeassistant.companion.android.common.R as commonR

@AndroidEntryPoint
class LocationTrackingFragment : Fragment() {

@Inject
lateinit var serverManager: ServerManager

val viewModel: LocationTrackingViewModel by viewModels()

override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
return ComposeView(requireContext()).apply {
setContent {
MdcTheme {
LocationTrackingView(
useHistory = viewModel.historyEnabled,
onSetHistory = viewModel::enableHistory,
history = viewModel.historyPagerFlow,
serversList = serverManager.defaultServers
)
}
}
}
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)

val menuHost: MenuHost = requireActivity()
menuHost.addMenuProvider(
object : MenuProvider {
override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
menuInflater.inflate(R.menu.menu_fragment_locationtracking, menu)
}

override fun onPrepareMenu(menu: Menu) {
menu.findItem(R.id.get_help).apply {
intent = Intent(Intent.ACTION_VIEW, "https://companion.home-assistant.io/docs/troubleshooting/faqs#device-tracker-is-not-updating-in-android-app".toUri())
}
}

override fun onMenuItemSelected(menuItem: MenuItem): Boolean = when (menuItem.itemId) {
R.id.history_all, R.id.history_sent, R.id.history_skipped, R.id.history_failed -> {
viewModel.setHistoryFilter(menuItem.itemId)
menuItem.isChecked = true
true
}
else -> false
}
},
viewLifecycleOwner,
Lifecycle.State.RESUMED
)
}

override fun onResume() {
super.onResume()
activity?.title = getString(commonR.string.location_tracking)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package io.homeassistant.companion.android.settings.developer.location

import android.app.Application
import android.util.Log
import androidx.annotation.IdRes
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.viewModelScope
import androidx.paging.Pager
import androidx.paging.PagingConfig
import dagger.hilt.android.lifecycle.HiltViewModel
import io.homeassistant.companion.android.R
import io.homeassistant.companion.android.common.data.prefs.PrefsRepository
import io.homeassistant.companion.android.database.location.LocationHistoryDao
import io.homeassistant.companion.android.database.location.LocationHistoryItemResult
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.launch
import javax.inject.Inject

@HiltViewModel
class LocationTrackingViewModel @Inject constructor(
private val locationHistoryDao: LocationHistoryDao,
private val prefsRepository: PrefsRepository,
application: Application
) : AndroidViewModel(application) {

companion object {
private const val TAG = "LocationTrackingViewMod"

private const val PAGE_SIZE = 25
}

enum class HistoryFilter(@IdRes val menuItemId: Int) {
ALL(R.id.history_all),
SENT(R.id.history_sent),
SKIPPED(R.id.history_skipped),
FAILED(R.id.history_failed);

companion object {
val menuItemIdToFilter = values().associateBy { it.menuItemId }
}
}

var historyEnabled by mutableStateOf(false)
private set

private val historyResultFilter = MutableStateFlow(HistoryFilter.ALL)

@OptIn(ExperimentalCoroutinesApi::class)
val historyPagerFlow = historyResultFilter.flatMapLatest { filter ->
Pager(PagingConfig(pageSize = PAGE_SIZE, maxSize = PAGE_SIZE * 6)) {
Log.d(TAG, "Returning PagingSource for history filter: $filter")
when (filter) {
HistoryFilter.SENT ->
locationHistoryDao.getAll(listOf(LocationHistoryItemResult.SENT.name))
HistoryFilter.SKIPPED -> locationHistoryDao.getAll(
(LocationHistoryItemResult.values().toMutableList() - LocationHistoryItemResult.SENT - LocationHistoryItemResult.FAILED_SEND)
.map { it.name }
)
HistoryFilter.FAILED ->
locationHistoryDao.getAll(listOf(LocationHistoryItemResult.FAILED_SEND.name))
else -> locationHistoryDao.getAll()
}
}.flow
}

init {
viewModelScope.launch {
historyEnabled = prefsRepository.isLocationHistoryEnabled()
}
}

fun enableHistory(enabled: Boolean) {
if (enabled == historyEnabled) return
historyEnabled = enabled
viewModelScope.launch {
prefsRepository.setLocationHistoryEnabled(enabled)
if (!enabled) locationHistoryDao.deleteAll()
}
}

fun setHistoryFilter(@IdRes filterMenuItemId: Int) {
historyResultFilter.value = HistoryFilter.menuItemIdToFilter.getValue(filterMenuItemId)
}
}
Loading