mirror of
https://github.com/kiwix/kiwix-android.git
synced 2025-09-23 20:54:13 -04:00
Merge remote-tracking branch 'origin/develop' into new-translations
This commit is contained in:
commit
aa75f7a256
@ -226,20 +226,13 @@ android {
|
|||||||
testBuildType "debug"
|
testBuildType "debug"
|
||||||
|
|
||||||
lintOptions {
|
lintOptions {
|
||||||
// Treat lint seriously
|
|
||||||
abortOnError true
|
abortOnError true
|
||||||
// Hardcoded strings in xml
|
checkAllWarnings true
|
||||||
error "HardcodedText"
|
warningsAsErrors true
|
||||||
// Hardcoded strings in setText
|
ignore 'MissingTranslation' //TODO stop ignoring
|
||||||
error "SetTextI18n"
|
baseline file("lint-baseline.xml")
|
||||||
// Strings.xml issues
|
|
||||||
warning "ExtraTranslation"
|
|
||||||
warning "MissingTranslation"
|
|
||||||
|
|
||||||
// Warnings
|
|
||||||
warning "InvalidPackage"
|
|
||||||
warning "StringFormatInvalid"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
testOptions {
|
testOptions {
|
||||||
unitTests.returnDefaultValues = true
|
unitTests.returnDefaultValues = true
|
||||||
unitTests.all {
|
unitTests.all {
|
||||||
|
16748
app/lint-baseline.xml
Normal file
16748
app/lint-baseline.xml
Normal file
File diff suppressed because it is too large
Load Diff
@ -218,7 +218,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "2:6862771806221961183",
|
"id": "2:6862771806221961183",
|
||||||
"name": "zimID"
|
"name": "zimId"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "3:4312769031500860715",
|
"id": "3:4312769031500860715",
|
||||||
|
@ -23,6 +23,7 @@ import dagger.Subcomponent
|
|||||||
import org.kiwix.kiwixmobile.di.modules.ActivityModule
|
import org.kiwix.kiwixmobile.di.modules.ActivityModule
|
||||||
import org.kiwix.kiwixmobile.downloader.DownloadFragment
|
import org.kiwix.kiwixmobile.downloader.DownloadFragment
|
||||||
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.ZimFileSelectFragment
|
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.ZimFileSelectFragment
|
||||||
|
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.effects.DeleteFiles
|
||||||
import org.kiwix.kiwixmobile.zim_manager.library_view.LibraryFragment
|
import org.kiwix.kiwixmobile.zim_manager.library_view.LibraryFragment
|
||||||
|
|
||||||
@Subcomponent(modules = [ActivityModule::class])
|
@Subcomponent(modules = [ActivityModule::class])
|
||||||
@ -33,6 +34,8 @@ interface ActivityComponent {
|
|||||||
|
|
||||||
fun inject(zimFileSelectFragment: ZimFileSelectFragment)
|
fun inject(zimFileSelectFragment: ZimFileSelectFragment)
|
||||||
|
|
||||||
|
fun inject(deleteFiles: DeleteFiles)
|
||||||
|
|
||||||
@Subcomponent.Builder
|
@Subcomponent.Builder
|
||||||
interface Builder {
|
interface Builder {
|
||||||
|
|
||||||
|
@ -18,10 +18,13 @@
|
|||||||
package org.kiwix.kiwixmobile.downloader.model
|
package org.kiwix.kiwixmobile.downloader.model
|
||||||
|
|
||||||
import org.kiwix.kiwixmobile.library.entity.LibraryNetworkEntity.Book
|
import org.kiwix.kiwixmobile.library.entity.LibraryNetworkEntity.Book
|
||||||
|
import org.kiwix.kiwixmobile.utils.StorageUtils
|
||||||
|
|
||||||
data class DownloadModel(
|
data class DownloadModel(
|
||||||
val databaseId: Long? = null,
|
val databaseId: Long? = null,
|
||||||
val downloadId: Long ,
|
val downloadId: Long,
|
||||||
val book: Book
|
val book: Book
|
||||||
)
|
) {
|
||||||
|
val fileNameFromUrl: String get() = StorageUtils.getFileNameFromUrl(book.url)
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,43 @@
|
|||||||
|
package org.kiwix.kiwixmobile.extensions
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.view.ActionMode
|
||||||
|
import android.view.ActionMode.Callback
|
||||||
|
import android.view.Menu
|
||||||
|
import android.view.MenuItem
|
||||||
|
|
||||||
|
fun Activity.startActionMode(
|
||||||
|
menuId: Int,
|
||||||
|
idsToClickActions: Map<Int, () -> Any>,
|
||||||
|
onDestroyAction: () -> Unit
|
||||||
|
): ActionMode? {
|
||||||
|
return startActionMode(object : Callback {
|
||||||
|
override fun onActionItemClicked(
|
||||||
|
mode: ActionMode,
|
||||||
|
item: MenuItem
|
||||||
|
) = idsToClickActions[item.itemId]?.let {
|
||||||
|
it()
|
||||||
|
mode.finish()
|
||||||
|
true
|
||||||
|
} ?: false
|
||||||
|
|
||||||
|
override fun onCreateActionMode(
|
||||||
|
mode: ActionMode,
|
||||||
|
menu: Menu?
|
||||||
|
): Boolean {
|
||||||
|
mode.getMenuInflater()
|
||||||
|
.inflate(menuId, menu)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPrepareActionMode(
|
||||||
|
mode: ActionMode?,
|
||||||
|
menu: Menu?
|
||||||
|
) = false
|
||||||
|
|
||||||
|
override fun onDestroyActionMode(mode: ActionMode?) {
|
||||||
|
onDestroyAction()
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
@ -37,7 +37,6 @@ import android.net.Uri;
|
|||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.CountDownTimer;
|
import android.os.CountDownTimer;
|
||||||
import android.os.Environment;
|
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.provider.Settings;
|
import android.provider.Settings;
|
||||||
import android.text.SpannableString;
|
import android.text.SpannableString;
|
||||||
@ -84,10 +83,10 @@ import com.google.android.material.appbar.AppBarLayout;
|
|||||||
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
||||||
import com.google.android.material.navigation.NavigationView;
|
import com.google.android.material.navigation.NavigationView;
|
||||||
import com.google.android.material.snackbar.Snackbar;
|
import com.google.android.material.snackbar.Snackbar;
|
||||||
|
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
@ -100,20 +99,18 @@ import org.kiwix.kiwixmobile.base.BaseActivity;
|
|||||||
import org.kiwix.kiwixmobile.bookmark.BookmarkItem;
|
import org.kiwix.kiwixmobile.bookmark.BookmarkItem;
|
||||||
import org.kiwix.kiwixmobile.bookmark.BookmarksActivity;
|
import org.kiwix.kiwixmobile.bookmark.BookmarksActivity;
|
||||||
import org.kiwix.kiwixmobile.data.ZimContentProvider;
|
import org.kiwix.kiwixmobile.data.ZimContentProvider;
|
||||||
import org.kiwix.kiwixmobile.data.local.entity.Bookmark;
|
|
||||||
import org.kiwix.kiwixmobile.help.HelpActivity;
|
import org.kiwix.kiwixmobile.help.HelpActivity;
|
||||||
import org.kiwix.kiwixmobile.history.HistoryActivity;
|
import org.kiwix.kiwixmobile.history.HistoryActivity;
|
||||||
import org.kiwix.kiwixmobile.history.HistoryListItem;
|
import org.kiwix.kiwixmobile.history.HistoryListItem;
|
||||||
import org.kiwix.kiwixmobile.library.entity.LibraryNetworkEntity;
|
|
||||||
import org.kiwix.kiwixmobile.search.SearchActivity;
|
import org.kiwix.kiwixmobile.search.SearchActivity;
|
||||||
import org.kiwix.kiwixmobile.settings.KiwixSettingsActivity;
|
import org.kiwix.kiwixmobile.settings.KiwixSettingsActivity;
|
||||||
import org.kiwix.kiwixmobile.utils.DimenUtils;
|
import org.kiwix.kiwixmobile.utils.DimenUtils;
|
||||||
import org.kiwix.kiwixmobile.utils.LanguageUtils;
|
import org.kiwix.kiwixmobile.utils.LanguageUtils;
|
||||||
import org.kiwix.kiwixmobile.utils.NetworkUtils;
|
import org.kiwix.kiwixmobile.utils.NetworkUtils;
|
||||||
import org.kiwix.kiwixmobile.utils.StyleUtils;
|
import org.kiwix.kiwixmobile.utils.StyleUtils;
|
||||||
import org.kiwix.kiwixmobile.utils.files.FileSearch;
|
|
||||||
import org.kiwix.kiwixmobile.utils.files.FileUtils;
|
import org.kiwix.kiwixmobile.utils.files.FileUtils;
|
||||||
import org.kiwix.kiwixmobile.zim_manager.ZimManageActivity;
|
import org.kiwix.kiwixmobile.zim_manager.ZimManageActivity;
|
||||||
|
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.StorageObserver;
|
||||||
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.adapter.BookOnDiskDelegate;
|
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.adapter.BookOnDiskDelegate;
|
||||||
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.adapter.BooksOnDiskAdapter;
|
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.adapter.BooksOnDiskAdapter;
|
||||||
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.adapter.BooksOnDiskListItem;
|
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.adapter.BooksOnDiskListItem;
|
||||||
@ -201,14 +198,18 @@ public class MainActivity extends BaseActivity implements WebViewCallback,
|
|||||||
ImageView bottomToolbarArrowBack;
|
ImageView bottomToolbarArrowBack;
|
||||||
@BindView(R.id.bottom_toolbar_arrow_forward)
|
@BindView(R.id.bottom_toolbar_arrow_forward)
|
||||||
ImageView bottomToolbarArrowForward;
|
ImageView bottomToolbarArrowForward;
|
||||||
@Inject
|
|
||||||
MainContract.Presenter presenter;
|
|
||||||
@BindView(R.id.tab_switcher_recycler_view)
|
@BindView(R.id.tab_switcher_recycler_view)
|
||||||
RecyclerView tabRecyclerView;
|
RecyclerView tabRecyclerView;
|
||||||
@BindView(R.id.activity_main_tab_switcher)
|
@BindView(R.id.activity_main_tab_switcher)
|
||||||
View tabSwitcherRoot;
|
View tabSwitcherRoot;
|
||||||
@BindView(R.id.tab_switcher_close_all_tabs)
|
@BindView(R.id.tab_switcher_close_all_tabs)
|
||||||
FloatingActionButton closeAllTabsButton;
|
FloatingActionButton closeAllTabsButton;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
MainContract.Presenter presenter;
|
||||||
|
@Inject
|
||||||
|
StorageObserver storageObserver;
|
||||||
|
|
||||||
private CountDownTimer hideBackToTopTimer = new CountDownTimer(1200, 1200) {
|
private CountDownTimer hideBackToTopTimer = new CountDownTimer(1200, 1200) {
|
||||||
@Override
|
@Override
|
||||||
public void onTick(long millisUntilFinished) {
|
public void onTick(long millisUntilFinished) {
|
||||||
@ -274,21 +275,7 @@ public class MainActivity extends BaseActivity implements WebViewCallback,
|
|||||||
closeTab(viewHolder.getAdapterPosition());
|
closeTab(viewHolder.getAdapterPosition());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
private FileSearch fileSearch =
|
|
||||||
new FileSearch(this, Collections.emptyList(), new FileSearch.ResultListener() {
|
|
||||||
final List<BooksOnDiskListItem.BookOnDisk> newBooks = new ArrayList<>();
|
|
||||||
|
|
||||||
@Override public void onBookFound(BooksOnDiskListItem.BookOnDisk bookOnDisk) {
|
|
||||||
runOnUiThread(() -> {
|
|
||||||
newBooks.add(bookOnDisk);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onScanCompleted() {
|
|
||||||
presenter.saveBooks(newBooks);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
private static void updateWidgets(Context context) {
|
private static void updateWidgets(Context context) {
|
||||||
Intent intent = new Intent(context.getApplicationContext(), KiwixSearchWidget.class);
|
Intent intent = new Intent(context.getApplicationContext(), KiwixSearchWidget.class);
|
||||||
@ -377,6 +364,7 @@ public class MainActivity extends BaseActivity implements WebViewCallback,
|
|||||||
open(bookOnDiskItem);
|
open(bookOnDiskItem);
|
||||||
return Unit.INSTANCE;
|
return Unit.INSTANCE;
|
||||||
},
|
},
|
||||||
|
null,
|
||||||
null),
|
null),
|
||||||
BookOnDiskDelegate.LanguageDelegate.INSTANCE
|
BookOnDiskDelegate.LanguageDelegate.INSTANCE
|
||||||
);
|
);
|
||||||
@ -739,7 +727,6 @@ public class MainActivity extends BaseActivity implements WebViewCallback,
|
|||||||
downloadBookButton = null;
|
downloadBookButton = null;
|
||||||
hideBackToTopTimer.cancel();
|
hideBackToTopTimer.cancel();
|
||||||
hideBackToTopTimer = null;
|
hideBackToTopTimer = null;
|
||||||
fileSearch = null;
|
|
||||||
// TODO create a base Activity class that class this.
|
// TODO create a base Activity class that class this.
|
||||||
FileUtils.deleteCachedFiles(this);
|
FileUtils.deleteCachedFiles(this);
|
||||||
tts.shutdown();
|
tts.shutdown();
|
||||||
@ -1199,7 +1186,7 @@ public class MainActivity extends BaseActivity implements WebViewCallback,
|
|||||||
case REQUEST_READ_STORAGE_PERMISSION: {
|
case REQUEST_READ_STORAGE_PERMISSION: {
|
||||||
if (grantResults.length > 0
|
if (grantResults.length > 0
|
||||||
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||||
fileSearch.scan(sharedPreferenceUtil.getPrefStorage());
|
scanStorageForZims();
|
||||||
} else {
|
} else {
|
||||||
Snackbar.make(drawerLayout, R.string.request_storage, Snackbar.LENGTH_LONG)
|
Snackbar.make(drawerLayout, R.string.request_storage, Snackbar.LENGTH_LONG)
|
||||||
.setAction(R.string.menu_settings, view -> {
|
.setAction(R.string.menu_settings, view -> {
|
||||||
@ -1228,6 +1215,13 @@ public class MainActivity extends BaseActivity implements WebViewCallback,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void scanStorageForZims() {
|
||||||
|
storageObserver.getBooksOnFileSystem()
|
||||||
|
.take(1)
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.subscribe(presenter::saveBooks, Throwable::printStackTrace);
|
||||||
|
}
|
||||||
|
|
||||||
// Workaround for popup bottom menu on older devices
|
// Workaround for popup bottom menu on older devices
|
||||||
private void StyleMenuButtons(Menu m) {
|
private void StyleMenuButtons(Menu m) {
|
||||||
// Find each menu item and set its text colour
|
// Find each menu item and set its text colour
|
||||||
@ -2105,7 +2099,7 @@ public class MainActivity extends BaseActivity implements WebViewCallback,
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addBooks(List<BooksOnDiskListItem> books) {
|
public void addBooks(List<BooksOnDiskListItem> books) {
|
||||||
booksAdapter.setItemList(books);
|
booksAdapter.setItems(books);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void searchFiles() {
|
private void searchFiles() {
|
||||||
@ -2116,7 +2110,7 @@ public class MainActivity extends BaseActivity implements WebViewCallback,
|
|||||||
new String[] { Manifest.permission.READ_EXTERNAL_STORAGE },
|
new String[] { Manifest.permission.READ_EXTERNAL_STORAGE },
|
||||||
REQUEST_READ_STORAGE_PERMISSION);
|
REQUEST_READ_STORAGE_PERMISSION);
|
||||||
} else {
|
} else {
|
||||||
fileSearch.scan(sharedPreferenceUtil.getPrefStorage());
|
scanStorageForZims();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,296 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2013 Rashiq Ahmad <rashiq.z@gmail.com>
|
|
||||||
*
|
|
||||||
* 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
|
|
||||||
* 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, write to the Free Software
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
|
||||||
* MA 02110-1301, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.kiwix.kiwixmobile.utils.files;
|
|
||||||
|
|
||||||
import android.content.ContentResolver;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.database.Cursor;
|
|
||||||
import android.net.Uri;
|
|
||||||
import android.os.Environment;
|
|
||||||
import android.provider.MediaStore;
|
|
||||||
import android.util.Log;
|
|
||||||
import eu.mhutti1.utils.storage.StorageDevice;
|
|
||||||
import eu.mhutti1.utils.storage.StorageDeviceUtils;
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FilenameFilter;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Vector;
|
|
||||||
import org.kiwix.kiwixmobile.data.ZimContentProvider;
|
|
||||||
import org.kiwix.kiwixmobile.downloader.model.DownloadModel;
|
|
||||||
import org.kiwix.kiwixmobile.library.entity.LibraryNetworkEntity;
|
|
||||||
import org.kiwix.kiwixmobile.utils.StorageUtils;
|
|
||||||
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.adapter.BooksOnDiskListItem;
|
|
||||||
|
|
||||||
import static org.kiwix.kiwixmobile.utils.Constants.TAG_KIWIX;
|
|
||||||
|
|
||||||
public class FileSearch {
|
|
||||||
|
|
||||||
// Array of zim file extensions
|
|
||||||
public static final String[] zimFiles = { "zim", "zimaa" };
|
|
||||||
|
|
||||||
private final Context context;
|
|
||||||
private final List<DownloadModel> downloads;
|
|
||||||
private final ResultListener listener;
|
|
||||||
|
|
||||||
private boolean fileSystemScanCompleted = false;
|
|
||||||
private boolean mediaStoreScanCompleted = false;
|
|
||||||
|
|
||||||
public FileSearch(Context ctx, List<DownloadModel> downloads, ResultListener listener) {
|
|
||||||
this.context = ctx;
|
|
||||||
this.downloads = downloads;
|
|
||||||
this.listener = listener;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static synchronized LibraryNetworkEntity.Book fileToBook(String filePath) {
|
|
||||||
LibraryNetworkEntity.Book book = null;
|
|
||||||
|
|
||||||
if (ZimContentProvider.zimFileName != null) {
|
|
||||||
ZimContentProvider.originalFileName = ZimContentProvider.zimFileName;
|
|
||||||
}
|
|
||||||
// Check a file isn't being opened and temporally use content provider to access details
|
|
||||||
// This is not a great solution as we shouldn't need to fully open our ZIM files to get their metadata
|
|
||||||
if (ZimContentProvider.canIterate) {
|
|
||||||
if (ZimContentProvider.setZimFile(filePath) != null) {
|
|
||||||
try {
|
|
||||||
book = new LibraryNetworkEntity.Book();
|
|
||||||
book.title = ZimContentProvider.getZimFileTitle();
|
|
||||||
book.id = ZimContentProvider.getId();
|
|
||||||
book.file = new File(filePath);
|
|
||||||
book.size = String.valueOf(ZimContentProvider.getFileSize());
|
|
||||||
book.favicon = ZimContentProvider.getFavicon();
|
|
||||||
book.creator = ZimContentProvider.getCreator();
|
|
||||||
book.publisher = ZimContentProvider.getPublisher();
|
|
||||||
book.date = ZimContentProvider.getDate();
|
|
||||||
book.description = ZimContentProvider.getDescription();
|
|
||||||
book.language = ZimContentProvider.getLanguage();
|
|
||||||
} catch (Exception e) {
|
|
||||||
// TODO 20171215 Consider more elegant approaches.
|
|
||||||
// This is to see if we can catch the exception at all!
|
|
||||||
Log.e("kiwix-filesearch", "Problem parsing a book entry from the library file. ", e);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Return content provider to its previous state
|
|
||||||
if (!ZimContentProvider.originalFileName.equals("")) {
|
|
||||||
ZimContentProvider.setZimFile(ZimContentProvider.originalFileName);
|
|
||||||
}
|
|
||||||
ZimContentProvider.originalFileName = "";
|
|
||||||
|
|
||||||
return book;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void scan(String defaultPath) {
|
|
||||||
// Start custom file search
|
|
||||||
new Thread(() -> {
|
|
||||||
scanFileSystem(defaultPath);
|
|
||||||
fileSystemScanCompleted = true;
|
|
||||||
checkCompleted();
|
|
||||||
}).start();
|
|
||||||
|
|
||||||
// Star mediastore search
|
|
||||||
new Thread(() -> {
|
|
||||||
scanMediaStore();
|
|
||||||
mediaStoreScanCompleted = true;
|
|
||||||
checkCompleted();
|
|
||||||
}).start();
|
|
||||||
}
|
|
||||||
|
|
||||||
// If both searches are complete callback
|
|
||||||
private synchronized void checkCompleted() {
|
|
||||||
if (mediaStoreScanCompleted && fileSystemScanCompleted) {
|
|
||||||
listener.onScanCompleted();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void scanMediaStore() {
|
|
||||||
ContentResolver contentResolver = context.getContentResolver();
|
|
||||||
Uri uri = MediaStore.Files.getContentUri("external");
|
|
||||||
|
|
||||||
String[] projection = { MediaStore.MediaColumns.DATA };
|
|
||||||
String selection =
|
|
||||||
MediaStore.MediaColumns.DATA + " like ? or " + MediaStore.MediaColumns.DATA + " like ? ";
|
|
||||||
|
|
||||||
Cursor query = contentResolver.query(uri, projection, selection,
|
|
||||||
new String[] { "%." + zimFiles[0], "%." + zimFiles[1] }, null);
|
|
||||||
|
|
||||||
if (query == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
while (query.moveToNext()) {
|
|
||||||
File file = new File(query.getString(0));
|
|
||||||
if (file.canRead()) {
|
|
||||||
onFileFound(file.getAbsolutePath());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
query.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Scan through the file system and find all the files with .zim and .zimaa extensions
|
|
||||||
public void scanFileSystem(String defaultPath) {
|
|
||||||
FilenameFilter[] filter = new FilenameFilter[zimFiles.length];
|
|
||||||
|
|
||||||
// Search all external directories that we can find.
|
|
||||||
final ArrayList<StorageDevice> storageDevices =
|
|
||||||
StorageDeviceUtils.getStorageDevices(context, false);
|
|
||||||
String[] tempRoots = new String[storageDevices.size() + 2];
|
|
||||||
int j = 0;
|
|
||||||
tempRoots[j++] = "/mnt";
|
|
||||||
tempRoots[j++] = defaultPath;
|
|
||||||
for (StorageDevice storageDevice : storageDevices) {
|
|
||||||
tempRoots[j++] = storageDevice.getName();
|
|
||||||
}
|
|
||||||
|
|
||||||
int i = 0;
|
|
||||||
for (final String extension : zimFiles) {
|
|
||||||
filter[i] = (dir, name) -> name.endsWith("." + extension);
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
|
|
||||||
String dirNamePrimary = new File(
|
|
||||||
Environment.getExternalStorageDirectory().getAbsolutePath()).toString();
|
|
||||||
|
|
||||||
for (final String dirName : tempRoots) {
|
|
||||||
if (dirNamePrimary.equals(dirName)) {
|
|
||||||
// We already got this directory from getExternalStorageDirectory().
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
File f = new File(dirName);
|
|
||||||
if (f.isDirectory()) {
|
|
||||||
scanDirectory(dirName, filter);
|
|
||||||
} else {
|
|
||||||
Log.i(TAG_KIWIX, "Skipping missing directory " + dirName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Iterate through the file system
|
|
||||||
private Collection<File> listFiles(File directory, FilenameFilter[] filter, int recurse) {
|
|
||||||
|
|
||||||
Vector<File> files = new Vector<>();
|
|
||||||
|
|
||||||
File[] entries = directory.listFiles();
|
|
||||||
|
|
||||||
if (entries != null) {
|
|
||||||
for (File entry : entries) {
|
|
||||||
for (FilenameFilter filefilter : filter) {
|
|
||||||
if (filter == null || filefilter.accept(directory, entry.getName())) {
|
|
||||||
files.add(entry);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ((recurse <= -1) || (recurse > 0 && entry.isDirectory())) {
|
|
||||||
recurse--;
|
|
||||||
files.addAll(listFiles(entry, filter, recurse));
|
|
||||||
recurse++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return files;
|
|
||||||
}
|
|
||||||
|
|
||||||
private File[] listFilesAsArray(File directory, FilenameFilter[] filter, int recurse) {
|
|
||||||
Collection<File> files = listFiles(directory, filter, recurse);
|
|
||||||
|
|
||||||
File[] arr = new File[files.size()];
|
|
||||||
return files.toArray(arr);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static synchronized BooksOnDiskListItem.BookOnDisk fileToBookOnDisk(String filePath) {
|
|
||||||
LibraryNetworkEntity.Book book = null;
|
|
||||||
|
|
||||||
if (ZimContentProvider.zimFileName != null) {
|
|
||||||
ZimContentProvider.originalFileName = ZimContentProvider.zimFileName;
|
|
||||||
}
|
|
||||||
// Check a file isn't being opened and temporally use content provider to access details
|
|
||||||
// This is not a great solution as we shouldn't need to fully open our ZIM files to get their metadata
|
|
||||||
if (ZimContentProvider.canIterate) {
|
|
||||||
if (ZimContentProvider.setZimFile(filePath) != null) {
|
|
||||||
try {
|
|
||||||
book = new LibraryNetworkEntity.Book();
|
|
||||||
book.title = ZimContentProvider.getZimFileTitle();
|
|
||||||
book.id = ZimContentProvider.getId();
|
|
||||||
book.size = String.valueOf(ZimContentProvider.getFileSize());
|
|
||||||
book.favicon = ZimContentProvider.getFavicon();
|
|
||||||
book.creator = ZimContentProvider.getCreator();
|
|
||||||
book.publisher = ZimContentProvider.getPublisher();
|
|
||||||
book.date = ZimContentProvider.getDate();
|
|
||||||
book.description = ZimContentProvider.getDescription();
|
|
||||||
book.language = ZimContentProvider.getLanguage();
|
|
||||||
} catch (Exception e) {
|
|
||||||
// TODO 20171215 Consider more elegant approaches.
|
|
||||||
// This is to see if we can catch the exception at all!
|
|
||||||
Log.e("kiwix-filesearch", "Problem parsing a book entry from the library file. ", e);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Return content provider to its previous state
|
|
||||||
if (!ZimContentProvider.originalFileName.equals("")) {
|
|
||||||
ZimContentProvider.setZimFile(ZimContentProvider.originalFileName);
|
|
||||||
}
|
|
||||||
ZimContentProvider.originalFileName = "";
|
|
||||||
|
|
||||||
return book == null ? null
|
|
||||||
: new BooksOnDiskListItem.BookOnDisk(null, book, new File(filePath),0L);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fill fileList with files found in the specific directory
|
|
||||||
private void scanDirectory(String directory, FilenameFilter[] filter) {
|
|
||||||
Log.d(TAG_KIWIX, "Searching directory " + directory);
|
|
||||||
File[] foundFiles = listFilesAsArray(new File(directory), filter, -1);
|
|
||||||
for (File f : foundFiles) {
|
|
||||||
Log.d(TAG_KIWIX, "Found " + f.getAbsolutePath());
|
|
||||||
onFileFound(f.getAbsolutePath());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Callback that a new file has been found
|
|
||||||
public void onFileFound(String filePath) {
|
|
||||||
if (fileIsDownloading(filePath)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
BooksOnDiskListItem.BookOnDisk book = fileToBookOnDisk(filePath);
|
|
||||||
|
|
||||||
if (book != null) {
|
|
||||||
listener.onBookFound(book);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean fileIsDownloading(String filePath) {
|
|
||||||
for (DownloadModel download : downloads) {
|
|
||||||
if (filePath.endsWith(StorageUtils.getFileNameFromUrl(download.getBook().getUrl()))) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface ResultListener {
|
|
||||||
void onBookFound(BooksOnDiskListItem.BookOnDisk book);
|
|
||||||
|
|
||||||
void onScanCompleted();
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,86 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2013 Rashiq Ahmad <rashiq.z@gmail.com>
|
||||||
|
*
|
||||||
|
* 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
|
||||||
|
* 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, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
* MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.kiwix.kiwixmobile.utils.files
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.os.Environment
|
||||||
|
import android.provider.MediaStore.Files
|
||||||
|
import android.provider.MediaStore.MediaColumns
|
||||||
|
import eu.mhutti1.utils.storage.StorageDeviceUtils
|
||||||
|
import io.reactivex.Flowable
|
||||||
|
import io.reactivex.functions.BiFunction
|
||||||
|
import org.kiwix.kiwixmobile.extensions.forEachRow
|
||||||
|
import org.kiwix.kiwixmobile.extensions.get
|
||||||
|
import java.io.File
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class FileSearch @Inject constructor(private val context: Context) {
|
||||||
|
|
||||||
|
val zimFileExtensions = arrayOf("zim", "zimaa")
|
||||||
|
|
||||||
|
fun scan(defaultPath: String) =
|
||||||
|
Flowable.combineLatest(
|
||||||
|
Flowable.fromCallable { scanFileSystem(defaultPath) },
|
||||||
|
Flowable.fromCallable(this::scanMediaStore),
|
||||||
|
BiFunction<List<File>, List<File>, List<File>> { filesSystemFiles, mediaStoreFiles ->
|
||||||
|
filesSystemFiles + mediaStoreFiles
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun scanMediaStore() = mutableListOf<File>().apply {
|
||||||
|
queryMediaStore()
|
||||||
|
?.forEachRow { cursor ->
|
||||||
|
File(cursor.get<String>(MediaColumns.DATA)).takeIf(File::canRead)
|
||||||
|
?.also { add(it) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun queryMediaStore() = context.contentResolver
|
||||||
|
.query(
|
||||||
|
Files.getContentUri("external"),
|
||||||
|
arrayOf(MediaColumns.DATA),
|
||||||
|
MediaColumns.DATA + " like ? or " + MediaColumns.DATA + " like ? ",
|
||||||
|
arrayOf("%." + zimFileExtensions[0], "%." + zimFileExtensions[1]),
|
||||||
|
null
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun scanFileSystem(defaultPath: String) =
|
||||||
|
directoryRoots(defaultPath)
|
||||||
|
.minus(Environment.getExternalStorageDirectory().absolutePath)
|
||||||
|
.fold(mutableListOf<File>(), { acc, root ->
|
||||||
|
acc.apply { addAll(scanDirectory(root)) }
|
||||||
|
})
|
||||||
|
|
||||||
|
private fun directoryRoots(defaultPath: String) = listOf(
|
||||||
|
"/mnt",
|
||||||
|
defaultPath,
|
||||||
|
*StorageDeviceUtils.getStorageDevices(context, false).map { it.name }.toTypedArray()
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun scanDirectory(directory: String) = filesMatchingExtensions(directory) ?: emptyList()
|
||||||
|
|
||||||
|
private fun filesMatchingExtensions(directory: String) = File(directory)
|
||||||
|
.listFiles { _, name -> name.endsWithAny(*zimFileExtensions) }
|
||||||
|
?.toList()
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun String.endsWithAny(vararg suffixes: String) =
|
||||||
|
suffixes.fold(false, { acc, s -> acc or endsWith(s) })
|
@ -52,9 +52,24 @@ import org.kiwix.kiwixmobile.zim_manager.Fat32Checker.FileSystemState.CanWrite4G
|
|||||||
import org.kiwix.kiwixmobile.zim_manager.Fat32Checker.FileSystemState.CannotWrite4GbFile
|
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.NotEnoughSpaceFor4GbFile
|
||||||
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.RequestDeleteMultiSelection
|
||||||
|
import org.kiwix.kiwixmobile.zim_manager.ZimManageViewModel.FileSelectActions.RequestMultiSelection
|
||||||
|
import org.kiwix.kiwixmobile.zim_manager.ZimManageViewModel.FileSelectActions.RequestOpen
|
||||||
|
import org.kiwix.kiwixmobile.zim_manager.ZimManageViewModel.FileSelectActions.RequestSelect
|
||||||
|
import org.kiwix.kiwixmobile.zim_manager.ZimManageViewModel.FileSelectActions.RequestShareMultiSelection
|
||||||
|
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.FileSelectListState
|
||||||
|
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.SelectionMode.MULTI
|
||||||
|
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.SelectionMode.NORMAL
|
||||||
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.StorageObserver
|
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.StorageObserver
|
||||||
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.adapter.BooksOnDiskListItem
|
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.adapter.BooksOnDiskListItem
|
||||||
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.adapter.BooksOnDiskListItem.BookOnDisk
|
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.adapter.BooksOnDiskListItem.BookOnDisk
|
||||||
|
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.OpenFile
|
||||||
|
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.effects.ShareFiles
|
||||||
|
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.effects.SideEffect
|
||||||
|
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
|
||||||
import org.kiwix.kiwixmobile.zim_manager.library_view.adapter.LibraryListItem.DividerItem
|
import org.kiwix.kiwixmobile.zim_manager.library_view.adapter.LibraryListItem.DividerItem
|
||||||
@ -79,16 +94,26 @@ class ZimManageViewModel @Inject constructor(
|
|||||||
private val defaultLanguageProvider: DefaultLanguageProvider,
|
private val defaultLanguageProvider: DefaultLanguageProvider,
|
||||||
private val dataSource: DataSource
|
private val dataSource: DataSource
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
sealed class FileSelectActions {
|
||||||
|
data class RequestOpen(val bookOnDisk: BookOnDisk) : FileSelectActions()
|
||||||
|
data class RequestSelect(val bookOnDisk: BookOnDisk) : FileSelectActions()
|
||||||
|
data class RequestMultiSelection(val bookOnDisk: BookOnDisk) : FileSelectActions()
|
||||||
|
object RequestDeleteMultiSelection : FileSelectActions()
|
||||||
|
object RequestShareMultiSelection : FileSelectActions()
|
||||||
|
object MultiModeFinished : FileSelectActions()
|
||||||
|
}
|
||||||
|
|
||||||
|
val sideEffects = PublishProcessor.create<SideEffect<out Any?>>()
|
||||||
val libraryItems: MutableLiveData<List<LibraryListItem>> = MutableLiveData()
|
val libraryItems: MutableLiveData<List<LibraryListItem>> = MutableLiveData()
|
||||||
val downloadItems: MutableLiveData<List<DownloadItem>> = MutableLiveData()
|
val downloadItems: MutableLiveData<List<DownloadItem>> = MutableLiveData()
|
||||||
val bookItems: MutableLiveData<List<BooksOnDiskListItem>> = MutableLiveData()
|
val fileSelectListStates: MutableLiveData<FileSelectListState> = MutableLiveData()
|
||||||
val deviceListIsRefreshing = MutableLiveData<Boolean>()
|
val deviceListIsRefreshing = MutableLiveData<Boolean>()
|
||||||
val libraryListIsRefreshing = MutableLiveData<Boolean>()
|
val libraryListIsRefreshing = MutableLiveData<Boolean>()
|
||||||
val networkStates = MutableLiveData<NetworkState>()
|
val networkStates = MutableLiveData<NetworkState>()
|
||||||
val languageItems = MutableLiveData<List<Language>>()
|
val languageItems = MutableLiveData<List<Language>>()
|
||||||
|
|
||||||
val requestFileSystemCheck = PublishProcessor.create<Unit>()
|
val requestFileSystemCheck = PublishProcessor.create<Unit>()
|
||||||
|
val fileSelectActions = PublishProcessor.create<FileSelectActions>()
|
||||||
val requestDownloadLibrary = BehaviorProcessor.createDefault<Unit>(Unit)
|
val requestDownloadLibrary = BehaviorProcessor.createDefault<Unit>(Unit)
|
||||||
val requestFiltering = BehaviorProcessor.createDefault<String>("")
|
val requestFiltering = BehaviorProcessor.createDefault<String>("")
|
||||||
val requestLanguagesDialog = PublishProcessor.create<Unit>()
|
val requestLanguagesDialog = PublishProcessor.create<Unit>()
|
||||||
@ -127,10 +152,74 @@ class ZimManageViewModel @Inject constructor(
|
|||||||
updateLanguagesInDao(networkLibrary, languages),
|
updateLanguagesInDao(networkLibrary, languages),
|
||||||
updateNetworkStates(),
|
updateNetworkStates(),
|
||||||
updateLanguageItemsForDialog(languages),
|
updateLanguageItemsForDialog(languages),
|
||||||
requestsAndConnectivtyChangesToLibraryRequests(networkLibrary)
|
requestsAndConnectivtyChangesToLibraryRequests(networkLibrary),
|
||||||
|
fileSelectActions()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun fileSelectActions() = fileSelectActions.subscribe({
|
||||||
|
sideEffects.offer(
|
||||||
|
when (it) {
|
||||||
|
is RequestOpen -> OpenFile(it.bookOnDisk)
|
||||||
|
is RequestMultiSelection -> startMultiSelectionAndSelectBook(it.bookOnDisk)
|
||||||
|
RequestDeleteMultiSelection -> DeleteFiles(selectionsFromState())
|
||||||
|
RequestShareMultiSelection -> ShareFiles(selectionsFromState())
|
||||||
|
MultiModeFinished -> noSideEffectAndClearSelectionState()
|
||||||
|
is RequestSelect -> noSideEffectSelectBook(it.bookOnDisk)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}, Throwable::printStackTrace)
|
||||||
|
|
||||||
|
private fun startMultiSelectionAndSelectBook(
|
||||||
|
bookOnDisk: BookOnDisk
|
||||||
|
): StartMultiSelection {
|
||||||
|
fileSelectListStates.value?.let {
|
||||||
|
fileSelectListStates.postValue(
|
||||||
|
it.copy(
|
||||||
|
bookOnDiskListItems = selectBook(it, bookOnDisk),
|
||||||
|
selectionMode = MULTI
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return StartMultiSelection(bookOnDisk, fileSelectActions)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun selectBook(
|
||||||
|
it: FileSelectListState,
|
||||||
|
bookOnDisk: BookOnDisk
|
||||||
|
): List<BooksOnDiskListItem> {
|
||||||
|
return it.bookOnDiskListItems.map { listItem ->
|
||||||
|
if (listItem.id == bookOnDisk.id) listItem.apply { isSelected = !isSelected }
|
||||||
|
else listItem
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun noSideEffectSelectBook(bookOnDisk: BookOnDisk): SideEffect<Unit> {
|
||||||
|
fileSelectListStates.value?.let {
|
||||||
|
fileSelectListStates.postValue(
|
||||||
|
it.copy(bookOnDiskListItems = it.bookOnDiskListItems.map { listItem ->
|
||||||
|
if (listItem.id == bookOnDisk.id) listItem.apply { isSelected = !isSelected }
|
||||||
|
else listItem
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return None
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun selectionsFromState() = fileSelectListStates.value?.selectedBooks ?: emptyList()
|
||||||
|
|
||||||
|
private fun noSideEffectAndClearSelectionState(): SideEffect<Unit> {
|
||||||
|
fileSelectListStates.value?.let {
|
||||||
|
fileSelectListStates.postValue(
|
||||||
|
it.copy(
|
||||||
|
bookOnDiskListItems = it.bookOnDiskListItems.map { it.apply { isSelected = false } },
|
||||||
|
selectionMode = NORMAL
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return None
|
||||||
|
}
|
||||||
|
|
||||||
private fun requestsAndConnectivtyChangesToLibraryRequests(library: PublishProcessor<LibraryNetworkEntity>) =
|
private fun requestsAndConnectivtyChangesToLibraryRequests(library: PublishProcessor<LibraryNetworkEntity>) =
|
||||||
Flowable.combineLatest(
|
Flowable.combineLatest(
|
||||||
requestDownloadLibrary,
|
requestDownloadLibrary,
|
||||||
@ -411,10 +500,27 @@ class ZimManageViewModel @Inject constructor(
|
|||||||
private fun updateBookItems() =
|
private fun updateBookItems() =
|
||||||
dataSource.booksOnDiskAsListItems()
|
dataSource.booksOnDiskAsListItems()
|
||||||
.subscribe(
|
.subscribe(
|
||||||
bookItems::postValue,
|
{ newList ->
|
||||||
|
fileSelectListStates.postValue(
|
||||||
|
fileSelectListStates.value?.let { inheritSelections(it, newList) }
|
||||||
|
?: FileSelectListState(newList)
|
||||||
|
)
|
||||||
|
},
|
||||||
Throwable::printStackTrace
|
Throwable::printStackTrace
|
||||||
)
|
)
|
||||||
|
|
||||||
|
private fun inheritSelections(
|
||||||
|
oldState: FileSelectListState,
|
||||||
|
newList: MutableList<BooksOnDiskListItem>
|
||||||
|
): FileSelectListState {
|
||||||
|
return oldState.copy(
|
||||||
|
bookOnDiskListItems = newList.map { newBookOnDisk ->
|
||||||
|
val firstOrNull =
|
||||||
|
oldState.bookOnDiskListItems.firstOrNull { oldBookOnDisk -> oldBookOnDisk.id == newBookOnDisk.id }
|
||||||
|
newBookOnDisk.apply { isSelected = firstOrNull?.isSelected ?: false }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
private fun removeCompletedDownloadsFromDb(downloadStatuses: Flowable<List<DownloadStatus>>) =
|
private fun removeCompletedDownloadsFromDb(downloadStatuses: Flowable<List<DownloadStatus>>) =
|
||||||
downloadStatuses
|
downloadStatuses
|
||||||
.observeOn(Schedulers.io())
|
.observeOn(Schedulers.io())
|
||||||
|
@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
* Kiwix Android
|
||||||
|
* Copyright (C) 2018 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.fileselect_view
|
||||||
|
|
||||||
|
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.SelectionMode.NORMAL
|
||||||
|
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.adapter.BooksOnDiskListItem
|
||||||
|
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.adapter.BooksOnDiskListItem.BookOnDisk
|
||||||
|
|
||||||
|
data class FileSelectListState(
|
||||||
|
val bookOnDiskListItems: List<BooksOnDiskListItem>,
|
||||||
|
val selectionMode: SelectionMode = NORMAL
|
||||||
|
) {
|
||||||
|
val selectedBooks by lazy {
|
||||||
|
bookOnDiskListItems.filter { it.isSelected }.filterIsInstance(BookOnDisk::class.java)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class SelectionMode {
|
||||||
|
NORMAL,
|
||||||
|
MULTI
|
||||||
|
}
|
@ -1,45 +1,80 @@
|
|||||||
package org.kiwix.kiwixmobile.zim_manager.fileselect_view
|
package org.kiwix.kiwixmobile.zim_manager.fileselect_view
|
||||||
|
|
||||||
import android.content.Context
|
import io.reactivex.functions.BiFunction
|
||||||
import android.util.Log
|
|
||||||
import io.reactivex.processors.PublishProcessor
|
|
||||||
import io.reactivex.schedulers.Schedulers
|
import io.reactivex.schedulers.Schedulers
|
||||||
|
import org.kiwix.kiwixmobile.data.ZimContentProvider
|
||||||
import org.kiwix.kiwixmobile.database.newdb.dao.NewDownloadDao
|
import org.kiwix.kiwixmobile.database.newdb.dao.NewDownloadDao
|
||||||
import org.kiwix.kiwixmobile.downloader.model.DownloadModel
|
import org.kiwix.kiwixmobile.downloader.model.DownloadModel
|
||||||
|
import org.kiwix.kiwixmobile.library.entity.LibraryNetworkEntity.Book
|
||||||
import org.kiwix.kiwixmobile.utils.SharedPreferenceUtil
|
import org.kiwix.kiwixmobile.utils.SharedPreferenceUtil
|
||||||
import org.kiwix.kiwixmobile.utils.files.FileSearch
|
import org.kiwix.kiwixmobile.utils.files.FileSearch
|
||||||
import org.kiwix.kiwixmobile.utils.files.FileSearch.ResultListener
|
|
||||||
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.adapter.BooksOnDiskListItem.BookOnDisk
|
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.adapter.BooksOnDiskListItem.BookOnDisk
|
||||||
|
import java.io.File
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class StorageObserver @Inject constructor(
|
class StorageObserver @Inject constructor(
|
||||||
private val context: Context,
|
|
||||||
private val sharedPreferenceUtil: SharedPreferenceUtil,
|
private val sharedPreferenceUtil: SharedPreferenceUtil,
|
||||||
private val downloadDao: NewDownloadDao
|
downloadDao: NewDownloadDao,
|
||||||
|
private val fileSearch: FileSearch
|
||||||
) {
|
) {
|
||||||
|
|
||||||
private val _booksOnFileSystem = PublishProcessor.create<List<BookOnDisk>>()
|
val booksOnFileSystem = scanFiles()
|
||||||
val booksOnFileSystem = _booksOnFileSystem.distinctUntilChanged()
|
.withLatestFrom(
|
||||||
.doOnSubscribe {
|
downloadDao.downloads(),
|
||||||
downloadDao.downloads()
|
BiFunction(this::toFilesThatAreNotDownloading)
|
||||||
.subscribeOn(Schedulers.io())
|
)
|
||||||
.take(1)
|
.map {
|
||||||
.subscribe(this::scanFiles, Throwable::printStackTrace)
|
it.mapNotNull { file -> convertToBookOnDisk(file) }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun scanFiles(downloads: List<DownloadModel>) {
|
private fun toFilesThatAreNotDownloading(
|
||||||
FileSearch(context, downloads, object : ResultListener {
|
files: List<File>,
|
||||||
val foundBooks = mutableSetOf<BookOnDisk>()
|
downloads: List<DownloadModel>
|
||||||
|
) = files.filter { fileHasNoMatchingDownload(downloads, it) }
|
||||||
|
|
||||||
override fun onBookFound(book: BookOnDisk) {
|
private fun fileHasNoMatchingDownload(
|
||||||
foundBooks.add(book)
|
downloads: List<DownloadModel>,
|
||||||
Log.i("Scanner", "File Search: Found Book " + book.book.title)
|
file: File
|
||||||
}
|
) = downloads.firstOrNull {
|
||||||
|
file.absolutePath.endsWith(it.fileNameFromUrl)
|
||||||
|
} == null
|
||||||
|
|
||||||
override fun onScanCompleted() {
|
private fun scanFiles() = fileSearch.scan(sharedPreferenceUtil.prefStorage)
|
||||||
_booksOnFileSystem.onNext(foundBooks.toList())
|
.subscribeOn(Schedulers.io())
|
||||||
|
|
||||||
}
|
private fun convertToBookOnDisk(file: File): BookOnDisk? {
|
||||||
}).scan(sharedPreferenceUtil.prefStorage)
|
configureZimContentProvider()
|
||||||
|
var bookOnDisk: BookOnDisk? = null
|
||||||
|
if (ZimContentProvider.canIterate && ZimContentProvider.setZimFile(file.absolutePath) != null) {
|
||||||
|
bookOnDisk = BookOnDisk(book = bookFromZimContentProvider(), file = file)
|
||||||
|
}
|
||||||
|
resetZimContentProvider()
|
||||||
|
return bookOnDisk
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun bookFromZimContentProvider() = Book().apply {
|
||||||
|
title = ZimContentProvider.getZimFileTitle()
|
||||||
|
id = ZimContentProvider.getId()
|
||||||
|
size = ZimContentProvider.getFileSize()
|
||||||
|
.toString()
|
||||||
|
favicon = ZimContentProvider.getFavicon()
|
||||||
|
creator = ZimContentProvider.getCreator()
|
||||||
|
publisher = ZimContentProvider.getPublisher()
|
||||||
|
date = ZimContentProvider.getDate()
|
||||||
|
description = ZimContentProvider.getDescription()
|
||||||
|
language = ZimContentProvider.getLanguage()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun configureZimContentProvider() {
|
||||||
|
if (ZimContentProvider.zimFileName != null) {
|
||||||
|
ZimContentProvider.originalFileName = ZimContentProvider.zimFileName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun resetZimContentProvider() {
|
||||||
|
if (ZimContentProvider.originalFileName != "") {
|
||||||
|
ZimContentProvider.setZimFile(ZimContentProvider.originalFileName)
|
||||||
|
}
|
||||||
|
ZimContentProvider.originalFileName = ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,8 +21,8 @@ package org.kiwix.kiwixmobile.zim_manager.fileselect_view
|
|||||||
|
|
||||||
import android.Manifest
|
import android.Manifest
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
import android.os.Build
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.view.ActionMode
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
@ -32,49 +32,49 @@ import androidx.lifecycle.ViewModelProvider
|
|||||||
import androidx.lifecycle.ViewModelProviders
|
import androidx.lifecycle.ViewModelProviders
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import io.reactivex.disposables.CompositeDisposable
|
||||||
import kotlinx.android.synthetic.main.zim_list.file_management_no_files
|
import kotlinx.android.synthetic.main.zim_list.file_management_no_files
|
||||||
import kotlinx.android.synthetic.main.zim_list.zim_swiperefresh
|
import kotlinx.android.synthetic.main.zim_list.zim_swiperefresh
|
||||||
import kotlinx.android.synthetic.main.zim_list.zimfilelist
|
import kotlinx.android.synthetic.main.zim_list.zimfilelist
|
||||||
import org.kiwix.kiwixmobile.R
|
import org.kiwix.kiwixmobile.R
|
||||||
import org.kiwix.kiwixmobile.R.string
|
|
||||||
import org.kiwix.kiwixmobile.base.BaseFragment
|
import org.kiwix.kiwixmobile.base.BaseFragment
|
||||||
import org.kiwix.kiwixmobile.data.ZimContentProvider
|
|
||||||
import org.kiwix.kiwixmobile.database.newdb.dao.NewBookDao
|
|
||||||
import org.kiwix.kiwixmobile.di.components.ActivityComponent
|
import org.kiwix.kiwixmobile.di.components.ActivityComponent
|
||||||
import org.kiwix.kiwixmobile.extensions.toast
|
import org.kiwix.kiwixmobile.extensions.toast
|
||||||
import org.kiwix.kiwixmobile.utils.BookUtils
|
|
||||||
import org.kiwix.kiwixmobile.utils.Constants.REQUEST_STORAGE_PERMISSION
|
import org.kiwix.kiwixmobile.utils.Constants.REQUEST_STORAGE_PERMISSION
|
||||||
import org.kiwix.kiwixmobile.utils.DialogShower
|
|
||||||
import org.kiwix.kiwixmobile.utils.KiwixDialog.DeleteZim
|
|
||||||
import org.kiwix.kiwixmobile.utils.LanguageUtils
|
import org.kiwix.kiwixmobile.utils.LanguageUtils
|
||||||
import org.kiwix.kiwixmobile.utils.SharedPreferenceUtil
|
import org.kiwix.kiwixmobile.utils.SharedPreferenceUtil
|
||||||
import org.kiwix.kiwixmobile.utils.files.FileUtils
|
|
||||||
import org.kiwix.kiwixmobile.zim_manager.ZimManageActivity
|
|
||||||
import org.kiwix.kiwixmobile.zim_manager.ZimManageViewModel
|
import org.kiwix.kiwixmobile.zim_manager.ZimManageViewModel
|
||||||
|
import org.kiwix.kiwixmobile.zim_manager.ZimManageViewModel.FileSelectActions
|
||||||
|
import org.kiwix.kiwixmobile.zim_manager.ZimManageViewModel.FileSelectActions.RequestMultiSelection
|
||||||
|
import org.kiwix.kiwixmobile.zim_manager.ZimManageViewModel.FileSelectActions.RequestOpen
|
||||||
|
import org.kiwix.kiwixmobile.zim_manager.ZimManageViewModel.FileSelectActions.RequestSelect
|
||||||
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.adapter.BookOnDiskDelegate.BookDelegate
|
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.adapter.BookOnDiskDelegate.BookDelegate
|
||||||
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.adapter.BookOnDiskDelegate.LanguageDelegate
|
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.adapter.BookOnDiskDelegate.LanguageDelegate
|
||||||
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.adapter.BooksOnDiskAdapter
|
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.adapter.BooksOnDiskAdapter
|
||||||
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.adapter.BooksOnDiskListItem.BookOnDisk
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class ZimFileSelectFragment : BaseFragment() {
|
class ZimFileSelectFragment : BaseFragment() {
|
||||||
|
|
||||||
@Inject lateinit var sharedPreferenceUtil: SharedPreferenceUtil
|
@Inject lateinit var sharedPreferenceUtil: SharedPreferenceUtil
|
||||||
@Inject lateinit var bookDao: NewBookDao
|
|
||||||
@Inject lateinit var dialogShower: DialogShower
|
|
||||||
@Inject lateinit var viewModelFactory: ViewModelProvider.Factory
|
@Inject lateinit var viewModelFactory: ViewModelProvider.Factory
|
||||||
@Inject lateinit var bookUtils: BookUtils
|
|
||||||
|
private var actionMode: ActionMode? = null
|
||||||
|
val disposable = CompositeDisposable()
|
||||||
|
|
||||||
private val zimManageViewModel: ZimManageViewModel by lazy {
|
private val zimManageViewModel: ZimManageViewModel by lazy {
|
||||||
ViewModelProviders.of(activity!!, viewModelFactory)
|
ViewModelProviders.of(activity!!, viewModelFactory)
|
||||||
.get(ZimManageViewModel::class.java)
|
.get(ZimManageViewModel::class.java)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val bookDelegate: BookDelegate by lazy {
|
||||||
|
BookDelegate(sharedPreferenceUtil,
|
||||||
|
{ offerAction(RequestOpen(it)) },
|
||||||
|
{ offerAction(RequestMultiSelection(it)) },
|
||||||
|
{ offerAction(RequestSelect(it)) })
|
||||||
|
}
|
||||||
|
|
||||||
private val booksOnDiskAdapter: BooksOnDiskAdapter by lazy {
|
private val booksOnDiskAdapter: BooksOnDiskAdapter by lazy {
|
||||||
BooksOnDiskAdapter(
|
BooksOnDiskAdapter(bookDelegate, LanguageDelegate)
|
||||||
BookDelegate(sharedPreferenceUtil, this::openOnClick, this::deleteOnLongClick),
|
|
||||||
LanguageDelegate
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun inject(activityComponent: ActivityComponent) {
|
override fun inject(activityComponent: ActivityComponent) {
|
||||||
@ -101,31 +101,51 @@ class ZimFileSelectFragment : BaseFragment() {
|
|||||||
layoutManager = LinearLayoutManager(context, RecyclerView.VERTICAL, false)
|
layoutManager = LinearLayoutManager(context, RecyclerView.VERTICAL, false)
|
||||||
setHasFixedSize(true)
|
setHasFixedSize(true)
|
||||||
}
|
}
|
||||||
zimManageViewModel.bookItems.observe(this, Observer {
|
zimManageViewModel.fileSelectListStates.observe(this, Observer { render(it) })
|
||||||
booksOnDiskAdapter.itemList = it!!
|
disposable.add(sideEffects())
|
||||||
checkEmpty(it)
|
|
||||||
})
|
|
||||||
zimManageViewModel.deviceListIsRefreshing.observe(this, Observer {
|
zimManageViewModel.deviceListIsRefreshing.observe(this, Observer {
|
||||||
zim_swiperefresh.isRefreshing = it!!
|
zim_swiperefresh.isRefreshing = it!!
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun sideEffects() = zimManageViewModel.sideEffects.subscribe(
|
||||||
|
{
|
||||||
|
val effectResult = it.invokeWith(activity!!)
|
||||||
|
if (effectResult is ActionMode) {
|
||||||
|
actionMode = effectResult
|
||||||
|
}
|
||||||
|
}, Throwable::printStackTrace
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun render(state: FileSelectListState) {
|
||||||
|
val items = state.bookOnDiskListItems
|
||||||
|
bookDelegate.selectionMode = state.selectionMode
|
||||||
|
booksOnDiskAdapter.items = items
|
||||||
|
actionMode?.title = String.format("%d", state.selectedBooks.size)
|
||||||
|
file_management_no_files.visibility = if (items.isEmpty()) View.VISIBLE else View.GONE
|
||||||
|
}
|
||||||
|
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
super.onResume()
|
super.onResume()
|
||||||
checkPermissions()
|
checkPermissions()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun checkEmpty(books: List<Any>) {
|
override fun onDestroy() {
|
||||||
file_management_no_files.visibility =
|
super.onDestroy()
|
||||||
if (books.isEmpty()) View.VISIBLE
|
disposable.clear()
|
||||||
else View.GONE
|
}
|
||||||
|
|
||||||
|
private fun offerAction(
|
||||||
|
action: FileSelectActions
|
||||||
|
) {
|
||||||
|
zimManageViewModel.fileSelectActions.offer(action)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun checkPermissions() {
|
private fun checkPermissions() {
|
||||||
if (ContextCompat.checkSelfPermission(
|
if (ContextCompat.checkSelfPermission(
|
||||||
activity!!,
|
activity!!,
|
||||||
Manifest.permission.WRITE_EXTERNAL_STORAGE
|
Manifest.permission.WRITE_EXTERNAL_STORAGE
|
||||||
) != PackageManager.PERMISSION_GRANTED && Build.VERSION.SDK_INT > 18
|
) != PackageManager.PERMISSION_GRANTED
|
||||||
) {
|
) {
|
||||||
context.toast(R.string.request_storage)
|
context.toast(R.string.request_storage)
|
||||||
requestPermissions(
|
requestPermissions(
|
||||||
@ -140,34 +160,4 @@ class ZimFileSelectFragment : BaseFragment() {
|
|||||||
private fun requestFileSystemCheck() {
|
private fun requestFileSystemCheck() {
|
||||||
zimManageViewModel.requestFileSystemCheck.onNext(Unit)
|
zimManageViewModel.requestFileSystemCheck.onNext(Unit)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun openOnClick(it: BookOnDisk) {
|
|
||||||
val file = it.file
|
|
||||||
ZimContentProvider.canIterate = false
|
|
||||||
if (!file.canRead()) {
|
|
||||||
context.toast(string.error_filenotfound)
|
|
||||||
} else {
|
|
||||||
(activity as ZimManageActivity).finishResult(file.path)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun deleteOnLongClick(it: BookOnDisk) {
|
|
||||||
dialogShower.show(DeleteZim, {
|
|
||||||
if (deleteSpecificZimFile(it)) {
|
|
||||||
context.toast(string.delete_specific_zim_toast)
|
|
||||||
} else {
|
|
||||||
context.toast(string.delete_zim_failed)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun deleteSpecificZimFile(book: BookOnDisk): Boolean {
|
|
||||||
val file = book.file
|
|
||||||
FileUtils.deleteZimFile(file.path)
|
|
||||||
if (file.exists()) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
bookDao.delete(book.databaseId!!)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -18,11 +18,13 @@
|
|||||||
package org.kiwix.kiwixmobile.zim_manager.fileselect_view.adapter
|
package org.kiwix.kiwixmobile.zim_manager.fileselect_view.adapter
|
||||||
|
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
|
import androidx.recyclerview.widget.RecyclerView.ViewHolder
|
||||||
import org.kiwix.kiwixmobile.R
|
import org.kiwix.kiwixmobile.R
|
||||||
import org.kiwix.kiwixmobile.extensions.inflate
|
import org.kiwix.kiwixmobile.extensions.inflate
|
||||||
import org.kiwix.kiwixmobile.utils.SharedPreferenceUtil
|
import org.kiwix.kiwixmobile.utils.SharedPreferenceUtil
|
||||||
|
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.SelectionMode
|
||||||
|
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.SelectionMode.NORMAL
|
||||||
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.adapter.BookOnDiskViewHolder.BookViewHolder
|
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.adapter.BookOnDiskViewHolder.BookViewHolder
|
||||||
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.adapter.BookOnDiskViewHolder.LanguageItemViewHolder
|
|
||||||
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.adapter.BooksOnDiskListItem.BookOnDisk
|
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.adapter.BooksOnDiskListItem.BookOnDisk
|
||||||
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.adapter.BooksOnDiskListItem.LanguageItem
|
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.adapter.BooksOnDiskListItem.LanguageItem
|
||||||
import org.kiwix.kiwixmobile.zim_manager.library_view.adapter.base.AbsDelegateAdapter
|
import org.kiwix.kiwixmobile.zim_manager.library_view.adapter.base.AbsDelegateAdapter
|
||||||
@ -33,17 +35,28 @@ sealed class BookOnDiskDelegate<I : BooksOnDiskListItem, VH : BookOnDiskViewHold
|
|||||||
class BookDelegate(
|
class BookDelegate(
|
||||||
val sharedPreferenceUtil: SharedPreferenceUtil,
|
val sharedPreferenceUtil: SharedPreferenceUtil,
|
||||||
val clickAction: (BookOnDisk) -> Unit,
|
val clickAction: (BookOnDisk) -> Unit,
|
||||||
val longClickAction: ((BookOnDisk) -> Unit)? = null
|
val longClickAction: ((BookOnDisk) -> Unit)? = null,
|
||||||
|
val multiSelectAction: ((BookOnDisk) -> Unit)? = null
|
||||||
) : BookOnDiskDelegate<BookOnDisk, BookViewHolder>() {
|
) : BookOnDiskDelegate<BookOnDisk, BookViewHolder>() {
|
||||||
|
|
||||||
override val itemClass = BookOnDisk::class.java
|
override val itemClass = BookOnDisk::class.java
|
||||||
|
|
||||||
|
var selectionMode: SelectionMode = NORMAL
|
||||||
|
|
||||||
|
override fun bind(
|
||||||
|
viewHolder: ViewHolder,
|
||||||
|
itemToBind: BooksOnDiskListItem
|
||||||
|
) {
|
||||||
|
(viewHolder as BookOnDiskViewHolder.BookViewHolder).bind((itemToBind as BookOnDisk), selectionMode)
|
||||||
|
}
|
||||||
|
|
||||||
override fun createViewHolder(parent: ViewGroup) =
|
override fun createViewHolder(parent: ViewGroup) =
|
||||||
BookViewHolder(
|
BookViewHolder(
|
||||||
parent.inflate(R.layout.item_book, false),
|
parent.inflate(R.layout.item_book, false),
|
||||||
sharedPreferenceUtil,
|
sharedPreferenceUtil,
|
||||||
clickAction,
|
clickAction,
|
||||||
longClickAction
|
longClickAction,
|
||||||
|
multiSelectAction
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package org.kiwix.kiwixmobile.zim_manager.fileselect_view.adapter
|
package org.kiwix.kiwixmobile.zim_manager.fileselect_view.adapter
|
||||||
|
|
||||||
import org.kiwix.kiwixmobile.zim_manager.library_view.adapter.base.AdapterDelegate
|
import org.kiwix.kiwixmobile.zim_manager.library_view.adapter.base.AdapterDelegate
|
||||||
import org.kiwix.kiwixmobile.zim_manager.library_view.adapter.base.AdapterDelegateManager
|
|
||||||
import org.kiwix.kiwixmobile.zim_manager.library_view.adapter.base.BaseDelegateAdapter
|
import org.kiwix.kiwixmobile.zim_manager.library_view.adapter.base.BaseDelegateAdapter
|
||||||
|
|
||||||
class BooksOnDiskAdapter(
|
class BooksOnDiskAdapter(
|
||||||
|
@ -6,6 +6,7 @@ import java.io.File
|
|||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
|
|
||||||
sealed class BooksOnDiskListItem {
|
sealed class BooksOnDiskListItem {
|
||||||
|
var isSelected: Boolean = false
|
||||||
abstract val id: Long
|
abstract val id: Long
|
||||||
|
|
||||||
data class LanguageItem constructor(
|
data class LanguageItem constructor(
|
||||||
|
@ -3,6 +3,7 @@ package org.kiwix.kiwixmobile.zim_manager.fileselect_view.adapter
|
|||||||
import android.graphics.ColorMatrixColorFilter
|
import android.graphics.ColorMatrixColorFilter
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import kotlinx.android.synthetic.main.header_language.header_language
|
import kotlinx.android.synthetic.main.header_language.header_language
|
||||||
|
import kotlinx.android.synthetic.main.item_book.itemBookCheckbox
|
||||||
import kotlinx.android.synthetic.main.item_book.item_book_article_count
|
import kotlinx.android.synthetic.main.item_book.item_book_article_count
|
||||||
import kotlinx.android.synthetic.main.item_book.item_book_date
|
import kotlinx.android.synthetic.main.item_book.item_book_date
|
||||||
import kotlinx.android.synthetic.main.item_book.item_book_description
|
import kotlinx.android.synthetic.main.item_book.item_book_description
|
||||||
@ -17,6 +18,9 @@ import org.kiwix.kiwixmobile.main.KiwixWebView
|
|||||||
import org.kiwix.kiwixmobile.utils.SharedPreferenceUtil
|
import org.kiwix.kiwixmobile.utils.SharedPreferenceUtil
|
||||||
import org.kiwix.kiwixmobile.zim_manager.KiloByte
|
import org.kiwix.kiwixmobile.zim_manager.KiloByte
|
||||||
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.ArticleCount
|
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.ArticleCount
|
||||||
|
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.SelectionMode
|
||||||
|
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.SelectionMode.MULTI
|
||||||
|
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.SelectionMode.NORMAL
|
||||||
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.adapter.BooksOnDiskListItem.BookOnDisk
|
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.adapter.BooksOnDiskListItem.BookOnDisk
|
||||||
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.adapter.BooksOnDiskListItem.LanguageItem
|
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.adapter.BooksOnDiskListItem.LanguageItem
|
||||||
import org.kiwix.kiwixmobile.zim_manager.library_view.adapter.base.BaseViewHolder
|
import org.kiwix.kiwixmobile.zim_manager.library_view.adapter.base.BaseViewHolder
|
||||||
@ -28,10 +32,17 @@ sealed class BookOnDiskViewHolder<T : BooksOnDiskListItem>(containerView: View)
|
|||||||
containerView: View,
|
containerView: View,
|
||||||
private val sharedPreferenceUtil: SharedPreferenceUtil,
|
private val sharedPreferenceUtil: SharedPreferenceUtil,
|
||||||
private val clickAction: (BookOnDisk) -> Unit,
|
private val clickAction: (BookOnDisk) -> Unit,
|
||||||
private val longClickAction: ((BookOnDisk) -> Unit)?
|
private val longClickAction: ((BookOnDisk) -> Unit)?,
|
||||||
|
private val multiSelectAction: ((BookOnDisk) -> Unit)?
|
||||||
) : BookOnDiskViewHolder<BookOnDisk>(containerView) {
|
) : BookOnDiskViewHolder<BookOnDisk>(containerView) {
|
||||||
|
|
||||||
override fun bind(item: BookOnDisk) {
|
override fun bind(item: BookOnDisk) {
|
||||||
|
}
|
||||||
|
|
||||||
|
fun bind(
|
||||||
|
item: BookOnDisk,
|
||||||
|
selectionMode: SelectionMode
|
||||||
|
) {
|
||||||
val book = item.book
|
val book = item.book
|
||||||
item_book_title.text = book.getTitle()
|
item_book_title.text = book.getTitle()
|
||||||
item_book_date.text = book.getDate()
|
item_book_date.text = book.getDate()
|
||||||
@ -59,24 +70,30 @@ sealed class BookOnDiskViewHolder<T : BooksOnDiskListItem>(containerView: View)
|
|||||||
item_book_label_video.visibility = View.GONE
|
item_book_label_video.visibility = View.GONE
|
||||||
}
|
}
|
||||||
|
|
||||||
containerView.setOnClickListener {
|
itemBookCheckbox.isChecked = item.isSelected
|
||||||
clickAction.invoke(item)
|
when (selectionMode) {
|
||||||
}
|
MULTI -> {
|
||||||
containerView.setOnLongClickListener {
|
itemBookCheckbox.visibility = View.VISIBLE
|
||||||
longClickAction?.invoke(item)
|
containerView.setOnClickListener { multiSelectAction?.invoke(item) }
|
||||||
return@setOnLongClickListener true
|
containerView.setOnLongClickListener(null)
|
||||||
|
}
|
||||||
|
NORMAL -> {
|
||||||
|
itemBookCheckbox.visibility = View.GONE
|
||||||
|
containerView.setOnClickListener { clickAction.invoke(item) }
|
||||||
|
containerView.setOnLongClickListener {
|
||||||
|
longClickAction?.invoke(item)
|
||||||
|
return@setOnLongClickListener true
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class LanguageItemViewHolder(containerView: View) :
|
|
||||||
BookOnDiskViewHolder<LanguageItem>(containerView) {
|
|
||||||
|
|
||||||
override fun bind(item: LanguageItem) {
|
|
||||||
header_language.text = item.text
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class LanguageItemViewHolder(containerView: View) :
|
||||||
|
BookOnDiskViewHolder<LanguageItem>(containerView) {
|
||||||
|
|
||||||
|
override fun bind(item: LanguageItem) {
|
||||||
|
header_language.text = item.text
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -0,0 +1,42 @@
|
|||||||
|
package org.kiwix.kiwixmobile.zim_manager.fileselect_view.effects
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import org.kiwix.kiwixmobile.R.string
|
||||||
|
import org.kiwix.kiwixmobile.database.newdb.dao.NewBookDao
|
||||||
|
import org.kiwix.kiwixmobile.extensions.toast
|
||||||
|
import org.kiwix.kiwixmobile.utils.DialogShower
|
||||||
|
import org.kiwix.kiwixmobile.utils.KiwixDialog.DeleteZim
|
||||||
|
import org.kiwix.kiwixmobile.utils.files.FileUtils
|
||||||
|
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.adapter.BooksOnDiskListItem.BookOnDisk
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class DeleteFiles(val booksOnDiskListItem: List<BookOnDisk>) :
|
||||||
|
SideEffect<Unit> {
|
||||||
|
|
||||||
|
@Inject lateinit var dialogShower: DialogShower
|
||||||
|
@Inject lateinit var newBookDao: NewBookDao
|
||||||
|
|
||||||
|
override fun invokeWith(activity: Activity) {
|
||||||
|
activityComponent(activity).inject(this)
|
||||||
|
booksOnDiskListItem.forEach {
|
||||||
|
dialogShower.show(DeleteZim, {
|
||||||
|
if (deleteSpecificZimFile(it)) {
|
||||||
|
activity.toast(string.delete_specific_zim_toast)
|
||||||
|
} else {
|
||||||
|
activity.toast(string.delete_zim_failed)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun deleteSpecificZimFile(book: BookOnDisk): Boolean {
|
||||||
|
val file = book.file
|
||||||
|
FileUtils.deleteZimFile(file.path)
|
||||||
|
if (file.exists()) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
newBookDao.delete(book.databaseId!!)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,8 @@
|
|||||||
|
package org.kiwix.kiwixmobile.zim_manager.fileselect_view.effects
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
|
||||||
|
object None : SideEffect<Unit> {
|
||||||
|
override fun invokeWith(activity: Activity) {
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* Kiwix Android
|
||||||
|
* Copyright (C) 2018 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.fileselect_view.effects
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import org.kiwix.kiwixmobile.R
|
||||||
|
import org.kiwix.kiwixmobile.data.ZimContentProvider
|
||||||
|
import org.kiwix.kiwixmobile.extensions.toast
|
||||||
|
import org.kiwix.kiwixmobile.zim_manager.ZimManageActivity
|
||||||
|
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.adapter.BooksOnDiskListItem.BookOnDisk
|
||||||
|
|
||||||
|
class OpenFile(val bookOnDisk: BookOnDisk): SideEffect<Unit> {
|
||||||
|
|
||||||
|
override fun invokeWith(activity: Activity) {
|
||||||
|
val file = bookOnDisk.file
|
||||||
|
ZimContentProvider.canIterate = false
|
||||||
|
if (!file.canRead()) {
|
||||||
|
activity.toast(R.string.error_filenotfound)
|
||||||
|
} else {
|
||||||
|
(activity as ZimManageActivity).finishResult(file.path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,42 @@
|
|||||||
|
package org.kiwix.kiwixmobile.zim_manager.fileselect_view.effects
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.content.Intent
|
||||||
|
import android.net.Uri
|
||||||
|
import android.os.Build
|
||||||
|
import androidx.core.content.FileProvider
|
||||||
|
import org.kiwix.kiwixmobile.BuildConfig
|
||||||
|
import org.kiwix.kiwixmobile.R
|
||||||
|
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.adapter.BooksOnDiskListItem.BookOnDisk
|
||||||
|
|
||||||
|
class ShareFiles(val selectedBooks: List<BookOnDisk>) : SideEffect<Unit> {
|
||||||
|
override fun invokeWith(activity: Activity) {
|
||||||
|
val selectedFileShareIntent = Intent()
|
||||||
|
selectedFileShareIntent.action = Intent.ACTION_SEND_MULTIPLE
|
||||||
|
selectedFileShareIntent.type = "application/octet-stream"
|
||||||
|
val selectedFileContentURIs = selectedBooks.mapNotNull {
|
||||||
|
if (Build.VERSION.SDK_INT >= 24) {
|
||||||
|
FileProvider.getUriForFile(
|
||||||
|
activity,
|
||||||
|
BuildConfig.APPLICATION_ID + ".fileprovider",
|
||||||
|
it.file
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
Uri.fromFile(it.file)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
selectedFileShareIntent.putParcelableArrayListExtra(
|
||||||
|
Intent.EXTRA_STREAM,
|
||||||
|
ArrayList(selectedFileContentURIs)
|
||||||
|
)
|
||||||
|
selectedFileShareIntent.flags = Intent.FLAG_GRANT_READ_URI_PERMISSION
|
||||||
|
val shareChooserIntent = Intent.createChooser(
|
||||||
|
selectedFileShareIntent,
|
||||||
|
activity.getString(R.string.selected_file_cab_app_chooser_title)
|
||||||
|
)
|
||||||
|
if (shareChooserIntent.resolveActivity(activity.getPackageManager()) != null) {
|
||||||
|
activity.startActivity(shareChooserIntent) // Open the app chooser dialog
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,28 @@
|
|||||||
|
/*
|
||||||
|
* Kiwix Android
|
||||||
|
* Copyright (C) 2018 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.fileselect_view.effects
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import org.kiwix.kiwixmobile.KiwixApplication
|
||||||
|
|
||||||
|
interface SideEffect<T:Any?> {
|
||||||
|
fun invokeWith(activity: Activity):T
|
||||||
|
fun activityComponent(activity: Activity) =
|
||||||
|
KiwixApplication.getApplicationComponent().activityComponent().activity(activity).build()
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,27 @@
|
|||||||
|
package org.kiwix.kiwixmobile.zim_manager.fileselect_view.effects
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.view.ActionMode
|
||||||
|
import io.reactivex.processors.PublishProcessor
|
||||||
|
import org.kiwix.kiwixmobile.R
|
||||||
|
import org.kiwix.kiwixmobile.extensions.startActionMode
|
||||||
|
import org.kiwix.kiwixmobile.zim_manager.ZimManageViewModel.FileSelectActions
|
||||||
|
import org.kiwix.kiwixmobile.zim_manager.ZimManageViewModel.FileSelectActions.RequestDeleteMultiSelection
|
||||||
|
import org.kiwix.kiwixmobile.zim_manager.ZimManageViewModel.FileSelectActions.RequestShareMultiSelection
|
||||||
|
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.adapter.BooksOnDiskListItem
|
||||||
|
|
||||||
|
data class StartMultiSelection(
|
||||||
|
val bookOnDisk: BooksOnDiskListItem.BookOnDisk,
|
||||||
|
val fileSelectActions: PublishProcessor<FileSelectActions>
|
||||||
|
) : SideEffect<ActionMode?> {
|
||||||
|
override fun invokeWith(activity: Activity) =
|
||||||
|
activity.startActionMode(
|
||||||
|
R.menu.menu_zim_files_contextual,
|
||||||
|
mapOf(
|
||||||
|
R.id.zim_file_delete_item to { fileSelectActions.offer(RequestDeleteMultiSelection) },
|
||||||
|
R.id.zim_file_share_item to { fileSelectActions.offer(RequestShareMultiSelection) }
|
||||||
|
),
|
||||||
|
{ fileSelectActions.offer(FileSelectActions.MultiModeFinished) }
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
@ -140,7 +140,7 @@ class LibraryFragment : BaseFragment() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun onLibraryItemsChange(it: List<LibraryListItem>?) {
|
private fun onLibraryItemsChange(it: List<LibraryListItem>?) {
|
||||||
libraryAdapter.itemList = it!!
|
libraryAdapter.items = it!!
|
||||||
if (it.isEmpty()) {
|
if (it.isEmpty()) {
|
||||||
libraryErrorText.setText(
|
libraryErrorText.setText(
|
||||||
if (isNotConnected) R.string.no_network_connection
|
if (isNotConnected) R.string.no_network_connection
|
||||||
|
@ -30,7 +30,7 @@ abstract class BaseDelegateAdapter<ITEM>(
|
|||||||
setHasStableIds(true)
|
setHasStableIds(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
var itemList: List<ITEM> = mutableListOf()
|
var items: List<ITEM> = mutableListOf()
|
||||||
set(value) {
|
set(value) {
|
||||||
field = value
|
field = value
|
||||||
notifyDataSetChanged()
|
notifyDataSetChanged()
|
||||||
@ -41,19 +41,19 @@ abstract class BaseDelegateAdapter<ITEM>(
|
|||||||
viewType: Int
|
viewType: Int
|
||||||
) = delegateManager.createViewHolder(parent, viewType)
|
) = delegateManager.createViewHolder(parent, viewType)
|
||||||
|
|
||||||
override fun getItemCount() = itemList.size
|
override fun getItemCount() = items.size
|
||||||
override fun onBindViewHolder(
|
override fun onBindViewHolder(
|
||||||
holder: ViewHolder,
|
holder: ViewHolder,
|
||||||
position: Int
|
position: Int
|
||||||
) {
|
) {
|
||||||
delegateManager.onBindViewHolder(itemList[position], holder)
|
delegateManager.onBindViewHolder(items[position], holder)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getItemViewType(position: Int) =
|
override fun getItemViewType(position: Int) =
|
||||||
delegateManager.getViewTypeFor(itemList[position])
|
delegateManager.getViewTypeFor(items[position])
|
||||||
|
|
||||||
override fun getItemId(position: Int): Long {
|
override fun getItemId(position: Int): Long {
|
||||||
return getIdFor(itemList[position])
|
return getIdFor(items[position])
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract fun getIdFor(item:ITEM):Long
|
abstract fun getIdFor(item:ITEM):Long
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
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="?attr/selectableItemBackground"
|
android:background="?android:attr/selectableItemBackground"
|
||||||
android:paddingEnd="0dp"
|
android:paddingEnd="0dp"
|
||||||
android:paddingLeft="0dp"
|
android:paddingLeft="0dp"
|
||||||
android:paddingRight="0dp"
|
android:paddingRight="0dp"
|
||||||
@ -12,13 +12,25 @@
|
|||||||
android:paddingTop="@dimen/activity_vertical_margin"
|
android:paddingTop="@dimen/activity_vertical_margin"
|
||||||
>
|
>
|
||||||
|
|
||||||
|
<CheckBox
|
||||||
|
android:id="@+id/itemBookCheckbox"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:visibility="gone"
|
||||||
|
android:paddingRight="10dp"
|
||||||
|
android:paddingEnd="10dp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
/>
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/item_book_icon"
|
android:id="@+id/item_book_icon"
|
||||||
android:layout_width="40dp"
|
android:layout_width="40dp"
|
||||||
android:layout_height="40dp"
|
android:layout_height="40dp"
|
||||||
android:contentDescription="@string/favicon"
|
android:contentDescription="@string/favicon"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toEndOf="@id/itemBookCheckbox"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
tools:src="@mipmap/kiwix_icon_round"
|
tools:src="@mipmap/kiwix_icon_round"
|
||||||
/>
|
/>
|
||||||
|
@ -1 +0,0 @@
|
|||||||
values-id
|
|
@ -1 +0,0 @@
|
|||||||
values-he
|
|
@ -1 +0,0 @@
|
|||||||
values-yi
|
|
@ -27,11 +27,31 @@ import java.io.File
|
|||||||
|
|
||||||
fun bookOnDisk(
|
fun bookOnDisk(
|
||||||
book: Book = book(),
|
book: Book = book(),
|
||||||
databaseId: Long = 0L,
|
databaseId: Long? = 0L,
|
||||||
file: File = File("")
|
file: File = File("")
|
||||||
) = BookOnDisk(databaseId, book, file)
|
) = BookOnDisk(databaseId, book, file)
|
||||||
|
|
||||||
fun book(id: String = "0") = Book().apply { this.id = id }
|
fun book(
|
||||||
|
id: String = "0",
|
||||||
|
title: String = "",
|
||||||
|
size: String = "",
|
||||||
|
favicon: String = "",
|
||||||
|
creator: String = "",
|
||||||
|
publisher: String = "",
|
||||||
|
date: String = "",
|
||||||
|
description: String = "",
|
||||||
|
language: String = ""
|
||||||
|
) = Book().apply {
|
||||||
|
this.id = id
|
||||||
|
this.title = title
|
||||||
|
this.size = size
|
||||||
|
this.favicon = favicon
|
||||||
|
this.creator = creator
|
||||||
|
this.publisher = publisher
|
||||||
|
this.date = date
|
||||||
|
this.description = description
|
||||||
|
this.language = language
|
||||||
|
}
|
||||||
|
|
||||||
fun downloadStatus(
|
fun downloadStatus(
|
||||||
downloadId: Long = 0L,
|
downloadId: Long = 0L,
|
||||||
|
@ -0,0 +1,34 @@
|
|||||||
|
/*
|
||||||
|
* Kiwix Android
|
||||||
|
* Copyright (C) 2018 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
|
||||||
|
|
||||||
|
import io.reactivex.Scheduler
|
||||||
|
import io.reactivex.android.plugins.RxAndroidPlugins
|
||||||
|
import io.reactivex.plugins.RxJavaPlugins
|
||||||
|
|
||||||
|
fun setScheduler(replacementScheduler: Scheduler) {
|
||||||
|
RxJavaPlugins.setIoSchedulerHandler { scheduler -> replacementScheduler }
|
||||||
|
RxJavaPlugins.setComputationSchedulerHandler { scheduler -> replacementScheduler }
|
||||||
|
RxJavaPlugins.setNewThreadSchedulerHandler { scheduler -> replacementScheduler }
|
||||||
|
RxAndroidPlugins.setInitMainThreadSchedulerHandler { scheduler -> replacementScheduler }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun resetSchedulers() {
|
||||||
|
RxJavaPlugins.reset()
|
||||||
|
RxAndroidPlugins.reset()
|
||||||
|
}
|
@ -0,0 +1,139 @@
|
|||||||
|
/*
|
||||||
|
* Kiwix Android
|
||||||
|
* Copyright (C) 2018 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.utils.files
|
||||||
|
|
||||||
|
import android.content.ContentResolver
|
||||||
|
import android.content.Context
|
||||||
|
import android.database.Cursor
|
||||||
|
import android.os.Environment
|
||||||
|
import android.provider.MediaStore.MediaColumns
|
||||||
|
import eu.mhutti1.utils.storage.StorageDevice
|
||||||
|
import eu.mhutti1.utils.storage.StorageDeviceUtils
|
||||||
|
import io.mockk.clearMocks
|
||||||
|
import io.mockk.every
|
||||||
|
import io.mockk.mockk
|
||||||
|
import io.mockk.mockkStatic
|
||||||
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
|
import org.junit.jupiter.api.AfterAll
|
||||||
|
import org.junit.jupiter.api.BeforeEach
|
||||||
|
import org.junit.jupiter.api.Nested
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
class FileSearchTest {
|
||||||
|
|
||||||
|
private val context: Context = mockk()
|
||||||
|
private lateinit var fileSearch: FileSearch
|
||||||
|
|
||||||
|
private val externalStorageDirectory: File = mockk()
|
||||||
|
private val contentResolver: ContentResolver = mockk()
|
||||||
|
private val storageDevice: StorageDevice = mockk()
|
||||||
|
|
||||||
|
private val unitTestTempDirectoryPath = "unittest${File.separator}"
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
fun init() {
|
||||||
|
clearMocks(context, externalStorageDirectory, contentResolver, storageDevice)
|
||||||
|
deleteTempDirectory()
|
||||||
|
mockkStatic(StorageDeviceUtils::class)
|
||||||
|
mockkStatic(Environment::class)
|
||||||
|
every { Environment.getExternalStorageDirectory() } returns externalStorageDirectory
|
||||||
|
every { externalStorageDirectory.absolutePath } returns "/externalStorageDirectory"
|
||||||
|
every { context.contentResolver } returns contentResolver
|
||||||
|
every { StorageDeviceUtils.getStorageDevices(context, false) } returns arrayListOf(
|
||||||
|
storageDevice
|
||||||
|
)
|
||||||
|
every { storageDevice.name } returns "/deviceDir"
|
||||||
|
fileSearch = FileSearch(context)
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterAll
|
||||||
|
fun teardown() {
|
||||||
|
deleteTempDirectory()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
inner class FileSystem {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `scan of directory that doesn't exist returns nothing`() {
|
||||||
|
every { contentResolver.query(any(), any(), any(), any(), any()) } returns null
|
||||||
|
fileSearch.scan("doesNotExist")
|
||||||
|
.test()
|
||||||
|
.assertValue(listOf())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `scan of directory that has files returns files`() {
|
||||||
|
val zimFile = File.createTempFile("${unitTestTempDirectoryPath}fileToFind", ".zim")
|
||||||
|
val zimaaFile = File.createTempFile("${unitTestTempDirectoryPath}fileToFind2", ".zimaa")
|
||||||
|
File.createTempFile("${unitTestTempDirectoryPath}willNotFind", ".txt")
|
||||||
|
every { contentResolver.query(any(), any(), any(), any(), any()) } returns null
|
||||||
|
val fileList = fileSearch.scan(zimFile.parent)
|
||||||
|
.test()
|
||||||
|
.values()[0]
|
||||||
|
assertThat(fileList).containsExactlyInAnyOrder(zimFile, zimaaFile)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
inner class MediaStore {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `scan media store, if files are readable they are returned`() {
|
||||||
|
val fileToFind = File.createTempFile("${unitTestTempDirectoryPath}fileToFind", ".zim")
|
||||||
|
expectFromMediaStore(fileToFind)
|
||||||
|
fileSearch.scan("")
|
||||||
|
.test()
|
||||||
|
.assertValue(listOf(fileToFind))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `scan media store, if files are not readable they are not returned`() {
|
||||||
|
val unreadableFile = File.createTempFile("${unitTestTempDirectoryPath}fileToFind", ".zim")
|
||||||
|
expectFromMediaStore(unreadableFile)
|
||||||
|
unreadableFile.delete()
|
||||||
|
fileSearch.scan("")
|
||||||
|
.test()
|
||||||
|
.assertValue(listOf())
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun expectFromMediaStore(fileToFind: File) {
|
||||||
|
val cursor = mockk<Cursor>()
|
||||||
|
every {
|
||||||
|
contentResolver.query(
|
||||||
|
null,
|
||||||
|
arrayOf(MediaColumns.DATA),
|
||||||
|
MediaColumns.DATA + " like ? or " + MediaColumns.DATA + " like ? ",
|
||||||
|
arrayOf("%." + "zim", "%." + "zimaa"),
|
||||||
|
null
|
||||||
|
)
|
||||||
|
} returns cursor
|
||||||
|
every { cursor.moveToNext() } returnsMany listOf(true, false)
|
||||||
|
every { cursor.columnNames } returns arrayOf(MediaColumns.DATA)
|
||||||
|
every { cursor.getColumnIndex(MediaColumns.DATA) } returns 0
|
||||||
|
every { cursor.getString(0) } returns fileToFind.absolutePath
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun deleteTempDirectory() {
|
||||||
|
File.createTempFile("${unitTestTempDirectoryPath}temp", ".txt")
|
||||||
|
.parentFile.deleteRecursively()
|
||||||
|
}
|
||||||
|
}
|
@ -20,16 +20,12 @@ package org.kiwix.kiwixmobile.zim_manager
|
|||||||
|
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
import com.jraska.livedata.test
|
import com.jraska.livedata.test
|
||||||
import io.mockk.clearMocks
|
import io.mockk.clearAllMocks
|
||||||
import io.mockk.every
|
import io.mockk.every
|
||||||
import io.mockk.mockk
|
import io.mockk.mockk
|
||||||
import io.mockk.verify
|
import io.mockk.verify
|
||||||
import io.reactivex.Scheduler
|
|
||||||
import io.reactivex.Single
|
import io.reactivex.Single
|
||||||
import io.reactivex.android.plugins.RxAndroidPlugins
|
|
||||||
import io.reactivex.plugins.RxJavaPlugins
|
|
||||||
import io.reactivex.processors.PublishProcessor
|
import io.reactivex.processors.PublishProcessor
|
||||||
import io.reactivex.schedulers.Schedulers
|
|
||||||
import io.reactivex.schedulers.TestScheduler
|
import io.reactivex.schedulers.TestScheduler
|
||||||
import org.junit.jupiter.api.AfterAll
|
import org.junit.jupiter.api.AfterAll
|
||||||
import org.junit.jupiter.api.BeforeEach
|
import org.junit.jupiter.api.BeforeEach
|
||||||
@ -55,12 +51,15 @@ import org.kiwix.kiwixmobile.downloader.model.DownloadStatus
|
|||||||
import org.kiwix.kiwixmobile.downloader.model.UriToFileConverter
|
import org.kiwix.kiwixmobile.downloader.model.UriToFileConverter
|
||||||
import org.kiwix.kiwixmobile.library.entity.LibraryNetworkEntity
|
import org.kiwix.kiwixmobile.library.entity.LibraryNetworkEntity
|
||||||
import org.kiwix.kiwixmobile.library.entity.LibraryNetworkEntity.Book
|
import org.kiwix.kiwixmobile.library.entity.LibraryNetworkEntity.Book
|
||||||
|
import org.kiwix.kiwixmobile.resetSchedulers
|
||||||
|
import org.kiwix.kiwixmobile.setScheduler
|
||||||
import org.kiwix.kiwixmobile.utils.BookUtils
|
import org.kiwix.kiwixmobile.utils.BookUtils
|
||||||
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.CanWrite4GbFile
|
||||||
import org.kiwix.kiwixmobile.zim_manager.Fat32Checker.FileSystemState.CannotWrite4GbFile
|
import org.kiwix.kiwixmobile.zim_manager.Fat32Checker.FileSystemState.CannotWrite4GbFile
|
||||||
import org.kiwix.kiwixmobile.zim_manager.NetworkState.CONNECTED
|
import org.kiwix.kiwixmobile.zim_manager.NetworkState.CONNECTED
|
||||||
import org.kiwix.kiwixmobile.zim_manager.NetworkState.NOT_CONNECTED
|
import org.kiwix.kiwixmobile.zim_manager.NetworkState.NOT_CONNECTED
|
||||||
|
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.FileSelectListState
|
||||||
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.StorageObserver
|
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.StorageObserver
|
||||||
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.adapter.BooksOnDiskListItem
|
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.adapter.BooksOnDiskListItem
|
||||||
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.adapter.BooksOnDiskListItem.BookOnDisk
|
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.adapter.BooksOnDiskListItem.BookOnDisk
|
||||||
@ -104,26 +103,14 @@ class ZimManageViewModelTest {
|
|||||||
setScheduler(testScheduler)
|
setScheduler(testScheduler)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setScheduler(replacementScheduler: Scheduler) {
|
|
||||||
RxJavaPlugins.setIoSchedulerHandler { scheduler -> replacementScheduler }
|
|
||||||
RxJavaPlugins.setComputationSchedulerHandler { scheduler -> replacementScheduler }
|
|
||||||
RxJavaPlugins.setNewThreadSchedulerHandler { scheduler -> replacementScheduler }
|
|
||||||
RxAndroidPlugins.setInitMainThreadSchedulerHandler { scheduler -> Schedulers.trampoline() }
|
|
||||||
}
|
|
||||||
|
|
||||||
@AfterAll
|
@AfterAll
|
||||||
fun teardown() {
|
fun teardown() {
|
||||||
RxJavaPlugins.reset()
|
resetSchedulers()
|
||||||
RxAndroidPlugins.reset()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
fun init() {
|
fun init() {
|
||||||
clearMocks(
|
clearAllMocks()
|
||||||
newDownloadDao, newBookDao, newLanguagesDao, downloader,
|
|
||||||
storageObserver, kiwixService, application, connectivityBroadcastReceiver, bookUtils,
|
|
||||||
fat32Checker, uriToFileConverter, defaultLanguageProvider, dataSource
|
|
||||||
)
|
|
||||||
every { connectivityBroadcastReceiver.action } returns "test"
|
every { connectivityBroadcastReceiver.action } returns "test"
|
||||||
every { newDownloadDao.downloads() } returns downloads
|
every { newDownloadDao.downloads() } returns downloads
|
||||||
every { newBookDao.books() } returns books
|
every { newBookDao.books() } returns books
|
||||||
@ -235,8 +222,8 @@ class ZimManageViewModelTest {
|
|||||||
val expectedList = listOf(bookOnDisk())
|
val expectedList = listOf(bookOnDisk())
|
||||||
booksOnDiskListItems.onNext(expectedList)
|
booksOnDiskListItems.onNext(expectedList)
|
||||||
testScheduler.triggerActions()
|
testScheduler.triggerActions()
|
||||||
viewModel.bookItems.test()
|
viewModel.fileSelectListStates.test()
|
||||||
.assertValue(expectedList)
|
.assertValue(FileSelectListState(expectedList))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -0,0 +1,149 @@
|
|||||||
|
package org.kiwix.kiwixmobile.zim_manager.fileselect_view
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Kiwix Android
|
||||||
|
* Copyright (C) 2018 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import io.mockk.clearAllMocks
|
||||||
|
import io.mockk.every
|
||||||
|
import io.mockk.mockk
|
||||||
|
import io.mockk.mockkStatic
|
||||||
|
import io.mockk.verify
|
||||||
|
import io.reactivex.processors.PublishProcessor
|
||||||
|
import io.reactivex.schedulers.Schedulers
|
||||||
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
|
import org.junit.jupiter.api.AfterAll
|
||||||
|
import org.junit.jupiter.api.BeforeEach
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
import org.kiwix.kiwixmobile.book
|
||||||
|
import org.kiwix.kiwixmobile.bookOnDisk
|
||||||
|
import org.kiwix.kiwixmobile.data.ZimContentProvider
|
||||||
|
import org.kiwix.kiwixmobile.database.newdb.dao.NewDownloadDao
|
||||||
|
import org.kiwix.kiwixmobile.downloader.model.DownloadModel
|
||||||
|
import org.kiwix.kiwixmobile.library.entity.LibraryNetworkEntity.Book
|
||||||
|
import org.kiwix.kiwixmobile.resetSchedulers
|
||||||
|
import org.kiwix.kiwixmobile.setScheduler
|
||||||
|
import org.kiwix.kiwixmobile.utils.SharedPreferenceUtil
|
||||||
|
import org.kiwix.kiwixmobile.utils.files.FileSearch
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
class StorageObserverTest {
|
||||||
|
|
||||||
|
private val sharedPreferenceUtil: SharedPreferenceUtil = mockk()
|
||||||
|
private val newDownloadDao: NewDownloadDao = mockk()
|
||||||
|
private val fileSearch: FileSearch = mockk()
|
||||||
|
private val downloadModel = mockk<DownloadModel>()
|
||||||
|
private val file = mockk<File>()
|
||||||
|
|
||||||
|
private val files: PublishProcessor<List<File>> = PublishProcessor.create()
|
||||||
|
private val downloads: PublishProcessor<List<DownloadModel>> = PublishProcessor.create()
|
||||||
|
|
||||||
|
private lateinit var storageObserver: StorageObserver
|
||||||
|
|
||||||
|
init {
|
||||||
|
setScheduler(Schedulers.trampoline())
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterAll
|
||||||
|
fun teardown() {
|
||||||
|
resetSchedulers()
|
||||||
|
}
|
||||||
|
|
||||||
|
@BeforeEach fun init() {
|
||||||
|
clearAllMocks()
|
||||||
|
every { sharedPreferenceUtil.prefStorage } returns "a"
|
||||||
|
every { fileSearch.scan("a") } returns files
|
||||||
|
every { newDownloadDao.downloads() } returns downloads
|
||||||
|
storageObserver = StorageObserver(sharedPreferenceUtil, newDownloadDao, fileSearch)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `books from disk are filtered by current downloads`() {
|
||||||
|
withFiltering()
|
||||||
|
booksOnFileSystem().assertValues(listOf())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `null books from ZimContentProvider are filtered out`() {
|
||||||
|
withNoFiltering()
|
||||||
|
booksOnFileSystem().assertValues(listOf())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `iterable ZimContentProvider with zim file produces a book`() {
|
||||||
|
val expectedBook = book(
|
||||||
|
"id", "title", "1", "favicon", "creator", "publisher", "date",
|
||||||
|
"description", "language"
|
||||||
|
)
|
||||||
|
mockkStatic(ZimContentProvider::class)
|
||||||
|
withNoFiltering()
|
||||||
|
every { ZimContentProvider.setZimFile("This won't match") } returns ""
|
||||||
|
expect(expectedBook)
|
||||||
|
booksOnFileSystem().assertValues(
|
||||||
|
listOf(
|
||||||
|
bookOnDisk(book = expectedBook, file = file, databaseId = null)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
assertThat(ZimContentProvider.originalFileName).isEqualTo("")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `zim provider sets zim file to original file name if it exists`() {
|
||||||
|
withNoFiltering()
|
||||||
|
mockkStatic(ZimContentProvider::class)
|
||||||
|
every { ZimContentProvider.setZimFile(any()) } returns null
|
||||||
|
ZimContentProvider.zimFileName = "myZimFileName"
|
||||||
|
booksOnFileSystem().assertValues(listOf())
|
||||||
|
verify { ZimContentProvider.setZimFile("myZimFileName") }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `zim provider does not read book if it can not iterate`() {
|
||||||
|
withNoFiltering()
|
||||||
|
ZimContentProvider.canIterate = false
|
||||||
|
booksOnFileSystem().assertValues(listOf())
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun booksOnFileSystem() = storageObserver.booksOnFileSystem
|
||||||
|
.test()
|
||||||
|
.also {
|
||||||
|
downloads.offer(listOf(downloadModel))
|
||||||
|
files.offer(listOf(file))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun expect(expectedBook: Book) {
|
||||||
|
every { ZimContentProvider.getZimFileTitle() } returns expectedBook.title
|
||||||
|
every { ZimContentProvider.getId() } returns expectedBook.id
|
||||||
|
every { ZimContentProvider.getFileSize() } returns expectedBook.size.toInt()
|
||||||
|
every { ZimContentProvider.getFavicon() } returns expectedBook.favicon
|
||||||
|
every { ZimContentProvider.getCreator() } returns expectedBook.creator
|
||||||
|
every { ZimContentProvider.getPublisher() } returns expectedBook.publisher
|
||||||
|
every { ZimContentProvider.getDate() } returns expectedBook.date
|
||||||
|
every { ZimContentProvider.getDescription() } returns expectedBook.description
|
||||||
|
every { ZimContentProvider.getLanguage() } returns expectedBook.language
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun withFiltering() {
|
||||||
|
every { downloadModel.fileNameFromUrl } returns "test"
|
||||||
|
every { file.absolutePath } returns "This is a test"
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun withNoFiltering() {
|
||||||
|
every { downloadModel.fileNameFromUrl } returns "test"
|
||||||
|
every { file.absolutePath } returns "This won't match"
|
||||||
|
}
|
||||||
|
}
|
@ -35,7 +35,7 @@ ext {
|
|||||||
set("powerMockVersion", "1.6.6")
|
set("powerMockVersion", "1.6.6")
|
||||||
set("powerMockJUnitVersion", "1.7.4")
|
set("powerMockJUnitVersion", "1.7.4")
|
||||||
set("baristaVersion", "2.7.1")
|
set("baristaVersion", "2.7.1")
|
||||||
set("kotlinVersion", "1.3.31")
|
set("kotlinVersion", "1.3.40")
|
||||||
set("objectboxVersion", "2.3.4")
|
set("objectboxVersion", "2.3.4")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user