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

Show file size on offline areas list, fix styling #1961

Merged
merged 42 commits into from
Oct 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
9fa5454
Minor refactor
gino-m Sep 20, 2023
e9f939a
WIP: Get size of offline imagery on disk
gino-m Sep 20, 2023
ae86b79
Refactor tile download path builder
gino-m Sep 21, 2023
ec0c506
Calculate size on disk
gino-m Sep 21, 2023
b9ca610
Calculate size on disk
gino-m Sep 26, 2023
ef5250d
Merge branch 'master' of https://github.com/google/ground-android int…
gino-m Sep 26, 2023
3724fd7
Merge branch 'master' of https://github.com/google/ground-android int…
gino-m Oct 5, 2023
5c8fa31
Implement delete offline maps
gino-m Oct 5, 2023
79ec92a
Reduce Maps SDK leaky abstractions
gino-m Oct 5, 2023
5ffab04
Consistent naming
gino-m Oct 5, 2023
659baec
Simplify
gino-m Oct 5, 2023
9970f06
Save downloaded zoom range
gino-m Oct 5, 2023
64265c0
Save downloaded zoom range
gino-m Oct 5, 2023
d871bee
Fix detekt errors
gino-m Oct 5, 2023
1a37a22
Tweak "no imagery" message
gino-m Oct 5, 2023
efb37e2
Fix compile warnings
gino-m Oct 6, 2023
a957400
Fix tests
gino-m Oct 6, 2023
f2fb9b7
Merge remote-tracking branch 'origin/gino-m/1821/offline-area-sizes-1…
gino-m Oct 6, 2023
b7f1ccc
Minor refactor
gino-m Oct 6, 2023
7672ad8
Fix tests, small refactor
gino-m Oct 6, 2023
24c3616
Fix size calculation
gino-m Oct 6, 2023
392118d
Fix layout
gino-m Oct 6, 2023
97563cb
Format code
gino-m Oct 6, 2023
699ae2e
Styling work in progress
gino-m Oct 6, 2023
18aecfa
Styling work in progress
gino-m Oct 6, 2023
1e7b27c
Address comments, add test
gino-m Oct 6, 2023
e87ff8f
Tweak syntax
gino-m Oct 6, 2023
64f3f91
Tweak syntax
gino-m Oct 6, 2023
1ccc6de
Fix detekt errors
gino-m Oct 6, 2023
7fa3141
Fix lint check
gino-m Oct 6, 2023
5454f63
Merge branch 'gino-m/1821/offline-area-sizes-2' of https://github.com…
gino-m Oct 6, 2023
c51def1
Fix toolbar
gino-m Oct 6, 2023
14ef4b9
Merge branch 'master' of https://github.com/google/ground-android int…
gino-m Oct 6, 2023
f6f28af
Update offline areas list UI
gino-m Oct 6, 2023
929cea0
Fixes
gino-m Oct 6, 2023
8e8f772
Fix strings
gino-m Oct 6, 2023
411e047
UI tweaks
gino-m Oct 6, 2023
5f2476c
Fix lint errors
gino-m Oct 6, 2023
b1aa539
Merge branch 'master' into gino-m/1821/offline-area-sizes-2
gino-m Oct 7, 2023
b7aa4f5
Merge branch 'master' into gino-m/1821/offline-area-sizes-2
gino-m Oct 7, 2023
6dedebe
Remove unused
gino-m Oct 7, 2023
e76d57e
Merge remote-tracking branch 'origin/gino-m/1821/offline-area-sizes-2…
gino-m Oct 7, 2023
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
Original file line number Diff line number Diff line change
Expand Up @@ -15,51 +15,33 @@
*/
package com.google.android.ground.ui.offlineareas

import android.annotation.SuppressLint
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.google.android.ground.databinding.OfflineAreasListItemBinding
import com.google.android.ground.model.imagery.OfflineArea
import com.google.android.ground.ui.common.Navigator

