#1173 Do not filter out files over 4GB when storage is fat32, Grey them out instead

This commit is contained in:
Sean Mac Gillicuddy 2019-12-06 10:28:42 +00:00
parent d7834e7da4
commit 30827d5815
6 changed files with 214 additions and 114 deletions

View File

@ -32,6 +32,7 @@ import io.reactivex.processors.PublishProcessor
import io.reactivex.schedulers.Schedulers import io.reactivex.schedulers.Schedulers
import org.kiwix.kiwixmobile.core.R import org.kiwix.kiwixmobile.core.R
import org.kiwix.kiwixmobile.core.StorageObserver import org.kiwix.kiwixmobile.core.StorageObserver
import org.kiwix.kiwixmobile.core.base.SideEffect
import org.kiwix.kiwixmobile.core.dao.FetchDownloadDao import org.kiwix.kiwixmobile.core.dao.FetchDownloadDao
import org.kiwix.kiwixmobile.core.dao.NewBookDao import org.kiwix.kiwixmobile.core.dao.NewBookDao
import org.kiwix.kiwixmobile.core.dao.NewLanguagesDao import org.kiwix.kiwixmobile.core.dao.NewLanguagesDao
@ -50,10 +51,6 @@ import org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.SelectionMode.NORM
import org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.adapter.BooksOnDiskListItem import org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.adapter.BooksOnDiskListItem
import org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.adapter.BooksOnDiskListItem.BookOnDisk import org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.adapter.BooksOnDiskListItem.BookOnDisk
import org.kiwix.kiwixmobile.zim_manager.Fat32Checker.FileSystemState import org.kiwix.kiwixmobile.zim_manager.Fat32Checker.FileSystemState
import org.kiwix.kiwixmobile.zim_manager.Fat32Checker.FileSystemState.CanWrite4GbFile
import org.kiwix.kiwixmobile.zim_manager.Fat32Checker.FileSystemState.CannotWrite4GbFile
import org.kiwix.kiwixmobile.zim_manager.Fat32Checker.FileSystemState.NotEnoughSpaceFor4GbFile
import org.kiwix.kiwixmobile.zim_manager.Fat32Checker.FileSystemState.Unknown
import org.kiwix.kiwixmobile.zim_manager.NetworkState.CONNECTED import org.kiwix.kiwixmobile.zim_manager.NetworkState.CONNECTED
import org.kiwix.kiwixmobile.zim_manager.ZimManageViewModel.FileSelectActions.MultiModeFinished import org.kiwix.kiwixmobile.zim_manager.ZimManageViewModel.FileSelectActions.MultiModeFinished
import org.kiwix.kiwixmobile.zim_manager.ZimManageViewModel.FileSelectActions.RequestDeleteMultiSelection import org.kiwix.kiwixmobile.zim_manager.ZimManageViewModel.FileSelectActions.RequestDeleteMultiSelection
@ -66,7 +63,6 @@ import org.kiwix.kiwixmobile.zim_manager.fileselect_view.effects.DeleteFiles
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.effects.None import org.kiwix.kiwixmobile.zim_manager.fileselect_view.effects.None
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.effects.OpenFile import org.kiwix.kiwixmobile.zim_manager.fileselect_view.effects.OpenFile
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.effects.ShareFiles import org.kiwix.kiwixmobile.zim_manager.fileselect_view.effects.ShareFiles
import org.kiwix.kiwixmobile.core.base.SideEffect
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.effects.StartMultiSelection import org.kiwix.kiwixmobile.zim_manager.fileselect_view.effects.StartMultiSelection
import org.kiwix.kiwixmobile.zim_manager.library_view.adapter.LibraryListItem import org.kiwix.kiwixmobile.zim_manager.library_view.adapter.LibraryListItem
import org.kiwix.kiwixmobile.zim_manager.library_view.adapter.LibraryListItem.BookItem import org.kiwix.kiwixmobile.zim_manager.library_view.adapter.LibraryListItem.BookItem
@ -354,14 +350,6 @@ class ZimManageViewModel @Inject constructor(
val booksUnfilteredByLanguage = val booksUnfilteredByLanguage =
applyUserFilter( applyUserFilter(
libraryNetworkEntity.books libraryNetworkEntity.books
.filter {
when (fileSystemState) {
Unknown,
CannotWrite4GbFile -> isLessThan4GB(it)
NotEnoughSpaceFor4GbFile,
CanWrite4GbFile -> true
}
}
.filterNot { downloadedBooksIds.contains(it.id) } .filterNot { downloadedBooksIds.contains(it.id) }
.filterNot { downloadingBookIds.contains(it.id) }, .filterNot { downloadingBookIds.contains(it.id) },
filter filter
@ -370,30 +358,29 @@ class ZimManageViewModel @Inject constructor(
return listOf( return listOf(
*createLibrarySection( *createLibrarySection(
booksUnfilteredByLanguage.filter { activeLanguageCodes.contains(it.language) }, booksUnfilteredByLanguage.filter { activeLanguageCodes.contains(it.language) },
fileSystemState,
R.string.your_languages, R.string.your_languages,
Long.MAX_VALUE Long.MAX_VALUE
), ),
*createLibrarySection( *createLibrarySection(
booksUnfilteredByLanguage.filterNot { activeLanguageCodes.contains(it.language) }, booksUnfilteredByLanguage.filterNot { activeLanguageCodes.contains(it.language) },
fileSystemState,
R.string.other_languages, R.string.other_languages,
Long.MIN_VALUE Long.MIN_VALUE
) )
) )
} }
private fun isLessThan4GB(it: Book) =
it.size.toLongOrNull() ?: 0L <
Fat32Checker.FOUR_GIGABYTES_IN_KILOBYTES
private fun createLibrarySection( private fun createLibrarySection(
books: List<Book>, books: List<Book>,
fileSystemState: FileSystemState,
sectionStringId: Int, sectionStringId: Int,
sectionId: Long sectionId: Long
) = ) =
if (books.isNotEmpty()) if (books.isNotEmpty())
arrayOf( arrayOf(
DividerItem(sectionId, context.getString(sectionStringId)), DividerItem(sectionId, context.getString(sectionStringId)),
*toBookItems(books) *toBookItems(books, fileSystemState)
) )
else emptyArray() else emptyArray()
@ -407,8 +394,11 @@ class ZimManageViewModel @Inject constructor(
booksUnfilteredByLanguage.filter { it.searchMatches > 0 } booksUnfilteredByLanguage.filter { it.searchMatches > 0 }
} }
private fun toBookItems(books: List<Book>) = private fun toBookItems(
books.map { BookItem(it) }.toTypedArray() books: List<Book>,
fileSystemState: FileSystemState
) =
books.map { BookItem(it, fileSystemState) }.toTypedArray()
private fun checkFileSystemForBooksOnRequest(booksFromDao: Flowable<List<BookOnDisk>>) = private fun checkFileSystemForBooksOnRequest(booksFromDao: Flowable<List<BookOnDisk>>) =
requestFileSystemCheck requestFileSystemCheck

View File

@ -19,6 +19,12 @@
package org.kiwix.kiwixmobile.zim_manager.library_view.adapter package org.kiwix.kiwixmobile.zim_manager.library_view.adapter
import org.kiwix.kiwixmobile.core.entity.LibraryNetworkEntity.Book import org.kiwix.kiwixmobile.core.entity.LibraryNetworkEntity.Book
import org.kiwix.kiwixmobile.zim_manager.Fat32Checker
import org.kiwix.kiwixmobile.zim_manager.Fat32Checker.FileSystemState
import org.kiwix.kiwixmobile.zim_manager.Fat32Checker.FileSystemState.CanWrite4GbFile
import org.kiwix.kiwixmobile.zim_manager.Fat32Checker.FileSystemState.CannotWrite4GbFile
import org.kiwix.kiwixmobile.zim_manager.Fat32Checker.FileSystemState.NotEnoughSpaceFor4GbFile
import org.kiwix.kiwixmobile.zim_manager.Fat32Checker.FileSystemState.Unknown
sealed class LibraryListItem { sealed class LibraryListItem {
abstract val id: Long abstract val id: Long
@ -28,8 +34,22 @@ sealed class LibraryListItem {
val text: String val text: String
) : LibraryListItem() ) : LibraryListItem()
data class BookItem( data class BookItem constructor(
val book: Book, val book: Book,
val canBeDownloaded: Boolean,
override val id: Long = book.id.hashCode().toLong() override val id: Long = book.id.hashCode().toLong()
) : LibraryListItem() ) : LibraryListItem() {
constructor(book: Book, fileSystemState: FileSystemState) : this(
book,
when (fileSystemState) {
Unknown, CannotWrite4GbFile -> book.isLessThan4GB()
NotEnoughSpaceFor4GbFile, CanWrite4GbFile -> true
}
)
companion object {
private fun Book.isLessThan4GB() =
size.toLongOrNull() ?: 0L < Fat32Checker.FOUR_GIGABYTES_IN_KILOBYTES
}
}
} }

