diff --git a/.gitignore b/.gitignore index bb9d46f62..bd6b59553 100644 --- a/.gitignore +++ b/.gitignore @@ -53,4 +53,5 @@ glassify .project .classpath -.vscode \ No newline at end of file +.vscode +captures/ diff --git a/app/src/androidTestKiwix/java/org/kiwix/kiwixmobile/tests/DownloadTest.java b/app/src/androidTestKiwix/java/org/kiwix/kiwixmobile/tests/DownloadTest.java index ca45dd161..1ac2c8040 100644 --- a/app/src/androidTestKiwix/java/org/kiwix/kiwixmobile/tests/DownloadTest.java +++ b/app/src/androidTestKiwix/java/org/kiwix/kiwixmobile/tests/DownloadTest.java @@ -51,7 +51,6 @@ import static android.support.test.espresso.matcher.ViewMatchers.withId; import static android.support.test.espresso.matcher.ViewMatchers.withParent; import static com.schibsted.spain.barista.assertion.BaristaVisibilityAssertions.assertDisplayed; import static com.schibsted.spain.barista.interaction.BaristaClickInteractions.clickOn; -import static com.schibsted.spain.barista.interaction.BaristaDialogInteractions.clickDialogPositiveButton; import static com.schibsted.spain.barista.interaction.BaristaSwipeRefreshInteractions.refresh; import static junit.framework.Assert.fail; import static org.hamcrest.Matchers.allOf; @@ -115,13 +114,13 @@ public class DownloadTest { BaristaSleepInteractions.sleep(TEST_PAUSE_MS); try { - onData(withContent("ray_charles")).inAdapterView(withId(R.id.library_list)); + onData(withContent("ray_charles")).inAdapterView(withId(R.id.libraryList)); } catch (Exception e) { fail("Couldn't find downloaded file 'ray_charles'\n\nOriginal Exception:\n" + e.getLocalizedMessage() + "\n\n" ); } - deleteZimIfExists("ray_charles", R.id.library_list); + deleteZimIfExists("ray_charles", R.id.libraryList); assertDisplayed(R.string.local_zims); clickOn(R.string.local_zims); diff --git a/app/src/androidTestKiwix/java/org/kiwix/kiwixmobile/tests/NetworkTest.java b/app/src/androidTestKiwix/java/org/kiwix/kiwixmobile/tests/NetworkTest.java index 44d3fbf65..1ea4f276a 100644 --- a/app/src/androidTestKiwix/java/org/kiwix/kiwixmobile/tests/NetworkTest.java +++ b/app/src/androidTestKiwix/java/org/kiwix/kiwixmobile/tests/NetworkTest.java @@ -137,7 +137,7 @@ public class NetworkTest { "Permission dialog was not shown, we probably already have required permissions"); } - onData(withContent("wikipedia_ab_all_2017-03")).inAdapterView(withId(R.id.library_list)).perform(click()); + onData(withContent("wikipedia_ab_all_2017-03")).inAdapterView(withId(R.id.libraryList)).perform(click()); try { onView(withId(android.R.id.button1)).perform(click()); diff --git a/app/src/main/java/org/kiwix/kiwixmobile/KiwixMobileActivity.java b/app/src/main/java/org/kiwix/kiwixmobile/KiwixMobileActivity.java index a542609dc..f733f117d 100644 --- a/app/src/main/java/org/kiwix/kiwixmobile/KiwixMobileActivity.java +++ b/app/src/main/java/org/kiwix/kiwixmobile/KiwixMobileActivity.java @@ -482,7 +482,7 @@ public class KiwixMobileActivity extends BaseActivity implements WebViewCallback } if (i.hasExtra(EXTRA_ZIM_FILE)) { File file = new File(getFileName(i.getStringExtra(EXTRA_ZIM_FILE))); - LibraryFragment.mService.cancelNotification(i.getIntExtra(EXTRA_NOTIFICATION_ID, 0)); + //LibraryFragment.mService.cancelNotification(i.getIntExtra(EXTRA_NOTIFICATION_ID, 0)); Uri uri = Uri.fromFile(file); finish(); diff --git a/app/src/main/java/org/kiwix/kiwixmobile/database/DownloadDao.java b/app/src/main/java/org/kiwix/kiwixmobile/database/DownloadDao.java index 02abadb91..ae8c0e68c 100644 --- a/app/src/main/java/org/kiwix/kiwixmobile/database/DownloadDao.java +++ b/app/src/main/java/org/kiwix/kiwixmobile/database/DownloadDao.java @@ -50,8 +50,14 @@ public class DownloadDao { } public void insert(final DownloadModel downloadModel) { - kiwixDatabase.persistWithOnConflict(databaseEntity(downloadModel), - TableStatement.ConflictAlgorithm.REPLACE); + if (doesNotAlreadyExist(downloadModel)) { + kiwixDatabase.persistWithOnConflict(databaseEntity(downloadModel), + TableStatement.ConflictAlgorithm.REPLACE); + } + } + + private boolean doesNotAlreadyExist(DownloadModel downloadModel) { + return kiwixDatabase.count(DownloadDatabaseEntity.class, DownloadDatabaseEntity.BOOK_ID.eq(downloadModel.getBookId())) == 0; } public void delete(@NotNull Long... downloadIds) { diff --git a/app/src/main/java/org/kiwix/kiwixmobile/database/NetworkLanguageDao.java b/app/src/main/java/org/kiwix/kiwixmobile/database/NetworkLanguageDao.java index a7e2319f7..3e825dd8f 100644 --- a/app/src/main/java/org/kiwix/kiwixmobile/database/NetworkLanguageDao.java +++ b/app/src/main/java/org/kiwix/kiwixmobile/database/NetworkLanguageDao.java @@ -19,50 +19,62 @@ package org.kiwix.kiwixmobile.database; +import com.yahoo.squidb.data.SimpleDataChangedNotifier; import com.yahoo.squidb.data.SquidCursor; import com.yahoo.squidb.sql.Query; - -import javax.inject.Inject; -import org.kiwix.kiwixmobile.database.entity.NetworkLanguageDatabaseEntity; -import org.kiwix.kiwixmobile.library.LibraryAdapter; -import org.kiwix.kiwixmobile.library.LibraryAdapter.Language; - +import io.reactivex.Flowable; +import io.reactivex.processors.PublishProcessor; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import javax.inject.Inject; +import org.kiwix.kiwixmobile.database.entity.NetworkLanguageDatabaseEntity; +import org.kiwix.kiwixmobile.zim_manager.library_view.adapter.Language; public class NetworkLanguageDao { private KiwixDatabase mDb; + private final PublishProcessor> languageProcessor = + PublishProcessor.create(); @Inject public NetworkLanguageDao(KiwixDatabase kiwikDatabase) { this.mDb = kiwikDatabase; + mDb.registerDataChangedNotifier( + new SimpleDataChangedNotifier(NetworkLanguageDatabaseEntity.TABLE) { + @Override + protected void onDataChanged() { + languageProcessor.onNext(getActiveLanguages()); + } + }); } - public ArrayList getFilteredLanguages() { - SquidCursor languageCursor = mDb.query( + public Flowable> activeLanguages() { + return languageProcessor.startWith(getActiveLanguages()).distinctUntilChanged(); + } + + public List getActiveLanguages() { + ArrayList result = new ArrayList<>(); + try (SquidCursor languageCursor = mDb.query( NetworkLanguageDatabaseEntity.class, - Query.select()); - ArrayList result = new ArrayList<>(); - try { + Query.select().where(NetworkLanguageDatabaseEntity.ENABLED.eq(true)))) { while (languageCursor.moveToNext()) { String languageCode = languageCursor.get(NetworkLanguageDatabaseEntity.LANGUAGE_I_S_O_3); boolean enabled = languageCursor.get(NetworkLanguageDatabaseEntity.ENABLED); - result.add(new LibraryAdapter.Language(languageCode, enabled)); + result.add(new Language(languageCode, enabled)); } - } finally { - languageCursor.close(); } return result; } - public void saveFilteredLanguages(List languages){ + public void saveFilteredLanguages(List languages) { mDb.deleteAll(NetworkLanguageDatabaseEntity.class); - Collections.sort(languages, (language, t1) -> language.language.compareTo(t1.language)); - for (LibraryAdapter.Language language : languages){ - NetworkLanguageDatabaseEntity networkLanguageDatabaseEntity = new NetworkLanguageDatabaseEntity(); - networkLanguageDatabaseEntity.setLanguageISO3(language.languageCode); - networkLanguageDatabaseEntity.setIsEnabled(language.active); + Collections.sort(languages, + (language, t1) -> language.getLanguage().compareTo(t1.getLanguage())); + for (Language language : languages) { + NetworkLanguageDatabaseEntity networkLanguageDatabaseEntity = + new NetworkLanguageDatabaseEntity(); + networkLanguageDatabaseEntity.setLanguageISO3(language.getLanguageCode()); + networkLanguageDatabaseEntity.setIsEnabled(language.getActive()); mDb.persist(networkLanguageDatabaseEntity); } } diff --git a/app/src/main/java/org/kiwix/kiwixmobile/di/components/ApplicationComponent.java b/app/src/main/java/org/kiwix/kiwixmobile/di/components/ApplicationComponent.java index ae57c7cad..b385e26e3 100644 --- a/app/src/main/java/org/kiwix/kiwixmobile/di/components/ApplicationComponent.java +++ b/app/src/main/java/org/kiwix/kiwixmobile/di/components/ApplicationComponent.java @@ -27,7 +27,7 @@ import org.kiwix.kiwixmobile.di.modules.ApplicationModule; import org.kiwix.kiwixmobile.di.modules.JNIModule; import org.kiwix.kiwixmobile.di.modules.NetworkModule; import org.kiwix.kiwixmobile.downloader.DownloadService; -import org.kiwix.kiwixmobile.library.LibraryAdapter; +import org.kiwix.kiwixmobile.zim_manager.library_view.adapter.LibraryAdapter; import org.kiwix.kiwixmobile.settings.KiwixSettingsActivity; import org.kiwix.kiwixmobile.views.AutoCompleteAdapter; import org.kiwix.kiwixmobile.views.web.KiwixWebView; @@ -56,8 +56,6 @@ public interface ApplicationComponent { void inject(ZimContentProvider zimContentProvider); - void inject(LibraryAdapter libraryAdapter); - void inject(KiwixWebView kiwixWebView); void inject(KiwixSettingsActivity.PrefsFragment prefsFragment); diff --git a/app/src/main/java/org/kiwix/kiwixmobile/di/modules/ApplicationModule.java b/app/src/main/java/org/kiwix/kiwixmobile/di/modules/ApplicationModule.java index ff009daad..14acf2c98 100644 --- a/app/src/main/java/org/kiwix/kiwixmobile/di/modules/ApplicationModule.java +++ b/app/src/main/java/org/kiwix/kiwixmobile/di/modules/ApplicationModule.java @@ -17,6 +17,7 @@ */ package org.kiwix.kiwixmobile.di.modules; +import android.app.Application; import android.app.DownloadManager; import android.app.NotificationManager; import android.content.Context; @@ -32,6 +33,10 @@ import org.kiwix.kiwixmobile.utils.BookUtils; }) public class ApplicationModule { + @Provides @Singleton Application provideApplication(Context context) { + return (Application) context; + } + @Provides @Singleton NotificationManager provideNotificationManager(Context context) { return (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); } diff --git a/app/src/main/java/org/kiwix/kiwixmobile/downloader/DownloadService.java b/app/src/main/java/org/kiwix/kiwixmobile/downloader/DownloadService.java index c872f8444..d6266edf0 100644 --- a/app/src/main/java/org/kiwix/kiwixmobile/downloader/DownloadService.java +++ b/app/src/main/java/org/kiwix/kiwixmobile/downloader/DownloadService.java @@ -201,7 +201,7 @@ public class DownloadService extends Service { notificationManager.notify(notificationID, notification.get(notificationID).build()); downloadStatus.put(notificationID, PLAY); - LibraryFragment.downloadingBooks.remove(book); + //LibraryFragment.downloadingBooks.remove(book); String url = intent.getExtras().getString(DownloadIntent.DOWNLOAD_URL_PARAMETER); downloadBook(url, notificationID, book); } diff --git a/app/src/main/java/org/kiwix/kiwixmobile/extensions/ConnectivityManagerExtensions.kt b/app/src/main/java/org/kiwix/kiwixmobile/extensions/ConnectivityManagerExtensions.kt new file mode 100644 index 000000000..80aee2438 --- /dev/null +++ b/app/src/main/java/org/kiwix/kiwixmobile/extensions/ConnectivityManagerExtensions.kt @@ -0,0 +1,12 @@ +package org.kiwix.kiwixmobile.extensions + +import android.net.ConnectivityManager +import org.kiwix.kiwixmobile.zim_manager.NetworkState +import org.kiwix.kiwixmobile.zim_manager.NetworkState.CONNECTED +import org.kiwix.kiwixmobile.zim_manager.NetworkState.NOT_CONNECTED + +val ConnectivityManager.networkState: NetworkState + get() = if (activeNetworkInfo?.isConnected == true) + CONNECTED + else + NOT_CONNECTED \ No newline at end of file diff --git a/app/src/main/java/org/kiwix/kiwixmobile/extensions/ContextExtensions.kt b/app/src/main/java/org/kiwix/kiwixmobile/extensions/ContextExtensions.kt index 361112851..548f8e2b7 100644 --- a/app/src/main/java/org/kiwix/kiwixmobile/extensions/ContextExtensions.kt +++ b/app/src/main/java/org/kiwix/kiwixmobile/extensions/ContextExtensions.kt @@ -1,7 +1,9 @@ package org.kiwix.kiwixmobile.extensions import android.content.Context +import android.content.IntentFilter import android.widget.Toast +import org.kiwix.kiwixmobile.zim_manager.BaseBroadcastReceiver fun Context?.toast( stringId: Int, @@ -11,4 +13,17 @@ fun Context?.toast( Toast.makeText(this, stringId, length) .show() } -} \ No newline at end of file +} + +fun Context?.toast( + text: String, + length: Int = Toast.LENGTH_LONG +) { + this?.let { + Toast.makeText(this, text, length) + .show() + } +} + +fun Context.registerReceiver(baseBroadcastReceiver: BaseBroadcastReceiver) = + registerReceiver(baseBroadcastReceiver, IntentFilter(baseBroadcastReceiver.action)) diff --git a/app/src/main/java/org/kiwix/kiwixmobile/extensions/TextViewExtensions.kt b/app/src/main/java/org/kiwix/kiwixmobile/extensions/TextViewExtensions.kt new file mode 100644 index 000000000..aa48fb359 --- /dev/null +++ b/app/src/main/java/org/kiwix/kiwixmobile/extensions/TextViewExtensions.kt @@ -0,0 +1,12 @@ +package org.kiwix.kiwixmobile.extensions + +import android.view.View +import android.widget.TextView + +fun TextView.setTextAndVisibility(nullableText: String?) = + if (nullableText != null && nullableText.isNotEmpty()) { + text = nullableText + visibility = View.VISIBLE + } else { + visibility = View.GONE + } \ No newline at end of file diff --git a/app/src/main/java/org/kiwix/kiwixmobile/extensions/ViewExtensions.kt b/app/src/main/java/org/kiwix/kiwixmobile/extensions/ViewExtensions.kt new file mode 100644 index 000000000..0fae85f28 --- /dev/null +++ b/app/src/main/java/org/kiwix/kiwixmobile/extensions/ViewExtensions.kt @@ -0,0 +1,21 @@ +package org.kiwix.kiwixmobile.extensions + +import android.graphics.Color +import android.support.design.widget.Snackbar +import android.view.View + +fun View.snack( + stringId: Int, + actionStringId: Int, + actionClick: () -> Unit, + actionTextColor: Int = Color.WHITE +) { + Snackbar.make( + this, stringId, Snackbar.LENGTH_LONG + ) + .setAction(actionStringId) { + actionClick.invoke() + } + .setActionTextColor(actionTextColor) + .show() +} \ No newline at end of file diff --git a/app/src/main/java/org/kiwix/kiwixmobile/library/LibraryAdapter.java b/app/src/main/java/org/kiwix/kiwixmobile/library/LibraryAdapter.java deleted file mode 100755 index b4fd127cf..000000000 --- a/app/src/main/java/org/kiwix/kiwixmobile/library/LibraryAdapter.java +++ /dev/null @@ -1,473 +0,0 @@ -/* - * Copyright 2013 Rashiq Ahmad - * - * 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.library; - -import android.content.Context; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.util.Base64; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.BaseAdapter; -import android.widget.Filter; -import android.widget.ImageView; -import android.widget.TextView; -import com.google.common.collect.ImmutableList; -import io.reactivex.Completable; -import io.reactivex.Observable; -import io.reactivex.android.schedulers.AndroidSchedulers; -import io.reactivex.disposables.Disposable; -import io.reactivex.schedulers.Schedulers; -import java.text.DecimalFormat; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Set; -import javax.inject.Inject; -import org.kiwix.kiwixmobile.KiwixApplication; -import org.kiwix.kiwixmobile.R; -import org.kiwix.kiwixmobile.database.BookDao; -import org.kiwix.kiwixmobile.database.NetworkLanguageDao; -import org.kiwix.kiwixmobile.library.entity.LibraryNetworkEntity.Book; -import org.kiwix.kiwixmobile.utils.BookUtils; -import org.kiwix.kiwixmobile.zim_manager.library_view.LibraryFragment; - -import static org.kiwix.kiwixmobile.utils.NetworkUtils.parseURL; - -public class LibraryAdapter extends BaseAdapter { - private static final int LIST_ITEM_TYPE_BOOK = 0; - private static final int LIST_ITEM_TYPE_DIVIDER = 1; - - private ImmutableList allBooks; - private List listItems = new ArrayList<>(); - private final Context context; - public Map languageCounts = new HashMap<>(); - public List languages = new ArrayList<>(); - private final LayoutInflater layoutInflater; - private final BookFilter bookFilter = new BookFilter(); - private Disposable saveNetworkLanguageDisposable; - @Inject BookUtils bookUtils; - @Inject - NetworkLanguageDao networkLanguageDao; - @Inject - BookDao bookDao; - - //TODO: restore functionality of commented out code - public LibraryAdapter(Context context) { - super(); - KiwixApplication.getApplicationComponent().inject(this); - this.context = context; - layoutInflater = LayoutInflater.from(context); - } - - public void setAllBooks(List books) { - allBooks = ImmutableList.copyOf(books); - updateLanguageCounts(); - updateLanguages(); - } - - public boolean isDivider(int position) { - return listItems.get(position).type == LIST_ITEM_TYPE_DIVIDER; - } - - @Override - public int getCount() { - return listItems.size(); - } - - @Override - public Object getItem(int i) { - return listItems.get(i).data; - } - - @Override - public long getItemId(int i) { - return i; - } - - @Override - public View getView(int position, View convertView, ViewGroup parent) { - ViewHolder holder; - if (position >= listItems.size()) { - return convertView; - } - ListItem item = listItems.get(position); - - if (item.type == LIST_ITEM_TYPE_BOOK) { - if (convertView != null && convertView.findViewById(R.id.title) != null) { - holder = (ViewHolder) convertView.getTag(); - } else { - convertView = layoutInflater.inflate(R.layout.library_item, null); - holder = new ViewHolder(); - holder.title = convertView.findViewById(R.id.title); - holder.description = convertView.findViewById(R.id.description); - holder.language = convertView.findViewById(R.id.language); - holder.creator = convertView.findViewById(R.id.creator); - holder.publisher = convertView.findViewById(R.id.publisher); - holder.date = convertView.findViewById(R.id.date); - holder.size = convertView.findViewById(R.id.size); - holder.fileName = convertView.findViewById(R.id.fileName); - holder.favicon = convertView.findViewById(R.id.favicon); - convertView.setTag(holder); - } - - Book book = (Book) listItems.get(position).data; - - holder.title.setText(book.getTitle()); - holder.description.setText(book.getDescription()); - holder.language.setText(bookUtils.getLanguage(book.getLanguage())); - holder.creator.setText(book.getCreator()); - holder.publisher.setText(book.getPublisher()); - holder.date.setText(book.getDate()); - holder.size.setText(createGbString(book.getSize())); - holder.fileName.setText(parseURL(context, book.getUrl())); - holder.favicon.setImageBitmap(createBitmapFromEncodedString(book.getFavicon(), context)); - - // Check if no value is empty. Set the view to View.GONE, if it is. To View.VISIBLE, if not. - if (book.getTitle() == null || book.getTitle().isEmpty()) { - holder.title.setVisibility(View.GONE); - } else { - holder.title.setVisibility(View.VISIBLE); - } - - if (book.getDescription() == null || book.getDescription().isEmpty()) { - holder.description.setVisibility(View.GONE); - } else { - holder.description.setVisibility(View.VISIBLE); - } - - if (book.getCreator() == null || book.getCreator().isEmpty()) { - holder.creator.setVisibility(View.GONE); - } else { - holder.creator.setVisibility(View.VISIBLE); - } - - if (book.getPublisher() == null || book.getPublisher().isEmpty()) { - holder.publisher.setVisibility(View.GONE); - } else { - holder.publisher.setVisibility(View.VISIBLE); - } - - if (book.getDate() == null || book.getDate().isEmpty()) { - holder.date.setVisibility(View.GONE); - } else { - holder.date.setVisibility(View.VISIBLE); - } - - if (book.getSize() == null || book.getSize().isEmpty()) { - holder.size.setVisibility(View.GONE); - } else { - holder.size.setVisibility(View.VISIBLE); - } - - return convertView; - } else { - if (convertView != null && convertView.findViewById(R.id.divider_text) != null) { - holder = (ViewHolder) convertView.getTag(); - } else { - convertView = layoutInflater.inflate(R.layout.library_divider, null); - holder = new ViewHolder(); - holder.title = convertView.findViewById(R.id.divider_text); - convertView.setTag(holder); - } - - String dividerText = (String) listItems.get(position).data; - - holder.title.setText(dividerText); - - return convertView; - } - } - - private boolean languageActive(Book book) { - return Observable.fromIterable(languages) - .filter(language -> language.languageCode.equals(book.getLanguage())) - .firstElement() - .map(language -> language.active) - .blockingGet(false); - } - - private Observable getMatches(Book b, String s) { - StringBuilder text = new StringBuilder(); - text.append(b.getTitle()).append("|").append(b.getDescription()).append("|") - .append(parseURL(context, b.getUrl())).append("|"); - if (bookUtils.localeMap.containsKey(b.getLanguage())) { - text.append(bookUtils.localeMap.get(b.getLanguage()).getDisplayLanguage()).append("|"); - } - String[] words = s.toLowerCase().split("\\s+"); - b.searchMatches = Observable.fromArray(words) - .filter(text.toString().toLowerCase()::contains) - .count() - .blockingGet() - .intValue(); - if (b.searchMatches > 0) { - return Observable.just(b); - } else { - return Observable.empty(); - } - } - - private class BookFilter extends Filter { - @Override - protected FilterResults performFiltering(CharSequence s) { - List books = bookDao.getBooks(); - listItems.clear(); - if (s.length() == 0) { - List selectedLanguages = Observable.fromIterable(allBooks) - .filter(LibraryAdapter.this::languageActive) - .filter(book -> !books.contains(book)) - // .filter(book -> !DownloadFragment.mDownloads.values().contains(book)) - .filter(book -> !LibraryFragment.downloadingBooks.contains(book)) - .filter(book -> !book.url.contains("/stack_exchange/")) // Temp filter see #694 - .toList() - .blockingGet(); - - List unselectedLanguages = Observable.fromIterable(allBooks) - .filter(book -> !languageActive(book)) - .filter(book -> !books.contains(book)) - // .filter(book -> !DownloadFragment.mDownloads.values().contains(book)) - .filter(book -> !LibraryFragment.downloadingBooks.contains(book)) - .filter(book -> !book.url.contains("/stack_exchange/")) // Temp filter see #694 - .toList() - .blockingGet(); - - listItems.add(new ListItem(context.getResources().getString(R.string.your_languages), LIST_ITEM_TYPE_DIVIDER)); - addBooks(selectedLanguages); - listItems.add(new ListItem(context.getResources().getString(R.string.other_languages), LIST_ITEM_TYPE_DIVIDER)); - addBooks(unselectedLanguages); - } else { - List selectedLanguages = Observable.fromIterable(allBooks) - .filter(LibraryAdapter.this::languageActive) - .filter(book -> !books.contains(book)) - // .filter(book -> !DownloadFragment.mDownloads.values().contains(book)) - .filter(book -> !LibraryFragment.downloadingBooks.contains(book)) - .filter(book -> !book.url.contains("/stack_exchange/")) // Temp filter see #694 - .flatMap(book -> getMatches(book, s.toString())) - .toList() - .blockingGet(); - - Collections.sort(selectedLanguages, new BookMatchComparator()); - - List unselectedLanguages = Observable.fromIterable(allBooks) - .filter(book -> !languageActive(book)) - .filter(book -> !books.contains(book)) - // .filter(book -> !DownloadFragment.mDownloads.values().contains(book)) - .filter(book -> !LibraryFragment.downloadingBooks.contains(book)) - .filter(book -> !book.url.contains("/stack_exchange/")) // Temp filter see #694 - .flatMap(book -> getMatches(book, s.toString())) - .toList() - .blockingGet(); - - Collections.sort(unselectedLanguages, new BookMatchComparator()); - - listItems.add(new ListItem("In your language:", LIST_ITEM_TYPE_DIVIDER)); - addBooks(selectedLanguages); - listItems.add(new ListItem("In other languages:", LIST_ITEM_TYPE_DIVIDER)); - addBooks(unselectedLanguages); - } - - FilterResults results = new FilterResults(); - results.values = listItems; - results.count = listItems.size(); - return results; - } - - @Override - protected void publishResults(CharSequence constraint, FilterResults results) { - List filtered = (List) results.values; - if (filtered != null) { - if (filtered.isEmpty()) { - addBooks(allBooks); - } - } - notifyDataSetChanged(); - } - } - - public Filter getFilter() { - return bookFilter; - } - - public void updateNetworkLanguages() { - saveNetworkLanguages(); - } - - private void updateLanguageCounts() { - languageCounts.clear(); - for (Book book : allBooks) { - Integer cnt = languageCounts.get(book.getLanguage()); - if (cnt == null) { - languageCounts.put(book.getLanguage(), 1); - } else { - languageCounts.put(book.getLanguage(), cnt + 1); - } - } - } - - private void updateLanguages() { - // Load previously stored languages and extract which ones were enabled. The new book list might - // have new languages, or be missing some old ones so we want to refresh it, but retain user's - // selections. - Set enabled_languages = new HashSet<>(); - for (Language language : networkLanguageDao.getFilteredLanguages()) { - if (language.active) { - enabled_languages.add(language.languageCode); - } - } - - // Populate languages with all available locales, which appear in the current list of all books. - this.languages = new ArrayList<>(); - for (String iso_language : Locale.getISOLanguages()) { - Locale locale = new Locale(iso_language); - if (languageCounts.get(locale.getISO3Language()) != null) { - // Enable this language either if it was enabled previously, or if it is the device language. - if (enabled_languages.contains(locale.getISO3Language()) || - context.getResources().getConfiguration().locale.getISO3Language().equals(locale.getISO3Language())) { - this.languages.add(new Language(locale, true)); - } else { - this.languages.add(new Language(locale, false)); - } - } - } - - saveNetworkLanguages(); - } - - private void addBooks(List books) { - for (Book book : books) { - listItems.add(new ListItem(book, LIST_ITEM_TYPE_BOOK)); - } - } - - // Create a string that represents the size of the zim file in a human readable way - public static String createGbString(String megaByte) { - - int size = 0; - try { - size = Integer.parseInt(megaByte); - } catch (NumberFormatException e) { - e.printStackTrace(); - } - - if (size <= 0) { - return ""; - } - - final String[] units = new String[]{"KB", "MB", "GB", "TB"}; - int conversion = (int) (Math.log10(size) / Math.log10(1024)); - return new DecimalFormat("#,##0.#") - .format(size / Math.pow(1024, conversion)) - + " " - + units[conversion]; - } - - // Decode and create a Bitmap from the 64-Bit encoded favicon string - public static Bitmap createBitmapFromEncodedString(String encodedString, Context context) { - - try { - byte[] decodedString = Base64.decode(encodedString, Base64.DEFAULT); - return BitmapFactory.decodeByteArray(decodedString, 0, decodedString.length); - } catch (Exception e) { - e.printStackTrace(); - } - - return BitmapFactory.decodeResource(context.getResources(), R.mipmap.kiwix_icon); - } - - private static class ViewHolder { - - TextView title; - - TextView description; - - TextView language; - - TextView creator; - - TextView publisher; - - TextView date; - - TextView size; - - TextView fileName; - - ImageView favicon; - } - - private class ListItem { - public Object data; - public int type; - - public ListItem(Object data, int type) { - this.data = data; - this.type = type; - } - } - - private class BookMatchComparator implements Comparator { - public int compare(Book book1, Book book2) { - return book2.searchMatches - book1.searchMatches; - } - } - - public static class Language { - public String language; - public String languageLocalized; - public String languageCode; - public String languageCodeISO2; - public Boolean active; - - Language(Locale locale, Boolean active) { - this.language = locale.getDisplayLanguage(); - this.languageLocalized = locale.getDisplayLanguage(locale); - this.languageCode = locale.getISO3Language(); - this.languageCodeISO2 = locale.getLanguage(); - - this.active = active; - } - - public Language(String languageCode, Boolean active) { - this(new Locale(languageCode), active); - } - - @Override - public boolean equals(Object obj) { - return ((Language) obj).language.equals(language) && - ((Language) obj).active.equals(active); - } - } - - private void saveNetworkLanguages() { - if (saveNetworkLanguageDisposable != null && !saveNetworkLanguageDisposable.isDisposed()) { - saveNetworkLanguageDisposable.dispose(); - } - saveNetworkLanguageDisposable = Completable.fromAction(() -> networkLanguageDao.saveFilteredLanguages(languages)) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(); - } -} diff --git a/app/src/main/java/org/kiwix/kiwixmobile/library/entity/LibraryNetworkEntity.java b/app/src/main/java/org/kiwix/kiwixmobile/library/entity/LibraryNetworkEntity.java index 9b6348ee9..2133859dd 100644 --- a/app/src/main/java/org/kiwix/kiwixmobile/library/entity/LibraryNetworkEntity.java +++ b/app/src/main/java/org/kiwix/kiwixmobile/library/entity/LibraryNetworkEntity.java @@ -29,7 +29,7 @@ import org.simpleframework.xml.Root; public class LibraryNetworkEntity { @ElementList(name = "book", inline = true, required = false) - private LinkedList book; + public LinkedList book; @Attribute(name = "version", required = false) private String version; diff --git a/app/src/main/java/org/kiwix/kiwixmobile/network/KiwixService.java b/app/src/main/java/org/kiwix/kiwixmobile/network/KiwixService.java index 551bc8b4e..2d1da4f57 100644 --- a/app/src/main/java/org/kiwix/kiwixmobile/network/KiwixService.java +++ b/app/src/main/java/org/kiwix/kiwixmobile/network/KiwixService.java @@ -17,12 +17,12 @@ */ package org.kiwix.kiwixmobile.network; -import org.kiwix.kiwixmobile.library.entity.LibraryNetworkEntity; -import org.kiwix.kiwixmobile.library.entity.MetaLinkNetworkEntity; - +import io.reactivex.Flowable; import io.reactivex.Observable; import io.reactivex.schedulers.Schedulers; import okhttp3.OkHttpClient; +import org.kiwix.kiwixmobile.library.entity.LibraryNetworkEntity; +import org.kiwix.kiwixmobile.library.entity.MetaLinkNetworkEntity; import retrofit2.Retrofit; import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory; import retrofit2.converter.simplexml.SimpleXmlConverterFactory; @@ -30,7 +30,7 @@ import retrofit2.http.GET; import retrofit2.http.Url; public interface KiwixService { - @GET("/library/library_zim.xml") Observable getLibrary(); + @GET("/library/library_zim.xml") Flowable getLibrary(); @GET Observable getMetaLinks(@Url String url); diff --git a/app/src/main/java/org/kiwix/kiwixmobile/utils/KiwixDialog.kt b/app/src/main/java/org/kiwix/kiwixmobile/utils/KiwixDialog.kt index 851b20acf..e850b0e06 100644 --- a/app/src/main/java/org/kiwix/kiwixmobile/utils/KiwixDialog.kt +++ b/app/src/main/java/org/kiwix/kiwixmobile/utils/KiwixDialog.kt @@ -16,7 +16,7 @@ sealed class KiwixDialog( open class YesNoDialog( title: Int, message: Int - ) : KiwixDialog(title, message, R.string.yes, R.string.no){ + ) : KiwixDialog(title, message, R.string.yes, R.string.no) { object NoWifi : YesNoDialog( R.string.wifi_only_title, R.string.wifi_only_msg ) @@ -25,9 +25,8 @@ sealed class KiwixDialog( R.string.confirm_stop_download_title, R.string.confirm_stop_download_msg ) - + object WifiOnly : YesNoDialog( + R.string.wifi_only_title, R.string.wifi_only_msg + ) } - - - } diff --git a/app/src/main/java/org/kiwix/kiwixmobile/utils/LanguageUtils.java b/app/src/main/java/org/kiwix/kiwixmobile/utils/LanguageUtils.java index f603402fb..a27393c99 100644 --- a/app/src/main/java/org/kiwix/kiwixmobile/utils/LanguageUtils.java +++ b/app/src/main/java/org/kiwix/kiwixmobile/utils/LanguageUtils.java @@ -19,7 +19,6 @@ package org.kiwix.kiwixmobile.utils; -import android.annotation.TargetApi; import android.content.Context; import android.content.res.Configuration; import android.graphics.Typeface; @@ -33,7 +32,7 @@ import android.view.LayoutInflater; import android.view.View; import android.widget.TextView; -import org.kiwix.kiwixmobile.library.LibraryAdapter; +import org.kiwix.kiwixmobile.zim_manager.library_view.adapter.Language; import org.kiwix.kiwixmobile.utils.files.FileUtils; import java.lang.reflect.Field; @@ -45,7 +44,6 @@ import java.util.List; import java.util.Locale; import java.util.MissingResourceException; -import static org.kiwix.kiwixmobile.utils.Constants.PREF_LANG; import static org.kiwix.kiwixmobile.utils.Constants.TAG_KIWIX; public class LanguageUtils { @@ -239,11 +237,11 @@ public class LanguageUtils { return values; } - public List getLanguageList() { - List values = new ArrayList<>(); + public List getLanguageList() { + List values = new ArrayList<>(); for (LanguageContainer value : mLanguageList) { - values.add(new LibraryAdapter.Language(value.getLanguageCode(), false)); + values.add(new Language(value.getLanguageCode(), false)); } return values; diff --git a/app/src/main/java/org/kiwix/kiwixmobile/utils/files/FileSearch.java b/app/src/main/java/org/kiwix/kiwixmobile/utils/files/FileSearch.java index ace5b4906..fe17026a5 100644 --- a/app/src/main/java/org/kiwix/kiwixmobile/utils/files/FileSearch.java +++ b/app/src/main/java/org/kiwix/kiwixmobile/utils/files/FileSearch.java @@ -27,6 +27,7 @@ import android.os.Environment; import android.provider.MediaStore; import android.util.Log; +import java.util.ArrayList; import org.kiwix.kiwixmobile.ZimContentProvider; import org.kiwix.kiwixmobile.library.entity.LibraryNetworkEntity; @@ -109,11 +110,13 @@ public class FileSearch { FilenameFilter[] filter = new FilenameFilter[zimFiles.length]; // Search all external directories that we can find. - String[] tempRoots = new String[StorageDeviceUtils.getStorageDevices(context, false).size() + 2]; + final ArrayList storageDevices = + StorageDeviceUtils.getStorageDevices(context, false); + String[] tempRoots = new String[storageDevices.size() + 2]; int j = 0; tempRoots[j++] = "/mnt"; tempRoots[j++] = defaultPath; - for (StorageDevice storageDevice : StorageDeviceUtils.getStorageDevices(context, false)) { + for (StorageDevice storageDevice : storageDevices) { tempRoots[j++] = storageDevice.getName(); } diff --git a/app/src/main/java/org/kiwix/kiwixmobile/views/LanguageSelectDialog.java b/app/src/main/java/org/kiwix/kiwixmobile/views/LanguageSelectDialog.java index a808fdb3d..5a8cb9378 100644 --- a/app/src/main/java/org/kiwix/kiwixmobile/views/LanguageSelectDialog.java +++ b/app/src/main/java/org/kiwix/kiwixmobile/views/LanguageSelectDialog.java @@ -30,7 +30,7 @@ import android.widget.ListView; import android.widget.TextView; import org.kiwix.kiwixmobile.R; -import org.kiwix.kiwixmobile.library.LibraryAdapter; +import org.kiwix.kiwixmobile.zim_manager.library_view.adapter.Language; import org.kiwix.kiwixmobile.utils.LanguageUtils; import java.util.List; @@ -46,7 +46,7 @@ public class LanguageSelectDialog extends AlertDialog { } public static class Builder extends AlertDialog.Builder { - private List languages; + private List languages; private Map languageCounts; private boolean singleSelect = false; private String selectedLanguage; @@ -60,7 +60,7 @@ public class LanguageSelectDialog extends AlertDialog { super(context, themeResId); } - public Builder setLanguages(List languages) { + public Builder setLanguages(List languages) { this.languages = languages; return this; } @@ -114,13 +114,13 @@ public class LanguageSelectDialog extends AlertDialog { } } - private static class LanguageArrayAdapter extends ArrayAdapter { + private static class LanguageArrayAdapter extends ArrayAdapter { private Map languageCounts; private Context context; private boolean singleSelect; private String selectedLanguage; - public LanguageArrayAdapter(Context context, int textViewResourceId, List languages, + public LanguageArrayAdapter(Context context, int textViewResourceId, List languages, Map languageCounts, boolean singleSelect, String selectedLanguage) { super(context, textViewResourceId, languages); this.languageCounts = languageCounts; @@ -150,36 +150,36 @@ public class LanguageSelectDialog extends AlertDialog { // Set event listeners first, since updating the default values can trigger them. holder.row.setOnClickListener((view) -> holder.checkBox.toggle()); holder.checkBox - .setOnCheckedChangeListener((compoundButton, b) -> getItem(position).active = b); + .setOnCheckedChangeListener((compoundButton, b) -> getItem(position).setActive(b)); - LibraryAdapter.Language language = getItem(position); - holder.language.setText(language.language); + Language language = getItem(position); + holder.language.setText(language.getLanguage()); holder.languageLocalized.setText(context.getString(R.string.language_localized, - language.languageLocalized)); + language.getLanguageLocalized())); holder.languageLocalized.setTypeface(Typeface.createFromAsset(context.getAssets(), - LanguageUtils.getTypeface(language.languageCode))); + LanguageUtils.getTypeface(language.getLanguageCode()))); if (languageCounts != null) { holder.languageEntriesCount.setText(context.getString(R.string.language_count, - languageCounts.get(language.languageCode))); + languageCounts.get(language.getLanguageCode()))); } else { holder.languageEntriesCount.setVisibility(View.GONE); } if (!singleSelect) { - holder.checkBox.setChecked(language.active); + holder.checkBox.setChecked(language.getActive()); } else { holder.checkBox.setClickable(false); holder.checkBox.setFocusable(false); - if (getSelectedLanguage().equalsIgnoreCase(language.languageCodeISO2)) { + if (getSelectedLanguage().equalsIgnoreCase(language.getLanguageCodeISO2())) { holder.checkBox.setChecked(true); } else { holder.checkBox.setChecked(false); } convertView.setOnClickListener((v -> { - setSelectedLanguage(language.languageCodeISO2); + setSelectedLanguage(language.getLanguageCodeISO2()); notifyDataSetChanged(); })); } diff --git a/app/src/main/java/org/kiwix/kiwixmobile/zim_manager/BaseBroadcastReceiver.kt b/app/src/main/java/org/kiwix/kiwixmobile/zim_manager/BaseBroadcastReceiver.kt new file mode 100644 index 000000000..43e26236b --- /dev/null +++ b/app/src/main/java/org/kiwix/kiwixmobile/zim_manager/BaseBroadcastReceiver.kt @@ -0,0 +1,25 @@ +package org.kiwix.kiwixmobile.zim_manager + +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent + +abstract class BaseBroadcastReceiver : BroadcastReceiver() { + +abstract val action:String + + override fun onReceive( + context: Context, + intent: Intent? + ) { + if (intent?.action == action) { + onIntentWithActionReceived(context, intent) + } + } + + abstract fun onIntentWithActionReceived( + context: Context, + intent: Intent + ) + +} diff --git a/app/src/main/java/org/kiwix/kiwixmobile/zim_manager/ConnectivityBroadcastReceiver.kt b/app/src/main/java/org/kiwix/kiwixmobile/zim_manager/ConnectivityBroadcastReceiver.kt new file mode 100644 index 000000000..028712c7a --- /dev/null +++ b/app/src/main/java/org/kiwix/kiwixmobile/zim_manager/ConnectivityBroadcastReceiver.kt @@ -0,0 +1,26 @@ +package org.kiwix.kiwixmobile.zim_manager + +import android.content.Context +import android.content.Intent +import android.net.ConnectivityManager +import io.reactivex.processors.PublishProcessor +import org.kiwix.kiwixmobile.extensions.networkState +import javax.inject.Inject + +class ConnectivityBroadcastReceiver @Inject constructor(private val connectivityManager: ConnectivityManager) : + BaseBroadcastReceiver() { + + override val action: String = ConnectivityManager.CONNECTIVITY_ACTION + + private val _networkStates = + PublishProcessor.create() + val networkStates = _networkStates.startWith(connectivityManager.networkState) + + override fun onIntentWithActionReceived( + context: Context, + intent: Intent + ) { + _networkStates.onNext(connectivityManager.networkState) + } + +} \ No newline at end of file diff --git a/app/src/main/java/org/kiwix/kiwixmobile/zim_manager/NetworkState.kt b/app/src/main/java/org/kiwix/kiwixmobile/zim_manager/NetworkState.kt new file mode 100644 index 000000000..6ec44f11e --- /dev/null +++ b/app/src/main/java/org/kiwix/kiwixmobile/zim_manager/NetworkState.kt @@ -0,0 +1,6 @@ +package org.kiwix.kiwixmobile.zim_manager + +enum class NetworkState { + CONNECTED, + NOT_CONNECTED +} \ No newline at end of file diff --git a/app/src/main/java/org/kiwix/kiwixmobile/zim_manager/ZimManageActivity.java b/app/src/main/java/org/kiwix/kiwixmobile/zim_manager/ZimManageActivity.java index 2aa3c14bf..5122da415 100644 --- a/app/src/main/java/org/kiwix/kiwixmobile/zim_manager/ZimManageActivity.java +++ b/app/src/main/java/org/kiwix/kiwixmobile/zim_manager/ZimManageActivity.java @@ -33,7 +33,6 @@ import android.support.v7.widget.Toolbar; import android.util.Log; import android.view.Menu; import android.view.MenuItem; -import android.widget.Toast; import java.io.File; import javax.inject.Inject; import org.kiwix.kiwixmobile.KiwixMobileActivity; @@ -217,9 +216,9 @@ public class ZimManageActivity extends BaseActivity implements ZimManageViewCall public boolean onQueryTextChange(String s) { searchQuery = s; - if (mSectionsPagerAdapter.libraryFragment.libraryAdapter != null) { - mSectionsPagerAdapter.libraryFragment.libraryAdapter.getFilter().filter(searchQuery); - } + //if (mSectionsPagerAdapter.libraryFragment.libraryAdapter != null) { + // mSectionsPagerAdapter.libraryFragment.libraryAdapter.getFilter().filter(searchQuery); + //} mViewPager.setCurrentItem(1); return true; } @@ -235,11 +234,11 @@ public class ZimManageActivity extends BaseActivity implements ZimManageViewCall switch (item.getItemId()) { case R.id.select_language: if (mViewPager.getCurrentItem() == 1) { - if(mSectionsPagerAdapter.libraryFragment.libraryAdapter.languages.size() == 0) { - Toast.makeText(this, R.string.wait_for_load, Toast.LENGTH_LONG).show(); - } else { - showLanguageSelect(); - } + //if(mSectionsPagerAdapter.libraryFragment.libraryAdapter.languages.size() == 0) { + // Toast.makeText(this, R.string.wait_for_load, Toast.LENGTH_LONG).show(); + //} else { + // showLanguageSelect(); + //} } default: return super.onOptionsItemSelected(item); @@ -262,11 +261,11 @@ public class ZimManageActivity extends BaseActivity implements ZimManageViewCall private void showLanguageSelect() { new LanguageSelectDialog.Builder(this, dialogStyle()) - .setLanguages(mSectionsPagerAdapter.libraryFragment.libraryAdapter.languages) - .setLanguageCounts(mSectionsPagerAdapter.libraryFragment.libraryAdapter.languageCounts) + //.setLanguages(mSectionsPagerAdapter.libraryFragment.libraryAdapter.languages) + //.setLanguageCounts(mSectionsPagerAdapter.libraryFragment.libraryAdapter.languageCounts) .setPositiveButton(android.R.string.ok, (dialogInterface, i) -> { - mSectionsPagerAdapter.libraryFragment.libraryAdapter.updateNetworkLanguages(); - mSectionsPagerAdapter.libraryFragment.libraryAdapter.getFilter().filter(searchQuery); + //mSectionsPagerAdapter.libraryFragment.libraryAdapter.updateNetworkLanguages(); + //mSectionsPagerAdapter.libraryFragment.libraryAdapter.getFilter().filter(searchQuery); }) .show(); } diff --git a/app/src/main/java/org/kiwix/kiwixmobile/zim_manager/ZimManageViewModel.kt b/app/src/main/java/org/kiwix/kiwixmobile/zim_manager/ZimManageViewModel.kt index 674daa5f8..afaa87917 100644 --- a/app/src/main/java/org/kiwix/kiwixmobile/zim_manager/ZimManageViewModel.kt +++ b/app/src/main/java/org/kiwix/kiwixmobile/zim_manager/ZimManageViewModel.kt @@ -1,22 +1,35 @@ package org.kiwix.kiwixmobile.zim_manager +import android.app.Application import android.arch.lifecycle.MutableLiveData import android.arch.lifecycle.ViewModel import io.reactivex.Flowable import io.reactivex.disposables.CompositeDisposable import io.reactivex.disposables.Disposable import io.reactivex.functions.BiFunction +import io.reactivex.functions.Function4 import io.reactivex.processors.PublishProcessor import io.reactivex.schedulers.Schedulers +import org.kiwix.kiwixmobile.R.string import org.kiwix.kiwixmobile.database.BookDao import org.kiwix.kiwixmobile.database.DownloadDao +import org.kiwix.kiwixmobile.database.NetworkLanguageDao import org.kiwix.kiwixmobile.downloader.Downloader import org.kiwix.kiwixmobile.downloader.model.DownloadItem import org.kiwix.kiwixmobile.downloader.model.DownloadModel import org.kiwix.kiwixmobile.downloader.model.DownloadState.Successful import org.kiwix.kiwixmobile.downloader.model.DownloadStatus +import org.kiwix.kiwixmobile.extensions.registerReceiver +import org.kiwix.kiwixmobile.library.entity.LibraryNetworkEntity import org.kiwix.kiwixmobile.library.entity.LibraryNetworkEntity.Book +import org.kiwix.kiwixmobile.network.KiwixService import org.kiwix.kiwixmobile.zim_manager.fileselect_view.StorageObserver +import org.kiwix.kiwixmobile.zim_manager.library_view.adapter.Language +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.DividerItem +import java.util.LinkedList +import java.util.Locale import java.util.concurrent.TimeUnit.SECONDS import javax.inject.Inject @@ -40,30 +53,160 @@ import javax.inject.Inject class ZimManageViewModel @Inject constructor( private val downloadDao: DownloadDao, private val bookDao: BookDao, + private val languageDao: NetworkLanguageDao, private val downloader: Downloader, - private val storageObserver: StorageObserver + private val storageObserver: StorageObserver, + private val kiwixService: KiwixService, + private val context: Application, + private val connectivityBroadcastReceiver: ConnectivityBroadcastReceiver ) : ViewModel() { + val libraryItems: MutableLiveData> = MutableLiveData() val downloadItems: MutableLiveData> = MutableLiveData() val bookItems: MutableLiveData> = MutableLiveData() - val checkFileSystem = PublishProcessor.create() val deviceListIsRefreshing = MutableLiveData() + val libraryListIsRefreshing = MutableLiveData() + val networkStates = MutableLiveData() + + val requestFileSystemCheck = PublishProcessor.create() + val requestDownloadLibrary = PublishProcessor.create() private val compositeDisposable = CompositeDisposable() init { - val downloadStatuses = downloadStatuses() - val booksFromDao = books() - compositeDisposable.addAll( + compositeDisposable.addAll(*disposables()) + requestDownloadLibrary.onNext(Unit) + context.registerReceiver(connectivityBroadcastReceiver) + } + + override fun onCleared() { + compositeDisposable.clear() + context.unregisterReceiver(connectivityBroadcastReceiver) + super.onCleared() + } + + private fun disposables(): Array { + val downloads: Flowable> = downloadDao.downloads() + val downloadStatuses = downloadStatuses(downloads) + val booksFromDao: Flowable> = books() + val library = libraryFromNetwork() + return arrayOf( updateDownloadItems(downloadStatuses), removeCompletedDownloadsFromDb(downloadStatuses), updateBookItems(booksFromDao), - checkFileSystemForBooksOnRequest(booksFromDao) + checkFileSystemForBooksOnRequest(booksFromDao), + updateLibraryItems(booksFromDao, downloads, library), + updateActiveLanguages(library), + updateNetworkStates() ) } - private fun checkFileSystemForBooksOnRequest(booksFromDao: Flowable>): Disposable? { - return checkFileSystem + private fun updateNetworkStates() = + connectivityBroadcastReceiver.networkStates.subscribe( + networkStates::postValue, Throwable::printStackTrace + ) + + private fun libraryFromNetwork() = + Flowable.combineLatest( + requestDownloadLibrary, + connectivityBroadcastReceiver.networkStates.filter( + NetworkState.CONNECTED::equals + ), + BiFunction { _, _ -> Unit } + ) + .subscribeOn(Schedulers.io()) + .doOnNext { libraryListIsRefreshing.postValue(true) } + .switchMap { kiwixService.library } + .doOnError(Throwable::printStackTrace) + .onErrorResumeNext(Flowable.just(LibraryNetworkEntity().apply { book = LinkedList() })) + .doOnNext { libraryListIsRefreshing.postValue(false) } + + private fun updateLibraryItems( + booksFromDao: Flowable>, + downloads: Flowable>, + library: Flowable? + ) = Flowable.combineLatest( + booksFromDao, + downloads, + languageDao.activeLanguages().filter { it.isNotEmpty() }, + library, + Function4(this::combineLibrarySources) + ) + .subscribeOn(Schedulers.io()) + .subscribe( + libraryItems::postValue, + Throwable::printStackTrace + ) + + private fun updateActiveLanguages(library: Flowable) = library + .subscribeOn(Schedulers.io()) + .map { it.books } + .withLatestFrom( + languageDao.activeLanguages(), + BiFunction(this::combineToLanguageList) + ) + .subscribe( + languageDao::saveFilteredLanguages, + Throwable::printStackTrace + ) + + private fun combineToLanguageList( + booksFromNetwork: List, + activeLanguages: List + ): List { + val languagesFromNetwork = booksFromNetwork.distinctBy { it.language } + .map { it.language } + return Locale.getISOLanguages() + .map { Locale(it) } + .filter { languagesFromNetwork.contains(it.isO3Language) } + .map { locale -> + Language( + locale.isO3Language, + languageWasPreviouslyActiveOrIsPrimaryLanguage(activeLanguages, locale) + ) + } + .ifEmpty { listOf(Language(context.resources.configuration.locale.isO3Language, true)) } + } + + private fun languageWasPreviouslyActiveOrIsPrimaryLanguage( + activeLanguages: List, + locale: Locale + ) = activeLanguages.firstOrNull { it.languageCode == locale.isO3Language }?.let { true } + ?: isPrimaryLocale(locale) + + private fun isPrimaryLocale(locale: Locale) = + context.resources.configuration.locale.isO3Language == locale.isO3Language + + private fun combineLibrarySources( + booksOnFileSystem: List, + activeDownloads: List, + activeLanguages: List, + libraryNetworkEntity: LibraryNetworkEntity + ): List { + val downloadedBooksIds = booksOnFileSystem.map { it.id } + val downloadingBookIds = activeDownloads.map { it.bookId } + val activeLanguageCodes = activeLanguages.map { it.languageCode } + val booksUnfilteredByLanguage = libraryNetworkEntity.books + .filterNot { downloadedBooksIds.contains(it.id) } + .filterNot { downloadingBookIds.contains(it.id) } + .filterNot { it.url.contains("/stack_exchange/") }// Temp filter see #694 + return listOf( + DividerItem(Long.MAX_VALUE, context.getString(string.your_languages)), + *toBookItems( + booksUnfilteredByLanguage.filter { activeLanguageCodes.contains(it.language) } + ), + DividerItem(Long.MIN_VALUE, context.getString(string.other_languages)), + *toBookItems( + booksUnfilteredByLanguage.filterNot { activeLanguageCodes.contains(it.language) } + ) + ) + } + + private fun toBookItems(books: List) = + books.map { BookItem(it) }.toTypedArray() + + private fun checkFileSystemForBooksOnRequest(booksFromDao: Flowable>) = + requestFileSystemCheck .doOnNext { deviceListIsRefreshing.postValue(true) } .switchMap { updateBookDaoFromFilesystem(booksFromDao) @@ -73,20 +216,24 @@ class ZimManageViewModel @Inject constructor( bookDao::saveBooks, Throwable::printStackTrace ) - } private fun books() = bookDao.books() .subscribeOn(Schedulers.io()) .map { it.sortedBy { book -> book.title } } private fun updateBookDaoFromFilesystem(booksFromDao: Flowable>) = - storageObserver.booksOnFileSystem.withLatestFrom( - booksFromDao, - BiFunction, List, List> { booksFileSystem, booksDao -> - booksFileSystem.minus( - booksDao - ) - }) + storageObserver.booksOnFileSystem + .withLatestFrom( + booksFromDao, + BiFunction(this::removeBooksAlreadyInDao) + ) + + private fun removeBooksAlreadyInDao( + booksFromFileSystem: Collection, + booksFromDao: List + ) = booksFromFileSystem.minus( + booksFromDao + ) private fun updateBookItems(booksFromDao: Flowable>) = booksFromDao @@ -113,17 +260,13 @@ class ZimManageViewModel @Inject constructor( Throwable::printStackTrace ) - private fun downloadStatuses() = Flowable.combineLatest( - downloadDao.downloads(), - Flowable.interval(1, SECONDS), - BiFunction { downloadModels: List, _: Long -> downloadModels } - ) - .subscribeOn(Schedulers.io()) - .map(downloader::queryStatus) - .distinctUntilChanged() - - override fun onCleared() { - compositeDisposable.clear() - super.onCleared() - } + private fun downloadStatuses(downloads: Flowable>) = + Flowable.combineLatest( + downloads, + Flowable.interval(1, SECONDS), + BiFunction { downloadModels: List, _: Long -> downloadModels } + ) + .subscribeOn(Schedulers.io()) + .map(downloader::queryStatus) + .distinctUntilChanged() } \ No newline at end of file diff --git a/app/src/main/java/org/kiwix/kiwixmobile/zim_manager/fileselect_view/RescanDataAdapter.kt b/app/src/main/java/org/kiwix/kiwixmobile/zim_manager/fileselect_view/BooksAdapter.kt similarity index 75% rename from app/src/main/java/org/kiwix/kiwixmobile/zim_manager/fileselect_view/RescanDataAdapter.kt rename to app/src/main/java/org/kiwix/kiwixmobile/zim_manager/fileselect_view/BooksAdapter.kt index c852ab911..96129212a 100644 --- a/app/src/main/java/org/kiwix/kiwixmobile/zim_manager/fileselect_view/RescanDataAdapter.kt +++ b/app/src/main/java/org/kiwix/kiwixmobile/zim_manager/fileselect_view/BooksAdapter.kt @@ -8,11 +8,11 @@ import org.kiwix.kiwixmobile.library.entity.LibraryNetworkEntity.Book import org.kiwix.kiwixmobile.utils.BookUtils // The Adapter for the ListView for when the ListView is populated with the rescanned files -public class RescanDataAdapter( - val bookUtils: BookUtils, - val onItemClick: (Book) -> Unit, - val onItemLongClick: (Book) -> Unit -) : RecyclerView.Adapter() { +class BooksAdapter( + private val bookUtils: BookUtils, + private val onItemClick: (Book) -> Unit, + private val onItemLongClick: (Book) -> Unit +) : RecyclerView.Adapter() { init { setHasStableIds(true) @@ -29,12 +29,12 @@ public class RescanDataAdapter( override fun onCreateViewHolder( parent: ViewGroup, viewType: Int - ) = RescanViewHolder(parent.inflate(layout.library_item, false), bookUtils) + ) = BooksViewHolder(parent.inflate(layout.library_item, false), bookUtils) override fun getItemCount() = itemList.size override fun onBindViewHolder( - holder: RescanViewHolder, + holder: BooksViewHolder, position: Int ) { holder.bind(itemList[position], onItemClick, onItemLongClick) diff --git a/app/src/main/java/org/kiwix/kiwixmobile/zim_manager/fileselect_view/RescanViewHolder.kt b/app/src/main/java/org/kiwix/kiwixmobile/zim_manager/fileselect_view/BooksViewHolder.kt similarity index 83% rename from app/src/main/java/org/kiwix/kiwixmobile/zim_manager/fileselect_view/RescanViewHolder.kt rename to app/src/main/java/org/kiwix/kiwixmobile/zim_manager/fileselect_view/BooksViewHolder.kt index 1b0584009..2f74007de 100644 --- a/app/src/main/java/org/kiwix/kiwixmobile/zim_manager/fileselect_view/RescanViewHolder.kt +++ b/app/src/main/java/org/kiwix/kiwixmobile/zim_manager/fileselect_view/BooksViewHolder.kt @@ -2,9 +2,7 @@ package org.kiwix.kiwixmobile.zim_manager.fileselect_view import android.support.v7.widget.RecyclerView.ViewHolder import android.view.View -import android.view.View.GONE -import android.view.View.VISIBLE -import android.widget.TextView +import butterknife.internal.DebouncingOnClickListener import kotlinx.android.extensions.LayoutContainer import kotlinx.android.synthetic.main.library_item.creator import kotlinx.android.synthetic.main.library_item.date @@ -18,13 +16,15 @@ import kotlinx.android.synthetic.main.library_item.title import org.kiwix.kiwixmobile.KiwixApplication import org.kiwix.kiwixmobile.downloader.model.Base64String import org.kiwix.kiwixmobile.extensions.setBitmap +import org.kiwix.kiwixmobile.extensions.setTextAndVisibility import org.kiwix.kiwixmobile.library.entity.LibraryNetworkEntity.Book import org.kiwix.kiwixmobile.utils.BookUtils import org.kiwix.kiwixmobile.utils.NetworkUtils +import org.kiwix.kiwixmobile.zim_manager.library_view.adapter.MegaByte -class RescanViewHolder( +class BooksViewHolder( override val containerView: View, - val bookUtils: BookUtils + private val bookUtils: BookUtils ) : ViewHolder(containerView), LayoutContainer { fun bind( @@ -37,7 +37,7 @@ class RescanViewHolder( creator.setTextAndVisibility(book.creator) publisher.setTextAndVisibility(book.publisher) date.setTextAndVisibility(book.date) - size.setTextAndVisibility(book.size) + size.setTextAndVisibility(MegaByte(book.size).humanReadable) language.text = bookUtils.getLanguage(book.getLanguage()) fileName.text = NetworkUtils.parseURL( KiwixApplication.getInstance(), book.file.path @@ -52,13 +52,6 @@ class RescanViewHolder( return@setOnLongClickListener true } } - - private fun TextView.setTextAndVisibility(title: String?) = - if (title != null && title.isNotEmpty()) { - text = title - visibility = VISIBLE - } else { - visibility = GONE - } - } + + diff --git a/app/src/main/java/org/kiwix/kiwixmobile/zim_manager/fileselect_view/StorageObserver.kt b/app/src/main/java/org/kiwix/kiwixmobile/zim_manager/fileselect_view/StorageObserver.kt index 5827eefa9..6343913f8 100644 --- a/app/src/main/java/org/kiwix/kiwixmobile/zim_manager/fileselect_view/StorageObserver.kt +++ b/app/src/main/java/org/kiwix/kiwixmobile/zim_manager/fileselect_view/StorageObserver.kt @@ -10,15 +10,15 @@ import org.kiwix.kiwixmobile.utils.files.FileSearch.ResultListener import javax.inject.Inject class StorageObserver @Inject constructor( - val context: Context, - val sharedPreferenceUtil: SharedPreferenceUtil + private val context: Context, + private val sharedPreferenceUtil: SharedPreferenceUtil ) { private val _booksOnFileSystem = PublishProcessor.create>() val booksOnFileSystem = _booksOnFileSystem.distinctUntilChanged() .doOnSubscribe { scanFiles() } - fun scanFiles() { + private fun scanFiles() { FileSearch(context, object : ResultListener { val foundBooks = mutableSetOf() override fun onBookFound(book: Book) { diff --git a/app/src/main/java/org/kiwix/kiwixmobile/zim_manager/fileselect_view/ZimFileSelectFragment.kt b/app/src/main/java/org/kiwix/kiwixmobile/zim_manager/fileselect_view/ZimFileSelectFragment.kt index 2baae5f1c..7b6fc9ae1 100644 --- a/app/src/main/java/org/kiwix/kiwixmobile/zim_manager/fileselect_view/ZimFileSelectFragment.kt +++ b/app/src/main/java/org/kiwix/kiwixmobile/zim_manager/fileselect_view/ZimFileSelectFragment.kt @@ -63,8 +63,8 @@ class ZimFileSelectFragment : BaseFragment() { private lateinit var zimManageViewModel: ZimManageViewModel - private val rescanAdapter: RescanDataAdapter by lazy { - RescanDataAdapter( + private val booksAdapter: BooksAdapter by lazy { + BooksAdapter( bookUtils, this::open, this::tryToDelete ) } @@ -92,12 +92,12 @@ class ZimFileSelectFragment : BaseFragment() { .get(ZimManageViewModel::class.java) zim_swiperefresh.setOnRefreshListener(this::requestFileSystemCheck) zimfilelist.run { - adapter = rescanAdapter + adapter = booksAdapter layoutManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false) setHasFixedSize(true) } zimManageViewModel.bookItems.observe(this, Observer { - rescanAdapter.itemList = it!! + booksAdapter.itemList = it!! checkEmpty(it) }) zimManageViewModel.deviceListIsRefreshing.observe(this, Observer { @@ -151,7 +151,7 @@ class ZimFileSelectFragment : BaseFragment() { } private fun requestFileSystemCheck() { - zimManageViewModel.checkFileSystem.onNext(Unit) + zimManageViewModel.requestFileSystemCheck.onNext(Unit) } private fun open(it: Book) { diff --git a/app/src/main/java/org/kiwix/kiwixmobile/zim_manager/library_view/LibraryFragment.java b/app/src/main/java/org/kiwix/kiwixmobile/zim_manager/library_view/LibraryFragment.java deleted file mode 100644 index a3c000538..000000000 --- a/app/src/main/java/org/kiwix/kiwixmobile/zim_manager/library_view/LibraryFragment.java +++ /dev/null @@ -1,350 +0,0 @@ -/* - * Kiwix Android - * Copyright (C) 2018 Kiwix - * - * 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 . - */ -package org.kiwix.kiwixmobile.zim_manager.library_view; - -import android.app.AlertDialog; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.graphics.Color; -import android.net.ConnectivityManager; -import android.net.NetworkInfo; -import android.os.Bundle; -import android.support.design.widget.Snackbar; -import android.support.v4.app.FragmentManager; -import android.support.v4.widget.SwipeRefreshLayout; -import android.util.Log; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.AdapterView; -import android.widget.Button; -import android.widget.LinearLayout; -import android.widget.ListView; -import android.widget.TextView; -import android.widget.Toast; -import butterknife.BindView; -import butterknife.ButterKnife; -import eu.mhutti1.utils.storage.StorageDevice; -import eu.mhutti1.utils.storage.support.StorageSelectDialog; -import java.io.File; -import java.util.ArrayList; -import java.util.LinkedList; -import java.util.List; -import javax.inject.Inject; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.kiwix.kiwixmobile.KiwixMobileActivity; -import org.kiwix.kiwixmobile.R; -import org.kiwix.kiwixmobile.base.BaseFragment; -import org.kiwix.kiwixmobile.di.components.ActivityComponent; -import org.kiwix.kiwixmobile.di.components.ApplicationComponent; -import org.kiwix.kiwixmobile.downloader.DownloadService; -import org.kiwix.kiwixmobile.downloader.Downloader; -import org.kiwix.kiwixmobile.library.LibraryAdapter; -import org.kiwix.kiwixmobile.utils.NetworkUtils; -import org.kiwix.kiwixmobile.utils.SharedPreferenceUtil; -import org.kiwix.kiwixmobile.utils.StyleUtils; -import org.kiwix.kiwixmobile.utils.TestingUtils; -import org.kiwix.kiwixmobile.zim_manager.ZimManageActivity; - -import static android.view.View.GONE; -import static org.kiwix.kiwixmobile.library.entity.LibraryNetworkEntity.Book; - -public class LibraryFragment extends BaseFragment - implements AdapterView.OnItemClickListener, StorageSelectDialog.OnSelectListener, LibraryViewCallback { - - @BindView(R.id.library_list) - ListView libraryList; - - @BindView(R.id.network_permission_text) - TextView networkText; - @BindView(R.id.network_permission_button) - Button permissionButton; - - public LinearLayout llLayout; - - @BindView(R.id.library_swiperefresh) - SwipeRefreshLayout swipeRefreshLayout; - - private ArrayList books = new ArrayList<>(); - - public static DownloadService mService = new DownloadService(); - public LibraryAdapter libraryAdapter; - - @Inject - ConnectivityManager conMan; - - @Inject - Downloader downloader; - - private ZimManageActivity faActivity; - - public static NetworkBroadcastReceiver networkBroadcastReceiver; - - public static List downloadingBooks = new ArrayList<>(); - - public static boolean isReceiverRegistered = false; - - @Inject - LibraryPresenter presenter; - - @Inject - SharedPreferenceUtil sharedPreferenceUtil; - - @Override - public void inject(@NotNull ActivityComponent activityComponent) { - activityComponent.inject(this); - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - TestingUtils.bindResource(LibraryFragment.class); - llLayout = (LinearLayout) inflater.inflate(R.layout.activity_library, container, false); - ButterKnife.bind(this, llLayout); - presenter.attachView(this); - - networkText = llLayout.findViewById(R.id.network_text); - - faActivity = (ZimManageActivity) super.getActivity(); - swipeRefreshLayout.setOnRefreshListener(() -> refreshFragment()); - libraryAdapter = new LibraryAdapter(super.getContext()); - libraryList.setAdapter(libraryAdapter); - - - - NetworkInfo network = conMan.getActiveNetworkInfo(); - if (network == null || !network.isConnected()) { - displayNoNetworkConnection(); - } - - networkBroadcastReceiver = new NetworkBroadcastReceiver(); - faActivity.registerReceiver(networkBroadcastReceiver, new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION)); - isReceiverRegistered = true; - - presenter.loadRunningDownloadsFromDb(); - return llLayout; - } - - @Override - public void onStop() { - if (isReceiverRegistered) { - faActivity.unregisterReceiver(networkBroadcastReceiver); - isReceiverRegistered = false; - } - super.onStop(); - } - - @Override - public void showBooks(LinkedList books) { - if (books == null) { - displayNoItemsAvailable(); - return; - } - - Log.i("kiwix-showBooks", "Contains:" + books.size()); - libraryAdapter.setAllBooks(books); - if (faActivity.searchView != null) { - libraryAdapter.getFilter().filter( - faActivity.searchView.getQuery(), - i -> stopScanningContent()); - } else { - libraryAdapter.getFilter().filter("", i -> stopScanningContent()); - } - libraryAdapter.notifyDataSetChanged(); - libraryList.setOnItemClickListener(this); - } - - @Override - public void displayNoNetworkConnection() { - if (books.size() != 0) { - Toast.makeText(super.getActivity(), R.string.no_network_connection, Toast.LENGTH_LONG).show(); - return; - } - - networkText.setText(R.string.no_network_connection); - networkText.setVisibility(View.VISIBLE); - permissionButton.setVisibility(GONE); - swipeRefreshLayout.setRefreshing(false); - swipeRefreshLayout.setEnabled(false); - libraryList.setVisibility(View.INVISIBLE); - TestingUtils.unbindResource(LibraryFragment.class); - } - - @Override - public void displayNoItemsFound() { - networkText.setText(R.string.no_items_msg); - networkText.setVisibility(View.VISIBLE); - permissionButton.setVisibility(GONE); - swipeRefreshLayout.setRefreshing(false); - TestingUtils.unbindResource(LibraryFragment.class); - } - - @Override - public void displayNoItemsAvailable() { - if (books.size() != 0) { - Toast.makeText(super.getActivity(), R.string.no_items_available, Toast.LENGTH_LONG).show(); - return; - } - - networkText.setText(R.string.no_items_available); - networkText.setVisibility(View.VISIBLE); - permissionButton.setVisibility(View.GONE); - swipeRefreshLayout.setRefreshing(false); - TestingUtils.unbindResource(LibraryFragment.class); - } - - @Override - public void displayScanningContent() { - if (!swipeRefreshLayout.isRefreshing()) { - networkText.setVisibility(GONE); - permissionButton.setVisibility(GONE); - swipeRefreshLayout.setEnabled(true); - swipeRefreshLayout.setRefreshing(true); - TestingUtils.bindResource(LibraryFragment.class); - } - } - - - @Override - public void stopScanningContent() { - networkText.setVisibility(GONE); - permissionButton.setVisibility(GONE); - swipeRefreshLayout.setRefreshing(false); - TestingUtils.unbindResource(LibraryFragment.class); - } - - public void refreshFragment() { - NetworkInfo network = conMan.getActiveNetworkInfo(); - if (network == null || !network.isConnected()) { - Toast.makeText(super.getActivity(), R.string.no_network_connection, Toast.LENGTH_LONG).show(); - swipeRefreshLayout.setRefreshing(false); - return; - } - networkBroadcastReceiver.onReceive(super.getActivity(), null); - } - - @Override - public void onItemClick(AdapterView parent, View view, int position, long id) { - if (!libraryAdapter.isDivider(position)) { - if (getSpaceAvailable() - < Long.parseLong(((Book) (parent.getAdapter().getItem(position))).getSize()) * 1024f) { - Toast.makeText(super.getActivity(), getString(R.string.download_no_space) - + "\n" + getString(R.string.space_available) + " " - + LibraryUtils.bytesToHuman(getSpaceAvailable()), Toast.LENGTH_LONG).show(); - Snackbar snackbar = Snackbar.make(libraryList, - getString(R.string.download_change_storage), - Snackbar.LENGTH_LONG) - .setAction(getString(R.string.open), v -> { - FragmentManager fm = getFragmentManager(); - StorageSelectDialog dialogFragment = new StorageSelectDialog(); - Bundle b = new Bundle(); - b.putString(StorageSelectDialog.STORAGE_DIALOG_INTERNAL, getResources().getString(R.string.internal_storage)); - b.putString(StorageSelectDialog.STORAGE_DIALOG_EXTERNAL, getResources().getString(R.string.external_storage)); - b.putInt(StorageSelectDialog.STORAGE_DIALOG_THEME, StyleUtils.dialogStyle()); - dialogFragment.setArguments(b); - dialogFragment.setOnSelectListener(this); - dialogFragment.show(fm, getResources().getString(R.string.pref_storage)); - }); - snackbar.setActionTextColor(Color.WHITE); - snackbar.show(); - return; - } - - // if (DownloadFragment.mDownloadFiles - // .containsValue(KIWIX_ROOT + StorageUtils.getFileNameFromUrl(((Book) parent.getAdapter() - // .getItem(position)).getUrl()))) { - // Toast.makeText(super.getActivity(), getString(R.string.zim_already_downloading), Toast.LENGTH_LONG) - // .show(); - // } else { - // - // NetworkInfo network = conMan.getActiveNetworkInfo(); - // if (network == null || !network.isConnected()) { - // Toast.makeText(super.getActivity(), getString(R.string.no_network_connection), Toast.LENGTH_LONG) - // .show(); - // return; - // } - - if (KiwixMobileActivity.wifiOnly && !NetworkUtils.isWiFi(getContext())) { - new AlertDialog.Builder(getContext()) - .setTitle(R.string.wifi_only_title) - .setMessage(R.string.wifi_only_msg) - .setPositiveButton(R.string.yes, (dialog, i) -> { - sharedPreferenceUtil.putPrefWifiOnly(false); - KiwixMobileActivity.wifiOnly = false; - downloadFile((Book) parent.getAdapter().getItem(position)); - }) - .setNegativeButton(R.string.no, (dialog, i) -> { - }) - .show(); - } else { - downloadFile((Book) parent.getAdapter().getItem(position)); - } - } - } - - @Override - public void downloadFile(Book book) { - downloader.download(book); - // downloadingBooks.add(book); - // if (libraryAdapter != null && faActivity != null && faActivity.searchView != null) { - // libraryAdapter.getFilter().filter(faActivity.searchView.getQuery()); - // } - // Toast.makeText(super.getActivity(), getString(R.string.download_started_library), Toast.LENGTH_LONG) - // .show(); - // - // ZimManageActivity manage = (ZimManageActivity) super.getActivity(); - // manage.displayDownloadInterface(); - } - - public long getSpaceAvailable() { - return new File(sharedPreferenceUtil.getPrefStorage()).getFreeSpace(); - } - - @Override - public void selectionCallback(StorageDevice storageDevice) { - sharedPreferenceUtil.putPrefStorage(storageDevice.getName()); - if (storageDevice.isInternal()) { - sharedPreferenceUtil.putPrefStorageTitle(getResources().getString(R.string.internal_storage)); - } else { - sharedPreferenceUtil.putPrefStorageTitle(getResources().getString(R.string.external_storage)); - } - } - - - public class NetworkBroadcastReceiver extends BroadcastReceiver { - @Override - public void onReceive(Context context, Intent intent) { - NetworkInfo network = conMan.getActiveNetworkInfo(); - - if (network == null || !network.isConnected()) { - displayNoNetworkConnection(); - } - - if ((books == null || books.isEmpty()) && network != null && network.isConnected()) { - presenter.loadBooks(); - permissionButton.setVisibility(GONE); - networkText.setVisibility(GONE); - libraryList.setVisibility(View.VISIBLE); - } - - } - } -} diff --git a/app/src/main/java/org/kiwix/kiwixmobile/zim_manager/library_view/LibraryFragment.kt b/app/src/main/java/org/kiwix/kiwixmobile/zim_manager/library_view/LibraryFragment.kt new file mode 100644 index 000000000..41bd3ddd0 --- /dev/null +++ b/app/src/main/java/org/kiwix/kiwixmobile/zim_manager/library_view/LibraryFragment.kt @@ -0,0 +1,232 @@ +/* + * Kiwix Android + * Copyright (C) 2018 Kiwix + * + * 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 . + */ +package org.kiwix.kiwixmobile.zim_manager.library_view + +import android.arch.lifecycle.Observer +import android.arch.lifecycle.ViewModelProvider +import android.arch.lifecycle.ViewModelProviders +import android.content.Context +import android.net.ConnectivityManager +import android.os.Bundle +import android.support.v7.widget.LinearLayoutManager +import android.view.LayoutInflater +import android.view.View +import android.view.View.GONE +import android.view.View.VISIBLE +import android.view.ViewGroup +import eu.mhutti1.utils.storage.StorageDevice +import eu.mhutti1.utils.storage.support.StorageSelectDialog +import kotlinx.android.synthetic.main.activity_library.libraryErrorText +import kotlinx.android.synthetic.main.activity_library.libraryList +import kotlinx.android.synthetic.main.activity_library.librarySwipeRefresh +import org.kiwix.kiwixmobile.KiwixMobileActivity +import org.kiwix.kiwixmobile.R +import org.kiwix.kiwixmobile.R.string +import org.kiwix.kiwixmobile.base.BaseFragment +import org.kiwix.kiwixmobile.di.components.ActivityComponent +import org.kiwix.kiwixmobile.downloader.Downloader +import org.kiwix.kiwixmobile.extensions.snack +import org.kiwix.kiwixmobile.extensions.toast +import org.kiwix.kiwixmobile.library.entity.LibraryNetworkEntity.Book +import org.kiwix.kiwixmobile.utils.BookUtils +import org.kiwix.kiwixmobile.utils.DialogShower +import org.kiwix.kiwixmobile.utils.KiwixDialog.YesNoDialog.WifiOnly +import org.kiwix.kiwixmobile.utils.NetworkUtils +import org.kiwix.kiwixmobile.utils.SharedPreferenceUtil +import org.kiwix.kiwixmobile.utils.StyleUtils +import org.kiwix.kiwixmobile.utils.TestingUtils +import org.kiwix.kiwixmobile.zim_manager.NetworkState +import org.kiwix.kiwixmobile.zim_manager.NetworkState.CONNECTED +import org.kiwix.kiwixmobile.zim_manager.NetworkState.NOT_CONNECTED +import org.kiwix.kiwixmobile.zim_manager.ZimManageViewModel +import org.kiwix.kiwixmobile.zim_manager.library_view.adapter.LibraryAdapter +import org.kiwix.kiwixmobile.zim_manager.library_view.adapter.LibraryDelegate.BookDelegate +import org.kiwix.kiwixmobile.zim_manager.library_view.adapter.LibraryDelegate.DividerDelegate +import org.kiwix.kiwixmobile.zim_manager.library_view.adapter.LibraryListItem +import org.kiwix.kiwixmobile.zim_manager.library_view.adapter.LibraryListItem.BookItem +import java.io.File +import javax.inject.Inject + +class LibraryFragment : BaseFragment() { + + @Inject lateinit var conMan: ConnectivityManager + @Inject lateinit var downloader: Downloader + @Inject lateinit var sharedPreferenceUtil: SharedPreferenceUtil + @Inject lateinit var dialogShower: DialogShower + @Inject lateinit var viewModelFactory: ViewModelProvider.Factory + @Inject lateinit var bookUtils: BookUtils + private lateinit var zimManageViewModel: ZimManageViewModel + + private val libraryAdapter: LibraryAdapter by lazy { + LibraryAdapter( + delegates = *arrayOf(BookDelegate(bookUtils, this::onBookItemClick), DividerDelegate) + ) + } + + private val spaceAvailable: Long + get() = File(sharedPreferenceUtil.prefStorage).freeSpace + + private val noWifiWithWifiOnlyPreferenceSet + get() = sharedPreferenceUtil.prefWifiOnly && !NetworkUtils.isWiFi(context!!) + + private val isNotConnected get() = conMan.activeNetworkInfo?.isConnected?.not() ?: true + + override fun inject(activityComponent: ActivityComponent) { + activityComponent.inject(this) + } + + override fun onAttach(context: Context?) { + super.onAttach(context) + zimManageViewModel = ViewModelProviders.of(activity!!, viewModelFactory) + .get(ZimManageViewModel::class.java) + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + TestingUtils.bindResource(LibraryFragment::class.java) + return inflater.inflate(R.layout.activity_library, container, false) + } + + override fun onViewCreated( + view: View, + savedInstanceState: Bundle? + ) { + super.onViewCreated(view, savedInstanceState) + librarySwipeRefresh.setOnRefreshListener { refreshFragment() } + libraryList.run { + adapter = libraryAdapter + layoutManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false) + setHasFixedSize(true) + } + zimManageViewModel.libraryItems.observe(this, Observer(this::onLibraryItemsChange)) + zimManageViewModel.libraryListIsRefreshing.observe( + this, Observer(this::onRefreshStateChange) + ) + zimManageViewModel.networkStates.observe(this, Observer(this::onNetworkStateChange)) + } + + private fun onRefreshStateChange(isRefreshing: Boolean?) { + librarySwipeRefresh.isRefreshing = isRefreshing!! + } + + private fun onNetworkStateChange(networkState: NetworkState?) { + when (networkState) { + CONNECTED -> { + } + NOT_CONNECTED -> { + if (libraryAdapter.itemCount > 0) { + context.toast(R.string.no_network_connection) + } else { + libraryErrorText.setText(R.string.no_network_connection) + libraryErrorText.visibility = VISIBLE + } + } + } + } + + private fun onLibraryItemsChange(it: List?) { + libraryAdapter.itemList = it!! + if (it.isEmpty()) { + libraryErrorText.setText( + if (isNotConnected) R.string.no_network_connection + else R.string.no_items_msg + ) + libraryErrorText.visibility = VISIBLE + TestingUtils.unbindResource(LibraryFragment::class.java) + } else { + libraryErrorText.visibility = GONE + } + } + + private fun refreshFragment() { + if (isNotConnected) { + context.toast(R.string.no_network_connection) + } else { + zimManageViewModel.requestDownloadLibrary.onNext(Unit) + } + } + + private fun downloadFile(book: Book) { + downloader.download(book) + } + + private fun storeDeviceInPreferences(storageDevice: StorageDevice) { + sharedPreferenceUtil.putPrefStorage(storageDevice.name) + sharedPreferenceUtil.putPrefStorageTitle( + getString( + if (storageDevice.isInternal) R.string.internal_storage + else R.string.external_storage + ) + ) + } + + private fun onBookItemClick(item: BookItem) { + when { + notEnoughSpaceAvailable(item) -> { + context.toast( + getString(R.string.download_no_space) + + "\n" + getString(R.string.space_available) + " " + + LibraryUtils.bytesToHuman(spaceAvailable) + ) + libraryList.snack( + R.string.download_change_storage, + R.string.open, + this::showStorageSelectDialog + ) + return + } + isNotConnected -> { + context.toast(R.string.no_network_connection) + return + } + noWifiWithWifiOnlyPreferenceSet -> { + dialogShower.show(WifiOnly, { + sharedPreferenceUtil.putPrefWifiOnly(false) + KiwixMobileActivity.wifiOnly = false + downloadFile(item.book) + }) + return + } + else -> downloadFile(item.book) + } + } + + private fun notEnoughSpaceAvailable(item: BookItem) = + spaceAvailable < item.book.size.toLong() * 1024f + + private fun showStorageSelectDialog() { + val dialogFragment = StorageSelectDialog() + dialogFragment.arguments = Bundle().apply { + putString( + StorageSelectDialog.STORAGE_DIALOG_INTERNAL, + getString(string.internal_storage) + ) + putString( + StorageSelectDialog.STORAGE_DIALOG_EXTERNAL, + getString(string.external_storage) + ) + putInt(StorageSelectDialog.STORAGE_DIALOG_THEME, StyleUtils.dialogStyle()) + } + dialogFragment.setOnSelectListener(this::storeDeviceInPreferences) + dialogFragment.show(fragmentManager, getString(string.pref_storage)) + } +} + diff --git a/app/src/main/java/org/kiwix/kiwixmobile/zim_manager/library_view/LibraryPresenter.java b/app/src/main/java/org/kiwix/kiwixmobile/zim_manager/library_view/LibraryPresenter.java deleted file mode 100644 index 2d5a3cff3..000000000 --- a/app/src/main/java/org/kiwix/kiwixmobile/zim_manager/library_view/LibraryPresenter.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Kiwix Android - * Copyright (C) 2018 Kiwix - * - * 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 . - */ -package org.kiwix.kiwixmobile.zim_manager.library_view; - -import android.util.Log; -import io.reactivex.android.schedulers.AndroidSchedulers; -import javax.inject.Inject; -import org.kiwix.kiwixmobile.base.BasePresenter; -import org.kiwix.kiwixmobile.database.BookDao; -import org.kiwix.kiwixmobile.library.entity.LibraryNetworkEntity; -import org.kiwix.kiwixmobile.network.KiwixService; - -/** - * Created by EladKeyshawn on 06/04/2017. - */ - -public class LibraryPresenter extends BasePresenter { - - @Inject - KiwixService kiwixService; - - @Inject - BookDao bookDao; - - @Inject - public LibraryPresenter() { - } - - void loadBooks() { - getMvpView().displayScanningContent(); - kiwixService.getLibrary() - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(library -> getMvpView().showBooks(library.getBooks()), error -> { - String msg = error.getLocalizedMessage(); - Log.w("kiwixLibrary", "Error loading books:" + (msg != null ? msg : "(null)")); - getMvpView().displayNoItemsFound(); - }); - } - - void loadRunningDownloadsFromDb() { - for (LibraryNetworkEntity.Book book : bookDao.getDownloadingBooks()) { - // if (!DownloadFragment.mDownloads.containsValue(book)) { - book.url = book.remoteUrl; - getMvpView().downloadFile(book); - // } - } - } - -} diff --git a/app/src/main/java/org/kiwix/kiwixmobile/zim_manager/library_view/adapter/AbsDelegateAdapter.kt b/app/src/main/java/org/kiwix/kiwixmobile/zim_manager/library_view/adapter/AbsDelegateAdapter.kt new file mode 100644 index 000000000..64e28fa9d --- /dev/null +++ b/app/src/main/java/org/kiwix/kiwixmobile/zim_manager/library_view/adapter/AbsDelegateAdapter.kt @@ -0,0 +1,38 @@ +package org.kiwix.kiwixmobile.zim_manager.library_view.adapter + +import android.support.v7.widget.RecyclerView +import android.view.ViewGroup + +/* + * Kiwix Android + * Copyright (C) 2018 Kiwix + * + * 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 . + */ +interface AbsDelegateAdapter : AdapterDelegate { + + override fun bind( + viewHolder: RecyclerView.ViewHolder, + itemToBind: SUPERTYPE + ) { + onBindViewHolder(itemToBind as INSTANCE, viewHolder as VIEWHOLDER) + } + + override fun createViewHolder(parent: ViewGroup): VIEWHOLDER + + fun onBindViewHolder( + item: INSTANCE, + holder: VIEWHOLDER + ) +} diff --git a/app/src/main/java/org/kiwix/kiwixmobile/zim_manager/library_view/adapter/AdapterDelegate.kt b/app/src/main/java/org/kiwix/kiwixmobile/zim_manager/library_view/adapter/AdapterDelegate.kt new file mode 100644 index 000000000..036c3565a --- /dev/null +++ b/app/src/main/java/org/kiwix/kiwixmobile/zim_manager/library_view/adapter/AdapterDelegate.kt @@ -0,0 +1,16 @@ +package org.kiwix.kiwixmobile.zim_manager.library_view.adapter + +import android.support.v7.widget.RecyclerView.ViewHolder +import android.view.ViewGroup + +interface AdapterDelegate { + fun createViewHolder(parent: ViewGroup): ViewHolder + + fun bind( + viewHolder: ViewHolder, + itemToBind: T + ) + + fun isFor(item: T): Boolean + +} diff --git a/app/src/main/java/org/kiwix/kiwixmobile/zim_manager/library_view/adapter/AdapterDelegateManager.kt b/app/src/main/java/org/kiwix/kiwixmobile/zim_manager/library_view/adapter/AdapterDelegateManager.kt new file mode 100644 index 000000000..d55f87bc6 --- /dev/null +++ b/app/src/main/java/org/kiwix/kiwixmobile/zim_manager/library_view/adapter/AdapterDelegateManager.kt @@ -0,0 +1,38 @@ +package org.kiwix.kiwixmobile.zim_manager.library_view.adapter + +import android.support.v4.util.SparseArrayCompat +import android.support.v7.widget.RecyclerView +import android.view.ViewGroup + +class AdapterDelegateManager() { + fun addDelegate(delegate: AdapterDelegate) { + delegates.put(delegates.size(), delegate) + } + + fun createViewHolder( + parent: ViewGroup, + viewType: Int + ) = delegates[viewType].createViewHolder(parent) + + fun onBindViewHolder( + libraryListItem: T, + holder: RecyclerView.ViewHolder + ) { + delegates[holder.itemViewType].bind(holder, libraryListItem) + } + + fun getViewTypeFor(item: T) = delegates.keyAt(getDelegateIndexFor(item)) + + private fun getDelegateIndexFor(item: T): Int { + for (index in 0..delegates.size()) { + val valueAt = delegates.valueAt(index) + if (valueAt.isFor(item)) { + return index; + } + } + throw RuntimeException("No delegate registered for $item") + } + + var delegates: SparseArrayCompat> = SparseArrayCompat() + +} diff --git a/app/src/main/java/org/kiwix/kiwixmobile/zim_manager/library_view/adapter/Language.kt b/app/src/main/java/org/kiwix/kiwixmobile/zim_manager/library_view/adapter/Language.kt new file mode 100644 index 000000000..857cefb79 --- /dev/null +++ b/app/src/main/java/org/kiwix/kiwixmobile/zim_manager/library_view/adapter/Language.kt @@ -0,0 +1,24 @@ +package org.kiwix.kiwixmobile.zim_manager.library_view.adapter + +import java.util.Locale + +class Language constructor( + locale: Locale, + var active: Boolean?, + var language: String = locale.displayLanguage, + var languageLocalized: String = locale.getDisplayLanguage(locale), + var languageCode: String = locale.isO3Language, + var languageCodeISO2: String = locale.language +) +{ + + constructor( + languageCode: String, + active: Boolean? + ) : this(Locale(languageCode), active) { + } + + override fun equals(obj: Any?): Boolean { + return (obj as Language).language == language && obj.active == active + } +} \ No newline at end of file diff --git a/app/src/main/java/org/kiwix/kiwixmobile/zim_manager/library_view/adapter/LibraryAdapter.kt b/app/src/main/java/org/kiwix/kiwixmobile/zim_manager/library_view/adapter/LibraryAdapter.kt new file mode 100644 index 000000000..9c19fd1e3 --- /dev/null +++ b/app/src/main/java/org/kiwix/kiwixmobile/zim_manager/library_view/adapter/LibraryAdapter.kt @@ -0,0 +1,82 @@ +/* + * Copyright 2013 Rashiq Ahmad + * + * 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.zim_manager.library_view.adapter + +import android.support.v7.widget.RecyclerView +import android.view.ViewGroup + +class LibraryAdapter( + private val delegateManager: AdapterDelegateManager = AdapterDelegateManager(), + vararg delegates: AdapterDelegate +) : RecyclerView.Adapter() { + + init { + delegates.forEach { + delegateManager.addDelegate(it) + } + setHasStableIds(true) + } + + var itemList: List = mutableListOf() + set(value) { + field = value + notifyDataSetChanged() + } + + override fun onCreateViewHolder( + parent: ViewGroup, + viewType: Int + ) = delegateManager.createViewHolder(parent, viewType) + + override fun getItemCount() = itemList.size + + override fun onBindViewHolder( + holder: RecyclerView.ViewHolder, + position: Int + ) { + delegateManager.onBindViewHolder(itemList[position], holder) + } + + override fun getItemId(position: Int) = itemList[position].id + + override fun getItemViewType(position: Int) = + delegateManager.getViewTypeFor(itemList[position]) + +} + +//private Observable getMatches(Book b, String s) { +// StringBuilder text = new StringBuilder(); +// text.append(b.getTitle()).append("|").append(b.getDescription()).append("|") +// .append(parseURL(context, b.getUrl())).append("|"); +// if (bookUtils.localeMap.containsKey(b.getLanguage())) { +// text.append(bookUtils.localeMap.get(b.getLanguage()).getDisplayLanguage()).append("|"); +// } +// String[] words = s.toLowerCase().split("\\s+"); +// b.searchMatches = Observable.fromArray(words) +// .filter(text.toString().toLowerCase()::contains) +// .count() +// .blockingGet() +// .intValue(); +// if (b.searchMatches > 0) { +// return Observable.just(b); +// } else { +// return Observable.empty(); +// } +//}} \ No newline at end of file diff --git a/app/src/main/java/org/kiwix/kiwixmobile/zim_manager/library_view/adapter/LibraryDelegate.kt b/app/src/main/java/org/kiwix/kiwixmobile/zim_manager/library_view/adapter/LibraryDelegate.kt new file mode 100644 index 000000000..6e281eddd --- /dev/null +++ b/app/src/main/java/org/kiwix/kiwixmobile/zim_manager/library_view/adapter/LibraryDelegate.kt @@ -0,0 +1,68 @@ +/* + * Kiwix Android + * Copyright (C) 2018 Kiwix + * + * 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 . + */ +package org.kiwix.kiwixmobile.zim_manager.library_view.adapter + +import android.view.ViewGroup +import org.kiwix.kiwixmobile.R +import org.kiwix.kiwixmobile.R.layout +import org.kiwix.kiwixmobile.extensions.inflate +import org.kiwix.kiwixmobile.utils.BookUtils +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.LibraryViewHolder.LibraryBookViewHolder +import org.kiwix.kiwixmobile.zim_manager.library_view.adapter.LibraryViewHolder.LibraryDividerViewHolder + +sealed class LibraryDelegate> : + AbsDelegateAdapter { + + class BookDelegate( + private val bookUtils: BookUtils, + private val clickAction: (BookItem) -> Unit + ) : LibraryDelegate() { + + override fun createViewHolder(parent: ViewGroup) = + LibraryBookViewHolder( + parent.inflate(R.layout.library_item, false), + bookUtils, + clickAction + ) + + override fun onBindViewHolder( + item: BookItem, + holder: LibraryBookViewHolder + ) { + holder.bind(item) + } + + override fun isFor(item: LibraryListItem) = item is BookItem + } + + object DividerDelegate : LibraryDelegate() { + override fun createViewHolder(parent: ViewGroup) = + LibraryDividerViewHolder(parent.inflate(layout.library_divider, false)) + + override fun onBindViewHolder( + item: DividerItem, + holder: LibraryDividerViewHolder + ) { + holder.bind(item) + } + + override fun isFor(item: LibraryListItem) = item is DividerItem + } +} \ No newline at end of file diff --git a/app/src/main/java/org/kiwix/kiwixmobile/zim_manager/library_view/adapter/LibraryListItem.kt b/app/src/main/java/org/kiwix/kiwixmobile/zim_manager/library_view/adapter/LibraryListItem.kt new file mode 100644 index 000000000..a8da03d9d --- /dev/null +++ b/app/src/main/java/org/kiwix/kiwixmobile/zim_manager/library_view/adapter/LibraryListItem.kt @@ -0,0 +1,17 @@ +package org.kiwix.kiwixmobile.zim_manager.library_view.adapter + +import org.kiwix.kiwixmobile.library.entity.LibraryNetworkEntity.Book + +sealed class LibraryListItem() { + abstract val id: Long + + data class DividerItem constructor( + override val id: Long, + val text: String + ) : LibraryListItem() + + data class BookItem( + val book: Book, + override val id: Long = book.id.hashCode().toLong() + ) : LibraryListItem() +} diff --git a/app/src/main/java/org/kiwix/kiwixmobile/zim_manager/library_view/adapter/LibraryViewHolder.kt b/app/src/main/java/org/kiwix/kiwixmobile/zim_manager/library_view/adapter/LibraryViewHolder.kt new file mode 100644 index 000000000..b94d630b1 --- /dev/null +++ b/app/src/main/java/org/kiwix/kiwixmobile/zim_manager/library_view/adapter/LibraryViewHolder.kt @@ -0,0 +1,63 @@ +package org.kiwix.kiwixmobile.zim_manager.library_view.adapter + +import android.support.v7.widget.RecyclerView +import android.view.View +import butterknife.internal.DebouncingOnClickListener +import kotlinx.android.extensions.LayoutContainer +import kotlinx.android.synthetic.main.library_divider.divider_text +import kotlinx.android.synthetic.main.library_item.creator +import kotlinx.android.synthetic.main.library_item.date +import kotlinx.android.synthetic.main.library_item.description +import kotlinx.android.synthetic.main.library_item.favicon +import kotlinx.android.synthetic.main.library_item.fileName +import kotlinx.android.synthetic.main.library_item.language +import kotlinx.android.synthetic.main.library_item.publisher +import kotlinx.android.synthetic.main.library_item.size +import kotlinx.android.synthetic.main.library_item.title +import org.kiwix.kiwixmobile.KiwixApplication +import org.kiwix.kiwixmobile.downloader.model.Base64String +import org.kiwix.kiwixmobile.extensions.setBitmap +import org.kiwix.kiwixmobile.extensions.setTextAndVisibility +import org.kiwix.kiwixmobile.utils.BookUtils +import org.kiwix.kiwixmobile.utils.NetworkUtils +import org.kiwix.kiwixmobile.zim_manager.library_view.adapter.LibraryListItem.BookItem +import org.kiwix.kiwixmobile.zim_manager.library_view.adapter.LibraryListItem.DividerItem + +sealed class LibraryViewHolder(override val containerView: View) : RecyclerView.ViewHolder( + containerView +), LayoutContainer { + + abstract fun bind(item: T) + + class LibraryBookViewHolder( + view: View, + private val bookUtils: BookUtils, + private val clickAction: (BookItem) -> Unit + ) : LibraryViewHolder(view) { + override fun bind(item: BookItem) { + title.setTextAndVisibility(item.book.title) + description.setTextAndVisibility(item.book.description) + creator.setTextAndVisibility(item.book.creator) + publisher.setTextAndVisibility(item.book.publisher) + date.setTextAndVisibility(item.book.date) + size.setTextAndVisibility(MegaByte(item.book.size).humanReadable) + language.text = bookUtils.getLanguage(item.book.getLanguage()) + fileName.text = NetworkUtils.parseURL( + KiwixApplication.getInstance(), item.book.file?.path ?: "" + ) + favicon.setBitmap(Base64String(item.book.favicon)) + + containerView.setOnClickListener { + clickAction.invoke(item) + } + } + + } + + class LibraryDividerViewHolder(view: View) : LibraryViewHolder(view) { + override fun bind(item: DividerItem) { + divider_text.text = item.text + } + } + +} diff --git a/app/src/main/java/org/kiwix/kiwixmobile/zim_manager/library_view/adapter/MegaByte.kt b/app/src/main/java/org/kiwix/kiwixmobile/zim_manager/library_view/adapter/MegaByte.kt new file mode 100644 index 000000000..df84bddb0 --- /dev/null +++ b/app/src/main/java/org/kiwix/kiwixmobile/zim_manager/library_view/adapter/MegaByte.kt @@ -0,0 +1,16 @@ +package org.kiwix.kiwixmobile.zim_manager.library_view.adapter + +import java.text.DecimalFormat + +inline class MegaByte(val megabyteString: String?) { + val humanReadable + get() = megabyteString?.toLongOrNull()?.let { + val units = arrayOf("KB", "MB", "GB", "TB") + val conversion = (Math.log10(it.toDouble()) / Math.log10(1024.0)).toInt() + (DecimalFormat("#,##0.#") + .format(it / Math.pow(1024.0, conversion.toDouble())) + + " " + + units[conversion]) + } ?: "" + +} \ No newline at end of file diff --git a/app/src/main/res/layout/activity_library.xml b/app/src/main/res/layout/activity_library.xml index d6341ca6e..95d287554 100644 --- a/app/src/main/res/layout/activity_library.xml +++ b/app/src/main/res/layout/activity_library.xml @@ -1,61 +1,42 @@ - - - + android:animateLayoutChanges="true" + tools:context=".zim_manager.library_view.LibraryFragment" + > - - - - + android:visibility="gone" + tools:visibility="visible" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" + /> -