internal class OfflineAreaListAdapter(private val navigator: Navigator) :
RecyclerView.Adapter<OfflineAreaListAdapter.ViewHolder>() {
internal class OfflineAreaListAdapter : RecyclerView.Adapter<OfflineAreaListAdapter.ViewHolder>() {

private var offlineAreas: List<OfflineArea> = listOf()
private var offlineAreas: List<OfflineAreaListItemViewModel> = listOf()

class ViewHolder
internal constructor(
var binding: OfflineAreasListItemBinding,
var areas: List<OfflineArea>,
private val navigator: Navigator
) : RecyclerView.ViewHolder(binding.root), View.OnClickListener {
init {
binding.offlineAreaName.setOnClickListener(this)
}

override fun onClick(v: View) {
if (areas.isNotEmpty()) {
val id = areas[adapterPosition].id
navigator.navigate(OfflineAreasFragmentDirections.viewOfflineArea(id))
}
}
}
class ViewHolder internal constructor(var binding: OfflineAreasListItemBinding) :
RecyclerView.ViewHolder(binding.root)

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val offlineAreasListItemBinding =
OfflineAreasListItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return ViewHolder(offlineAreasListItemBinding, offlineAreas, navigator)
return ViewHolder(offlineAreasListItemBinding)
}

override fun onBindViewHolder(viewHolder: ViewHolder, position: Int) {
viewHolder.areas = offlineAreas
viewHolder.binding.offlineAreaName.text = offlineAreas[position].name
viewHolder.binding.viewModel = offlineAreas[position]
}

override fun getItemCount(): Int = offlineAreas.size

fun update(offlineAreas: List<OfflineArea>) {
@SuppressLint("NotifyDataSetChanged")
fun update(offlineAreas: List<OfflineAreaListItemViewModel>) {
this.offlineAreas = offlineAreas
notifyDataSetChanged()
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* Copyright 2023 Google LLC
*
* 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
*
* https://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.google.android.ground.ui.offlineareas

import com.google.android.ground.model.imagery.OfflineArea
import com.google.android.ground.ui.common.Navigator

class OfflineAreaListItemViewModel(
private val navigator: Navigator,
val area: OfflineArea,
val sizeOnDisk: String
) {
val areaName = area.name

fun onClick() {
navigator.navigate(OfflineAreasFragmentDirections.viewOfflineArea(area.id))
}
}
shobhitagarwal1612 marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.LinearLayoutManager
import com.google.android.ground.MainActivity
import com.google.android.ground.databinding.OfflineAreasFragBinding
import com.google.android.ground.ui.common.AbstractFragment
import com.google.android.ground.ui.common.Navigator
Expand All @@ -43,7 +42,7 @@ class OfflineAreasFragment : Hilt_OfflineAreasFragment() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
viewModel = getViewModel(OfflineAreasViewModel::class.java)
offlineAreaListAdapter = OfflineAreaListAdapter(navigator)
offlineAreaListAdapter = OfflineAreaListAdapter()
viewModel.offlineAreas.observe(this) { offlineAreaListAdapter.update(it) }
}

Expand All @@ -57,7 +56,8 @@ class OfflineAreasFragment : Hilt_OfflineAreasFragment() {
binding.viewModel = viewModel
binding.lifecycleOwner = this

(requireActivity() as MainActivity).setActionBar(binding.offlineAreasToolbar, true)
val toolbar = binding.offlineAreasToolbar
toolbar.setNavigationOnClickListener { navigator.navigateUp() }

val recyclerView = binding.offlineAreasList
recyclerView.setHasFixedSize(true)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,16 @@
*/
package com.google.android.ground.ui.offlineareas

import android.view.View
import androidx.lifecycle.LiveData
import androidx.lifecycle.toLiveData
import com.google.android.ground.model.imagery.OfflineArea
import com.google.android.ground.repository.OfflineAreaRepository
import com.google.android.ground.ui.common.AbstractViewModel
import com.google.android.ground.ui.common.Navigator
import com.google.android.ground.util.toMb
import com.google.android.ground.util.toMbString
import javax.inject.Inject
import kotlinx.coroutines.rx2.rxSingle
import timber.log.Timber

/**
Expand All @@ -32,34 +34,45 @@ class OfflineAreasViewModel
@Inject
internal constructor(
private val navigator: Navigator,
offlineAreaRepository: OfflineAreaRepository
private val offlineAreaRepository: OfflineAreaRepository
) : AbstractViewModel() {

/**
* Returns the current list of downloaded offline map imagery areas available for viewing. If an
* unexpected error accessing the local store is encountered, emits an empty list, circumventing
* the error.
*/
val offlineAreas: LiveData<List<OfflineArea>>
val offlineAreas: LiveData<List<OfflineAreaListItemViewModel>>

/**
* Returns the visibility of the "no area" message based on the current number of available
* offline areas.
*/
val noAreasMessageVisibility: LiveData<Int>
val showList: LiveData<Boolean>
val showNoAreasMessage: LiveData<Boolean>
val showProgressSpinner: LiveData<Boolean>

init {
val offlineAreas =
offlineAreaRepository
.offlineAreasOnceAndStream()
.switchMapSingle { rxSingle { toViewModel(it) } }
.doOnError { Timber.e(it, "Unexpected error loading offline areas from the local db") }
.onErrorReturnItem(listOf())

this.offlineAreas = offlineAreas.toLiveData()
noAreasMessageVisibility =
offlineAreas.map { if (it.isEmpty()) View.VISIBLE else View.GONE }.toLiveData()
showProgressSpinner = offlineAreas.map { false }.startWith(true).toLiveData()
showNoAreasMessage = offlineAreas.map { it.isEmpty() }.startWith(false).toLiveData()
showList = offlineAreas.map { it.isNotEmpty() }.startWith(false).toLiveData()
}

private suspend fun toViewModel(
offlineAreas: List<OfflineArea>
): List<OfflineAreaListItemViewModel> = offlineAreas.map { toViewModel(it) }

private suspend fun toViewModel(offlineArea: OfflineArea) =
OfflineAreaListItemViewModel(
navigator,
offlineArea,
offlineAreaRepository.sizeOnDevice(offlineArea).toMb().toMbString()
)

/** Navigate to the area selector for offline map imagery. */
fun showOfflineAreaSelector() {
navigator.navigate(OfflineAreasFragmentDirections.showOfflineAreaSelector())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,9 @@
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="#333333"
android:alpha="0.6">
android:alpha="1.0"
android:autoMirrored="true">
<path
android:fillColor="@android:color/white"
android:pathData="M12,2C6.5,2 2,6.5 2,12s4.5,10 10,10s10,-4.5 10,-10S17.5,2 12,2zM17,18H7v-2h10V18zM10.3,14L7,10.7l1.4,-1.4l1.9,1.9l5.3,-5.3L17,7.3L10.3,14z"/>
android:pathData="M20,11H7.83l5.59,-5.59L12,4l-8,8 8,8 1.41,-1.41L7.83,13H20v-2z"/>
</vector>
29 changes: 0 additions & 29 deletions ground/src/main/res/drawable-anydpi/ic_edit_black.xml

This file was deleted.

27 changes: 27 additions & 0 deletions ground/src/main/res/drawable/ic_maps_ar.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>

<!--
~ Copyright 2023 Google LLC
~
~ 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
~
~ https://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.
-->

<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="22dp"
android:height="22dp"
android:viewportWidth="22"
android:viewportHeight="22">
<path
android:pathData="M0,5V3C0,2.167 0.292,1.458 0.875,0.875C1.458,0.292 2.167,-0 3,-0H5V2H3C2.717,2 2.475,2.1 2.275,2.3C2.092,2.483 2,2.717 2,3V5H0ZM3,22C2.167,22 1.458,21.708 0.875,21.125C0.292,20.542 0,19.833 0,19V17H2V19C2,19.283 2.092,19.525 2.275,19.725C2.475,19.908 2.717,20 3,20H5V22H3ZM17,22V20H19C19.283,20 19.517,19.908 19.7,19.725C19.9,19.525 20,19.283 20,19V17H22V19C22,19.833 21.708,20.542 21.125,21.125C20.542,21.708 19.833,22 19,22H17ZM20,5V3C20,2.717 19.9,2.483 19.7,2.3C19.517,2.1 19.283,2 19,2H17V-0H19C19.833,-0 20.542,0.292 21.125,0.875C21.708,1.458 22,2.167 22,3V5H20ZM11,15.3C11.283,14.817 11.567,14.375 11.85,13.975C12.15,13.575 12.442,13.183 12.725,12.8C13.292,12.05 13.742,11.383 14.075,10.8C14.425,10.217 14.6,9.483 14.6,8.6C14.6,7.6 14.25,6.75 13.55,6.05C12.85,5.35 12,5 11,5C10,5 9.15,5.35 8.45,6.05C7.75,6.75 7.4,7.6 7.4,8.6C7.4,9.483 7.567,10.217 7.9,10.8C8.25,11.383 8.708,12.05 9.275,12.8C9.558,13.183 9.842,13.575 10.125,13.975C10.425,14.375 10.717,14.817 11,15.3ZM11,19C10.817,19 10.65,18.95 10.5,18.85C10.367,18.733 10.275,18.592 10.225,18.425C9.858,17.325 9.4,16.4 8.85,15.65C8.3,14.883 7.767,14.158 7.25,13.475C6.75,12.775 6.317,12.058 5.95,11.325C5.583,10.575 5.4,9.667 5.4,8.6C5.4,7.033 5.942,5.708 7.025,4.625C8.108,3.542 9.433,3 11,3C12.567,3 13.892,3.542 14.975,4.625C16.058,5.708 16.6,7.033 16.6,8.6C16.6,9.667 16.417,10.575 16.05,11.325C15.683,12.058 15.242,12.775 14.725,13.475C14.225,14.158 13.7,14.883 13.15,15.65C12.6,16.4 12.142,17.325 11.775,18.425C11.725,18.592 11.625,18.733 11.475,18.85C11.342,18.95 11.183,19 11,19ZM11,10.6C11.55,10.6 12.017,10.408 12.4,10.025C12.8,9.625 13,9.15 13,8.6C13,8.05 12.8,7.583 12.4,7.2C12.017,6.8 11.55,6.6 11,6.6C10.45,6.6 9.975,6.8 9.575,7.2C9.192,7.583 9,8.05 9,8.6C9,9.15 9.192,9.625 9.575,10.025C9.975,10.408 10.45,10.6 11,10.6Z"
android:fillColor="#002108"/>
</vector>
3 changes: 2 additions & 1 deletion ground/src/main/res/layout/offline_area_selector_frag.xml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
android:layout_height="wrap_content"
android:elevation="@dimen/toolbar_elevation"
android:minHeight="?attr/actionBarSize"
android:background="?attr/colorSurfaceContainer"
app:layout_constraintTop_toTopOf="parent">

<TextView
Expand Down Expand Up @@ -156,7 +157,7 @@
android:id="@+id/button_buttons"
android:layout_width="match_parent"
android:layout_height="90dp"
android:background="@color/md_theme_background"
android:background="?attr/colorSurfaceContainer"
android:gravity="top"
android:orientation="horizontal"
app:layout_constraintBottom_toBottomOf="parent">
Expand Down
2 changes: 1 addition & 1 deletion ground/src/main/res/layout/offline_area_viewer_frag.xml
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@
tools:text="Area name, Country" />
<TextView
android:id="@+id/offline_area_size_on_device"
style="@style/TextAppearance.App.BodyMedium.OnSurface"
style="@style/TextAppearance.App.BodyMedium"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
Expand Down
65 changes: 51 additions & 14 deletions ground/src/main/res/layout/offline_areas_frag.xml
Original file line number Diff line number Diff line change
Expand Up @@ -28,55 +28,92 @@
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/colorSurfaceContainer"
android:fitsSystemWindows="true">

<com.google.android.ground.ui.common.TwoLineToolbar
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/offline_areas_toolbar"
android:layout_width="0dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:elevation="@dimen/toolbar_elevation"
android:theme="@style/PrimaryToolbarTheme"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:navigationIcon="@drawable/ic_arrow_back"
app:title="@string/offline_map_imagery" />
<LinearLayout
android:id="@+id/offline_area_list_heading"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="24dp"
android:layout_marginBottom="8dp"
android:orientation="vertical"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toBottomOf="@id/offline_areas_toolbar"
app:visible="@{viewModel.showList}">
<TextView
android:id="@+id/offline_areas_list_title"
style="@style/TextAppearance.App.TitleMedium"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/offline_downloaded_areas" />
<TextView
android:id="@+id/offline_areas_list_tip"
style="@style/TextAppearance.App.BodySmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/offline_area_list_tip"
android:textColor="?attr/colorOnSurfaceVariant" />
</LinearLayout>

<androidx.recyclerview.widget.RecyclerView
android:id="@+id/offline_areas_list"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginTop="16dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/offline_areas_toolbar" />
app:layout_constraintTop_toBottomOf="@+id/offline_area_list_heading"
app:visible="@{viewModel.showList}" />

<Button
<com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
android:id="@+id/floatingActionButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/chip_button_margin_right"
android:layout_marginBottom="@dimen/map_btn_margin_bottom"
android:clickable="true"
android:contentDescription="@string/offline_area_selector_prompt"
android:focusable="true"
android:onClick="@{() -> viewModel.showOfflineAreaSelector()}"
android:text="@string/offline_area_selector_select"
app:icon="@drawable/ic_maps_ar"
app:iconTint="?attr/colorOnPrimaryContainer"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<TextView
android:id="@+id/no_areas_downloaded_message"
style="@style/TextAppearance.App.BodyMedium"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="16dp"
android:textAlignment="center"
style="@style/TextAppearance.App.BodyMedium.OnSurface"
android:contentDescription="@string/offline_area_selector_prompt"
android:padding="48dp"
android:text="@string/no_basemaps_downloaded"
android:visibility="@{viewModel.noAreasMessageVisibility}"
android:textAlignment="center"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />

app:layout_constraintTop_toTopOf="parent"
app:visible="@{viewModel.showNoAreasMessage}" />
<ProgressBar
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:visible="@{viewModel.showProgressSpinner}" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
Loading