View File

@ -19,7 +19,6 @@
package org.kiwix.kiwixmobile.zim_manager.library_view.adapter package org.kiwix.kiwixmobile.zim_manager.library_view.adapter
import android.view.View import android.view.View
import kotlinx.android.synthetic.main.library_divider.divider_text
import kotlinx.android.synthetic.main.item_library.creator import kotlinx.android.synthetic.main.item_library.creator
import kotlinx.android.synthetic.main.item_library.date import kotlinx.android.synthetic.main.item_library.date
import kotlinx.android.synthetic.main.item_library.description import kotlinx.android.synthetic.main.item_library.description
@ -29,14 +28,16 @@ import kotlinx.android.synthetic.main.item_library.language
import kotlinx.android.synthetic.main.item_library.publisher import kotlinx.android.synthetic.main.item_library.publisher
import kotlinx.android.synthetic.main.item_library.size import kotlinx.android.synthetic.main.item_library.size
import kotlinx.android.synthetic.main.item_library.title import kotlinx.android.synthetic.main.item_library.title
import kotlinx.android.synthetic.main.item_library.unableToDownload
import kotlinx.android.synthetic.main.library_divider.divider_text
import org.kiwix.kiwixmobile.core.CoreApp import org.kiwix.kiwixmobile.core.CoreApp
import org.kiwix.kiwixmobile.core.base.adapter.BaseViewHolder
import org.kiwix.kiwixmobile.core.downloader.model.Base64String import org.kiwix.kiwixmobile.core.downloader.model.Base64String
import org.kiwix.kiwixmobile.core.extensions.setBitmap import org.kiwix.kiwixmobile.core.extensions.setBitmap
import org.kiwix.kiwixmobile.core.extensions.setTextAndVisibility import org.kiwix.kiwixmobile.core.extensions.setTextAndVisibility
import org.kiwix.kiwixmobile.core.utils.BookUtils import org.kiwix.kiwixmobile.core.utils.BookUtils
import org.kiwix.kiwixmobile.core.utils.NetworkUtils import org.kiwix.kiwixmobile.core.utils.NetworkUtils
import org.kiwix.kiwixmobile.core.zim_manager.KiloByte import org.kiwix.kiwixmobile.core.zim_manager.KiloByte
import org.kiwix.kiwixmobile.core.base.adapter.BaseViewHolder
import org.kiwix.kiwixmobile.zim_manager.library_view.adapter.LibraryListItem.BookItem import org.kiwix.kiwixmobile.zim_manager.library_view.adapter.LibraryListItem.BookItem
import org.kiwix.kiwixmobile.zim_manager.library_view.adapter.LibraryListItem.DividerItem import org.kiwix.kiwixmobile.zim_manager.library_view.adapter.LibraryListItem.DividerItem
@ -64,6 +65,8 @@ sealed class LibraryViewHolder<in T : LibraryListItem>(containerView: View) :
containerView.setOnClickListener { containerView.setOnClickListener {
clickAction.invoke(item) clickAction.invoke(item)
} }
unableToDownload.setOnTouchListener { _, _ -> true }
unableToDownload.visibility = if (item.canBeDownloaded) View.GONE else View.VISIBLE
} }
} }

View File

@ -1,115 +1,115 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="?selectableItemBackground" android:background="?selectableItemBackground"
android:minHeight="?listPreferredItemHeight" android:minHeight="?listPreferredItemHeight"
android:orientation="horizontal" android:orientation="horizontal"
android:paddingLeft="@dimen/activity_horizontal_margin" tools:ignore="Overdraw, RTLHardcoded">
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingBottom="@dimen/activity_vertical_margin"
tools:ignore="Overdraw">
<ImageView <ImageView
android:id="@+id/favicon" android:id="@+id/favicon"
android:layout_width="@dimen/favicon_width" android:layout_width="@dimen/favicon_width"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center" android:layout_marginLeft="@dimen/activity_horizontal_margin"
android:layout_marginRight="@dimen/favicon_margin_right" android:layout_marginRight="@dimen/favicon_margin_right"
android:adjustViewBounds="true" android:adjustViewBounds="true"
android:scaleType="fitCenter" android:scaleType="fitCenter"
android:src="@mipmap/ic_launcher" android:src="@mipmap/ic_launcher"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:ignore="RtlHardcoded" /> tools:ignore="RtlHardcoded" />
<LinearLayout <TextView
android:layout_width="0dp" android:id="@+id/title"
style="@style/list_item_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="1" android:layout_marginLeft="@dimen/favicon_margin_right"
android:orientation="vertical" android:layout_marginTop="@dimen/activity_horizontal_margin"
android:paddingLeft="@dimen/dimen_small_padding" app:layout_constraintStart_toEndOf="@+id/favicon"
tools:ignore="RtlHardcoded,RtlSymmetry"> app:layout_constraintTop_toTopOf="parent"
tools:text="Title" />
<TextView <TextView
android:id="@+id/title" android:id="@+id/description"
style="@style/list_item_title" style="@style/list_item_body"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
tools:text="Title" /> app:layout_constraintStart_toStartOf="@+id/title"
app:layout_constraintTop_toBottomOf="@+id/title"
tools:text="Description" />
<TextView <TextView
android:id="@+id/description" android:id="@+id/size"
style="@style/list_item_body" style="@style/list_item_body"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
tools:text="Description" /> app:layout_constraintStart_toStartOf="@+id/description"
app:layout_constraintTop_toBottomOf="@+id/description"
tools:text="File Size" />
<LinearLayout <TextView
android:layout_width="match_parent" android:id="@+id/creator"
android:layout_height="wrap_content" style="@style/list_item_body"
android:baselineAligned="false" android:layout_width="wrap_content"
android:orientation="horizontal" android:layout_height="wrap_content"
android:paddingTop="@dimen/dimen_medium_padding"> app:layout_constraintStart_toStartOf="@+id/size"
app:layout_constraintTop_toBottomOf="@+id/size"
tools:text="Author" />
<LinearLayout <TextView
android:layout_width="0dp" android:id="@+id/publisher"
android:layout_height="match_parent" style="@style/list_item_body"
android:layout_weight="1" android:layout_width="wrap_content"
android:orientation="vertical"> android:layout_height="wrap_content"
android:paddingBottom="@dimen/activity_horizontal_margin"
app:layout_constraintStart_toStartOf="@+id/creator"
app:layout_constraintTop_toBottomOf="@+id/creator"
tools:text="Publisher" />
<TextView
android:id="@+id/size"
style="@style/list_item_body"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:text="File Size" />
<TextView <TextView
android:id="@+id/creator" android:id="@+id/date"
style="@style/list_item_body" style="@style/list_item_body"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
tools:text="Author" /> android:layout_marginRight="@dimen/activity_horizontal_margin"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@+id/size"
tools:text="Date" />
<TextView <TextView
android:id="@+id/publisher" android:id="@+id/language"
style="@style/list_item_body" style="@style/list_item_body"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
tools:text="Publisher" /> app:layout_constraintEnd_toEndOf="@+id/date"
app:layout_constraintTop_toBottomOf="@+id/date"
tools:text="Language" />
</LinearLayout> <TextView
android:id="@+id/fileName"
style="@style/list_item_body"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="@+id/language"
app:layout_constraintTop_toBottomOf="@+id/language"
tools:text="File Name" />
<LinearLayout <View
android:layout_width="0dp" android:id="@+id/unableToDownload"
android:layout_height="match_parent" android:layout_width="0dp"
android:layout_weight="1" android:layout_height="0dp"
android:gravity="end" android:alpha=".5"
android:orientation="vertical"> android:background="#808080"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:visibility="gone" />
<TextView </androidx.constraintlayout.widget.ConstraintLayout>
android:id="@+id/date"
style="@style/list_item_body"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:text="Date" />
<TextView
android:id="@+id/language"
style="@style/list_item_body"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:text="Language" />
<TextView
android:id="@+id/fileName"
style="@style/list_item_body"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:text="File Name" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
</LinearLayout>

View File

@ -392,15 +392,16 @@ class ZimManageViewModelTest {
.assertValue( .assertValue(
listOf( listOf(
LibraryListItem.DividerItem(Long.MAX_VALUE, "1"), LibraryListItem.DividerItem(Long.MAX_VALUE, "1"),
LibraryListItem.BookItem(bookWithActiveLanguage), LibraryListItem.BookItem(bookWithActiveLanguage, true),
LibraryListItem.DividerItem(Long.MIN_VALUE, "2"), LibraryListItem.DividerItem(Long.MIN_VALUE, "2"),
LibraryListItem.BookItem(bookWithInactiveLanguage) LibraryListItem.BookItem(bookWithInactiveLanguage, true)
) )
) )
} }
@Test @Test
fun `library filters out files over 4GB if file system state says to`() { fun `library marks files over 4GB as can't download if file system state says to`() {
every { application.getString(R.string.other_languages) } returns "2"
val bookOver4Gb = book( val bookOver4Gb = book(
id = "0", id = "0",
url = "", url = "",
@ -423,6 +424,11 @@ class ZimManageViewModelTest {
testScheduler.advanceTimeBy(500, MILLISECONDS) testScheduler.advanceTimeBy(500, MILLISECONDS)
testScheduler.triggerActions() testScheduler.triggerActions()
viewModel.libraryItems.test() viewModel.libraryItems.test()
.assertValue(listOf()) .assertValue(
listOf(
LibraryListItem.DividerItem(Long.MIN_VALUE, "2"),
LibraryListItem.BookItem(bookOver4Gb, false)
)
)
} }
} }

View File

@ -0,0 +1,81 @@
/*
* Kiwix Android
* Copyright (c) 2019 Kiwix <android.kiwix.org>
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package org.kiwix.kiwixmobile.zim_manager.library_view.adapter
import io.mockk.clearAllMocks
import io.mockk.every
import io.mockk.mockk
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.kiwix.kiwixmobile.core.entity.LibraryNetworkEntity.Book
import org.kiwix.kiwixmobile.zim_manager.Fat32Checker
import org.kiwix.kiwixmobile.zim_manager.Fat32Checker.FileSystemState
import org.kiwix.kiwixmobile.zim_manager.Fat32Checker.FileSystemState.CanWrite4GbFile
import org.kiwix.kiwixmobile.zim_manager.Fat32Checker.FileSystemState.CannotWrite4GbFile
import org.kiwix.kiwixmobile.zim_manager.Fat32Checker.FileSystemState.NotEnoughSpaceFor4GbFile
import org.kiwix.kiwixmobile.zim_manager.Fat32Checker.FileSystemState.Unknown
import org.kiwix.kiwixmobile.zim_manager.library_view.adapter.LibraryListItem.BookItem
internal class LibraryListItemTest {
val book = mockk<Book>()
@BeforeEach
fun init() {
clearAllMocks()
every { book.getId() } returns "0"
every { book.getSize() } returns "0"
}
@Test
internal fun `Unknown file system state files under 4GB can be downloaded`() {
assertThat(canBeDownloaded(book, Unknown)).isTrue()
}
@Test
internal fun `Unknown file system state greater than 4GB can't be downloaded`() {
every { book.getSize() } returns (Fat32Checker.FOUR_GIGABYTES_IN_KILOBYTES + 1).toString()
assertThat(canBeDownloaded(book, Unknown)).isFalse()
}
@Test
internal fun `Unknown file system state empty size can be downloaded`() {
every { book.getSize() } returns ""
assertThat(canBeDownloaded(book, Unknown)).isTrue()
}
@Test
internal fun `CannotWrite4GB file system state can be downloaded`() {
assertThat(canBeDownloaded(book, CannotWrite4GbFile)).isTrue()
}
@Test
internal fun `CanWrite4GbFile file system state can be downloaded`() {
assertThat(canBeDownloaded(book, CanWrite4GbFile)).isTrue()
}
@Test
internal fun `NotEnoughSpaceFor4GbFile file system state can be downloaded`() {
assertThat(canBeDownloaded(book, NotEnoughSpaceFor4GbFile)).isTrue()
}
private fun canBeDownloaded(book: Book, fileSystemState: FileSystemState) =
BookItem(book, fileSystemState).canBeDownloaded
}