mirror of
https://github.com/kiwix/kiwix-android.git
synced 2025-09-22 12:03:09 -04:00
Merge branch 'develop' into feature/LocalFileTransfer
This commit is contained in:
commit
45cfc3d579
13
.travis.yml
13
.travis.yml
@ -4,6 +4,8 @@ jdk: oraclejdk8
|
||||
|
||||
sudo: required
|
||||
|
||||
dist: trusty
|
||||
|
||||
env:
|
||||
global:
|
||||
# switch glibc to a memory conserving mode
|
||||
@ -38,8 +40,6 @@ cache:
|
||||
|
||||
android:
|
||||
components:
|
||||
- tools
|
||||
- platform-tools
|
||||
- build-tools-28.0.3
|
||||
- android-28
|
||||
- extra-android-m2repository
|
||||
@ -63,10 +63,11 @@ after_success:
|
||||
- ./gradlew kiwixtestUploadKiwix
|
||||
|
||||
after_failure:
|
||||
- export LOG_DIR = ${TRAVIS_HOME}/build/kiwix/kiwix-android/app/build/outputs/reports/androidTests/connected/flavors/KIWIX/
|
||||
- lynx --dump ${LOG_DIR}com.android.builder.testing.ConnectedDevice.html
|
||||
- lynx --dump ${LOG_DIR}com.android.builder.testing.html
|
||||
- lynx --dump ${LOG_DIR}org.kiwix.kiwixmobile.tests.BasicTest.html;
|
||||
- export REPORT_DIR=${TRAVIS_HOME}/build/kiwix/kiwix-android/app/build/outputs/reports/
|
||||
- export LOG_DIR=${REPORT_DIR}androidTests/connected/flavors/KIWIX/
|
||||
- lynx -dump ${LOG_DIR}index.html
|
||||
- lynx -dump ${REPORT_DIR}lint-results-kiwixDebug.html
|
||||
|
||||
|
||||
before_deploy:
|
||||
# - export APP_CHANGELOG=$(cat app/src/kiwix/play/release-notes/en-US/default.txt)
|
||||
|
@ -46,11 +46,13 @@ Our branching strategy is based on [this article](https://nvie.com/posts/a-succe
|
||||
|
||||
+ **master** a history of releases, once merged to from develop and tagged we create a release on the play store & GitHub releases.
|
||||
+ **develop** the actively worked on next release of the app, what we branch off of while working on new features and what we merge into upon feature completion
|
||||
+ **feature/** or feature/\<username\>/ any branch under this directory is an actively developed feature, feature branches culminate in a PR, are merged and deleted. Typically a feature branch is off of develop and into develop but in rare scenarios if there is an issue in production a branch may be made off master to fix this issue, this type of feature branch must be merged to develop and master before being deleted.
|
||||
+ **feature/** or feature/\<username\>/ any branch under this directory is an actively developed feature, feature branches culminate in a PR, are merged and deleted.
|
||||
Typically a feature branch is off of develop and into develop but in rare scenarios if there is an issue in production a branch may be made off master to fix this issue, this type of feature branch must be merged to develop and master before being deleted.
|
||||
Branch names should be in the format **#\<issue-number\>-kebab-case-title**
|
||||
|
||||
All branches should have distinct history and should be visually easy to follow, for this reason only preform merge commits when merging code either by PR or when synchronising.
|
||||
All branches should have distinct history and should be visually easy to follow, for this reason only perform merge commits when merging code either by PR or when synchronising.
|
||||
|
||||
Rebasing should be avoided.
|
||||
If you wish to rebase you should be following the [Golden Rule](https://www.atlassian.com/git/tutorials/merging-vs-rebasing#the-golden-rule-of-rebasing) and ahere to the advice in the heading [Aside: Rebase as cleanup is awesome in the coding lifecycle](https://www.atlassian.com/git/articles/git-team-workflows-merge-or-rebase).
|
||||
|
||||
### Building
|
||||
|
||||
|
@ -30,10 +30,10 @@ We utilize different build variants (flavours) to build various different versio
|
||||
- [Butterknife](https://jakewharton.github.io/butterknife/) - View "injection" library for Android
|
||||
- [Mockito](https://github.com/mockito/mockito) - Most popular Mocking framework for unit tests written in Java
|
||||
- [RxJava](https://github.com/ReactiveX/RxJava) - Reactive Extensions for the JVM – a library for composing asynchronous and event-based programs using observable sequences for the Java VM.
|
||||
- [ObjectBox] (https://github.com/objectbox/objectbox-java) - Reactive NoSQL Databse to replace SquiDb
|
||||
- [MockK] (https://github.com/mockk/mockk) - Kotlin mocking library that allows mocking of final classes by default.
|
||||
- [JUnit5] (https://github.com/junit-team/junit5/) - The next generation of JUnit
|
||||
- [AssertJ] (https://github.com/joel-costigliola/assertj-core) - Fluent assertions for test code
|
||||
- [ObjectBox](https://github.com/objectbox/objectbox-java) - Reactive NoSQL Databse to replace SquiDb
|
||||
- [MockK](https://github.com/mockk/mockk) - Kotlin mocking library that allows mocking of final classes by default.
|
||||
- [JUnit5](https://github.com/junit-team/junit5/) - The next generation of JUnit
|
||||
- [AssertJ](https://github.com/joel-costigliola/assertj-core) - Fluent assertions for test code
|
||||
|
||||
## Contributing
|
||||
|
||||
|
@ -56,9 +56,6 @@ dependencies {
|
||||
archs = file("../kiwixlib/src/main/jniLibs").list()
|
||||
}
|
||||
|
||||
// Storage Devices
|
||||
implementation "eu.mhutti1.utils.storage:android-storage-devices:0.6.2"
|
||||
|
||||
// Android Support
|
||||
implementation "androidx.appcompat:appcompat:$appCompatVersion"
|
||||
implementation "com.google.android.material:material:$materialVersion"
|
||||
@ -125,9 +122,8 @@ dependencies {
|
||||
implementation "io.reactivex.rxjava2:rxjava:$rxJavaVersion"
|
||||
|
||||
// Leak canary
|
||||
releaseImplementation 'com.squareup.leakcanary:leakcanary-android-no-op:1.6.3'
|
||||
androidTestImplementation 'com.squareup.leakcanary:leakcanary-android-no-op:1.6.3'
|
||||
debugImplementation 'com.squareup.leakcanary:leakcanary-android:1.6.3'
|
||||
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.0-alpha-2'
|
||||
|
||||
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlinVersion"
|
||||
implementation "android.arch.lifecycle:extensions:1.1.1"
|
||||
@ -233,8 +229,11 @@ android {
|
||||
//TODO stop ignoring
|
||||
ignore 'MissingTranslation',
|
||||
'CheckResult',
|
||||
'LabelFor',
|
||||
'DuplicateStrings',
|
||||
'LogConditional'
|
||||
warning 'UnknownNullness',
|
||||
'SelectableText'
|
||||
baseline file("lint-baseline.xml")
|
||||
}
|
||||
|
||||
@ -274,6 +273,9 @@ android {
|
||||
|
||||
// Release Type
|
||||
release {
|
||||
minifyEnabled true
|
||||
shrinkResources true
|
||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||
signingConfig signingConfigs.release
|
||||
buildConfigField "String", "KIWIX_DOWNLOAD_URL", "\"http://mirror.download.kiwix.org/\""
|
||||
buildConfigField "boolean", "KIWIX_ERROR_ACTIVITY", "true"
|
||||
@ -408,15 +410,6 @@ android {
|
||||
javaMaxHeapSize "4g"
|
||||
}
|
||||
|
||||
/*
|
||||
Add back once proguard is configured
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled true
|
||||
proguardFiles getDefaultProguardFile("proguard-android.txt")
|
||||
}
|
||||
}
|
||||
*/
|
||||
androidExtensions {
|
||||
experimental = true
|
||||
}
|
||||
|
37
app/proguard-rules.pro
vendored
Normal file
37
app/proguard-rules.pro
vendored
Normal file
@ -0,0 +1,37 @@
|
||||
# Add project specific ProGuard rules here.
|
||||
# You can control the set of applied configuration files using the
|
||||
# proguardFiles setting in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
|
||||
# Uncomment this to preserve the line number information for
|
||||
# debugging stack traces.
|
||||
#-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
# If you keep the line number information, uncomment this to
|
||||
# hide the original source file name.
|
||||
#-renamesourcefileattribute SourceFile
|
||||
|
||||
########################
|
||||
# Kiwix specific rules #
|
||||
########################
|
||||
|
||||
#keep everything in kiwixlib
|
||||
-keep class org.kiwix.kiwixlib.** { *; }
|
||||
|
||||
## SimpleXml
|
||||
-dontwarn com.bea.xml.stream.**
|
||||
-dontwarn org.simpleframework.xml.stream.**
|
||||
-keep class org.simpleframework.xml.**{ *; }
|
||||
-keepclassmembers,allowobfuscation class * {
|
||||
@org.simpleframework.xml.* <fields>;
|
||||
@org.simpleframework.xml.* <init>(...);
|
||||
}
|
@ -26,21 +26,15 @@ import com.schibsted.spain.barista.interaction.BaristaSleepInteractions;
|
||||
import com.schibsted.spain.barista.rule.BaristaRule;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.kiwix.kiwixmobile.R;
|
||||
import org.kiwix.kiwixmobile.intro.IntroActivity;
|
||||
import org.kiwix.kiwixmobile.zim_manager.ZimManageActivity;
|
||||
|
||||
import static androidx.test.InstrumentationRegistry.getInstrumentation;
|
||||
import static androidx.test.espresso.Espresso.onView;
|
||||
import static androidx.test.espresso.Espresso.openActionBarOverflowOrOptionsMenu;
|
||||
import static androidx.test.espresso.action.ViewActions.click;
|
||||
import static androidx.test.espresso.action.ViewActions.closeSoftKeyboard;
|
||||
import static androidx.test.espresso.action.ViewActions.replaceText;
|
||||
import static androidx.test.espresso.action.ViewActions.swipeLeft;
|
||||
import static androidx.test.espresso.action.ViewActions.swipeRight;
|
||||
import static androidx.test.espresso.assertion.ViewAssertions.doesNotExist;
|
||||
import static androidx.test.espresso.assertion.ViewAssertions.matches;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.isChecked;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
|
||||
@ -52,12 +46,11 @@ import static org.hamcrest.Matchers.allOf;
|
||||
import static org.hamcrest.core.IsNull.notNullValue;
|
||||
import static org.kiwix.kiwixmobile.testutils.Matcher.childAtPosition;
|
||||
import static org.kiwix.kiwixmobile.testutils.TestUtils.TEST_PAUSE_MS;
|
||||
import static org.kiwix.kiwixmobile.testutils.ViewActions.setChecked;
|
||||
|
||||
public class LanguageActivityTest {
|
||||
|
||||
@Rule
|
||||
public BaristaRule<IntroActivity> activityTestRule = BaristaRule.create(IntroActivity.class);
|
||||
public BaristaRule<ZimManageActivity> activityTestRule = BaristaRule.create(ZimManageActivity.class);
|
||||
@Rule
|
||||
public GrantPermissionRule readPermissionRule =
|
||||
GrantPermissionRule.grant(Manifest.permission.READ_EXTERNAL_STORAGE);
|
||||
@ -72,46 +65,8 @@ public class LanguageActivityTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore("Broken in 2.5")//TODO: Fix in 3.0
|
||||
public void testLanguageActivity() {
|
||||
BaristaSleepInteractions.sleep(TEST_PAUSE_MS);
|
||||
onView(withId(R.id.get_started)).perform(click());
|
||||
BaristaSleepInteractions.sleep(TEST_PAUSE_MS);
|
||||
|
||||
// Open the Library
|
||||
openActionBarOverflowOrOptionsMenu(getInstrumentation().getTargetContext());
|
||||
onView(withText("Get Content")).perform(click());
|
||||
BaristaSleepInteractions.sleep(TEST_PAUSE_MS);
|
||||
|
||||
ViewInteraction viewPager = onView(allOf(withId(R.id.manageViewPager),
|
||||
childAtPosition(allOf(withId(R.id.zim_manager_main_activity),
|
||||
childAtPosition(withId(android.R.id.content), 0)), 1),
|
||||
isDisplayed()));
|
||||
|
||||
// Verify that the "Choose Language" and the "Search" buttons are present only in the "online" tab
|
||||
onView(withContentDescription("Search")).check(matches(notNullValue()));
|
||||
// Test that the language selection screen does not open if the "Choose language" button is clicked, while the data is being loaded
|
||||
onView(withContentDescription("Choose a language")).check(matches(notNullValue()))
|
||||
.perform(click());
|
||||
|
||||
viewPager.perform(swipeRight());
|
||||
onView(withContentDescription("Search")).check(doesNotExist());
|
||||
onView(withContentDescription("Choose a language")).check(doesNotExist());
|
||||
viewPager.perform(swipeLeft());
|
||||
viewPager.perform(swipeLeft());
|
||||
onView(withContentDescription("Search")).check(doesNotExist());
|
||||
onView(withContentDescription("Choose a language")).check(doesNotExist());
|
||||
|
||||
viewPager.perform(swipeRight());
|
||||
|
||||
// Verify that the library is still visible
|
||||
onView(allOf(withText("Library"), childAtPosition(allOf(withId(R.id.toolbar),
|
||||
childAtPosition(withId(R.id.toolbar_layout), 0)), 1), isDisplayed()));
|
||||
|
||||
// Make sure that the zim list has been loaded
|
||||
//IdlingRegistry.getInstance().register(LibraryFragment.IDLING_RESOURCE);
|
||||
onView(allOf(isDisplayed(), withText("Selected languages:"))).check(matches(notNullValue()));
|
||||
|
||||
onView(withText("Online")).perform(click());
|
||||
// Open the Language Activity
|
||||
onView(withContentDescription("Choose a language")).perform(click());
|
||||
|
||||
@ -143,18 +98,7 @@ public class LanguageActivityTest {
|
||||
0),
|
||||
isDisplayed()));
|
||||
|
||||
// Get a reference to the checkbox associated with the top unselected language
|
||||
checkBox2 = onView(
|
||||
allOf(withId(R.id.item_language_checkbox),
|
||||
childAtPosition(
|
||||
childAtPosition(
|
||||
withId(R.id.recycler_view),
|
||||
2),
|
||||
0),
|
||||
isDisplayed()));
|
||||
|
||||
// Initialise the language checkbox
|
||||
checkBox2.perform(setChecked(false));
|
||||
|
||||
onView(withContentDescription("Save languages")).perform(click());
|
||||
|
||||
@ -164,8 +108,6 @@ public class LanguageActivityTest {
|
||||
onView(withId(R.id.search_src_text)).perform(replaceText(language2), closeSoftKeyboard());
|
||||
BaristaSleepInteractions.sleep(TEST_PAUSE_MS);
|
||||
|
||||
// Initialise the language checkbox
|
||||
checkBox2.perform(setChecked(false));
|
||||
onView(withContentDescription("Clear query")).perform(click());
|
||||
// Collapse the search view to go to the full list of languages
|
||||
onView(withContentDescription("Collapse")).perform(click());
|
||||
@ -178,7 +120,7 @@ public class LanguageActivityTest {
|
||||
onView(withId(R.id.search_src_text)).perform(replaceText(language1), closeSoftKeyboard());
|
||||
BaristaSleepInteractions.sleep(TEST_PAUSE_MS);
|
||||
|
||||
checkBox2.perform(click());
|
||||
checkBox1.perform(click());
|
||||
onView(withContentDescription("Clear query")).perform(click());
|
||||
// Collapse the search view to go to the full list of languages
|
||||
onView(withContentDescription("Collapse")).perform(click());
|
||||
@ -187,7 +129,7 @@ public class LanguageActivityTest {
|
||||
onView(withId(R.id.search_src_text)).perform(replaceText(language2), closeSoftKeyboard());
|
||||
BaristaSleepInteractions.sleep(TEST_PAUSE_MS);
|
||||
|
||||
checkBox2.perform(click());
|
||||
checkBox1.perform(click());
|
||||
onView(withContentDescription("Clear query")).perform(click());
|
||||
onView(withContentDescription("Collapse")).perform(click());
|
||||
|
||||
@ -216,7 +158,7 @@ public class LanguageActivityTest {
|
||||
onView(withId(R.id.search_src_text)).perform(replaceText(language1), closeSoftKeyboard());
|
||||
BaristaSleepInteractions.sleep(TEST_PAUSE_MS);
|
||||
|
||||
checkBox2.check(matches(not(isChecked())));
|
||||
checkBox1.check(matches(not(isChecked())));
|
||||
|
||||
onView(withContentDescription("Clear query")).perform(click());
|
||||
onView(withContentDescription("Collapse")).perform(click());
|
||||
@ -224,7 +166,7 @@ public class LanguageActivityTest {
|
||||
onView(withId(R.id.search_src_text)).perform(replaceText(language2), closeSoftKeyboard());
|
||||
BaristaSleepInteractions.sleep(TEST_PAUSE_MS);
|
||||
|
||||
checkBox2.check(matches(not(isChecked())));
|
||||
checkBox1.check(matches(not(isChecked())));
|
||||
onView(withContentDescription("Clear query")).perform(click());
|
||||
onView(withContentDescription("Collapse")).perform(click());
|
||||
onView(withContentDescription("Navigate up")).perform(click());
|
||||
@ -237,7 +179,7 @@ public class LanguageActivityTest {
|
||||
onView(withId(R.id.search_src_text)).perform(replaceText(language1), closeSoftKeyboard());
|
||||
BaristaSleepInteractions.sleep(TEST_PAUSE_MS);
|
||||
|
||||
checkBox2.perform(click());
|
||||
checkBox1.perform(click());
|
||||
|
||||
onView(withContentDescription("Clear query")).perform(click());
|
||||
onView(withContentDescription("Collapse")).perform(click());
|
||||
@ -245,7 +187,7 @@ public class LanguageActivityTest {
|
||||
onView(withId(R.id.search_src_text)).perform(replaceText(language2), closeSoftKeyboard());
|
||||
BaristaSleepInteractions.sleep(TEST_PAUSE_MS);
|
||||
|
||||
checkBox2.perform(click());
|
||||
checkBox1.perform(click());
|
||||
onView(withContentDescription("Clear query")).perform(click());
|
||||
onView(withContentDescription("Collapse")).perform(click());
|
||||
onView(withContentDescription("Save languages")).perform(click());
|
||||
|
@ -49,10 +49,6 @@ public class SettingsTest {
|
||||
withKey("pref_hidetoolbar")))
|
||||
.perform(click());
|
||||
|
||||
onData(allOf(
|
||||
is(instanceOf(Preference.class)),
|
||||
withKey("pref_bottomtoolbar")))
|
||||
.perform(click());
|
||||
|
||||
onData(allOf(
|
||||
is(instanceOf(Preference.class)),
|
||||
|
@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright 2016 Isaac Hutt <mhutti1@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
package eu.mhutti1.utils.storage;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
|
||||
class ExternalPaths {
|
||||
|
||||
@SuppressLint("SdCardPath") private static final String[] paths = {
|
||||
"/storage/sdcard0",
|
||||
"/storage/sdcard1",
|
||||
"/storage/extsdcard",
|
||||
"/storage/extSdCard",
|
||||
"/storage/sdcard0/external_sdcard",
|
||||
"/mnt/sdcard/external_sd",
|
||||
"/mnt/external_sd",
|
||||
"/mnt/media_rw/*",
|
||||
"/removable/microsd",
|
||||
"/mnt/emmc",
|
||||
"/storage/external_SD",
|
||||
"/storage/ext_sd",
|
||||
"/storage/removable/sdcard1",
|
||||
"/data/sdext",
|
||||
"/data/sdext2",
|
||||
"/data/sdext3",
|
||||
"/data/sdext2",
|
||||
"/data/sdext3",
|
||||
"/data/sdext4",
|
||||
"/sdcard",
|
||||
"/sdcard1",
|
||||
"/sdcard2",
|
||||
"/storage/microsd",
|
||||
"/mnt/extsd",
|
||||
"/extsd",
|
||||
"/mnt/sdcard",
|
||||
"/misc/android",
|
||||
};
|
||||
|
||||
public static String[] getPossiblePaths() {
|
||||
return paths;
|
||||
}
|
||||
}
|
164
app/src/main/java/eu/mhutti1/utils/storage/StorageDevice.java
Normal file
164
app/src/main/java/eu/mhutti1/utils/storage/StorageDevice.java
Normal file
@ -0,0 +1,164 @@
|
||||
/*
|
||||
* Copyright 2016 Isaac Hutt <mhutti1@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
package eu.mhutti1.utils.storage;
|
||||
|
||||
import android.os.Build;
|
||||
import android.os.StatFs;
|
||||
import android.util.Log;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.text.DecimalFormat;
|
||||
|
||||
public class StorageDevice {
|
||||
|
||||
// File object containing device path
|
||||
private final File mFile;
|
||||
|
||||
private final boolean mInternal;
|
||||
|
||||
private boolean mDuplicate = true;
|
||||
|
||||
public StorageDevice(String path, boolean internal) {
|
||||
mFile = new File(path);
|
||||
mInternal = internal;
|
||||
if (mFile.exists()) {
|
||||
createLocationCode();
|
||||
}
|
||||
}
|
||||
|
||||
public StorageDevice(File file, boolean internal) {
|
||||
mFile = file;
|
||||
mInternal = internal;
|
||||
if (mFile.exists()) {
|
||||
createLocationCode();
|
||||
}
|
||||
}
|
||||
|
||||
// Get device path
|
||||
public String getName() {
|
||||
return mFile.getPath();
|
||||
}
|
||||
|
||||
// Get available space on device
|
||||
public String getSize() {
|
||||
return bytesToHuman(getAvailableBytes());
|
||||
}
|
||||
|
||||
private Long getAvailableBytes() {
|
||||
StatFs statFs = new StatFs(mFile.getPath());
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
|
||||
return statFs.getBlockSizeLong() * statFs.getAvailableBlocksLong();
|
||||
} else {
|
||||
return (long) statFs.getBlockSize() * (long) statFs.getAvailableBlocks();
|
||||
}
|
||||
}
|
||||
|
||||
public String getTotalSize() {
|
||||
return bytesToHuman(getTotalBytes());
|
||||
}
|
||||
|
||||
// Get total space on device
|
||||
private Long getTotalBytes() {
|
||||
StatFs statFs = new StatFs((mFile.getPath()));
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
|
||||
return statFs.getBlockSizeLong() * statFs.getBlockCountLong();
|
||||
} else {
|
||||
return (long) statFs.getBlockSize() * (long) statFs.getBlockCount();
|
||||
}
|
||||
}
|
||||
|
||||
// Convert bytes to human readable form
|
||||
private static String bytesToHuman(long size) {
|
||||
long Kb = 1 * 1024;
|
||||
long Mb = Kb * 1024;
|
||||
long Gb = Mb * 1024;
|
||||
long Tb = Gb * 1024;
|
||||
long Pb = Tb * 1024;
|
||||
long Eb = Pb * 1024;
|
||||
|
||||
if (size < Kb) return floatForm(size) + " byte";
|
||||
if (size >= Kb && size < Mb) return floatForm((double) size / Kb) + " KB";
|
||||
if (size >= Mb && size < Gb) return floatForm((double) size / Mb) + " MB";
|
||||
if (size >= Gb && size < Tb) return floatForm((double) size / Gb) + " GB";
|
||||
if (size >= Tb && size < Pb) return floatForm((double) size / Tb) + " TB";
|
||||
if (size >= Pb && size < Eb) return floatForm((double) size / Pb) + " PB";
|
||||
if (size >= Eb) return floatForm((double) size / Eb) + " EB";
|
||||
|
||||
return "???";
|
||||
}
|
||||
|
||||
public boolean isInternal() {
|
||||
return mInternal;
|
||||
}
|
||||
|
||||
public File getPath() {
|
||||
return mFile;
|
||||
}
|
||||
|
||||
private static String floatForm(double d) {
|
||||
return new DecimalFormat("#.#").format(d);
|
||||
}
|
||||
|
||||
// Create unique file to identify duplicate devices.
|
||||
private void createLocationCode() {
|
||||
if (!getLocationCodeFromFolder(mFile)) {
|
||||
File locationCode = new File(mFile.getPath(), ".storageLocationMarker");
|
||||
try {
|
||||
locationCode.createNewFile();
|
||||
FileWriter fw = new FileWriter(locationCode);
|
||||
fw.write(mFile.getPath());
|
||||
fw.close();
|
||||
} catch (IOException e) {
|
||||
Log.d("android-storage-devices", "Unable to create marker file, duplicates may be listed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check if there is already a device code in our path
|
||||
private boolean getLocationCodeFromFolder(File folder) {
|
||||
File locationCode = new File(folder.getPath(), ".storageLocationMarker");
|
||||
if (locationCode.exists()) {
|
||||
try ( BufferedReader br = new BufferedReader(new FileReader(locationCode))){
|
||||
if (br.readLine().equals(mFile.getPath())) {
|
||||
mDuplicate = false;
|
||||
} else {
|
||||
mDuplicate = true;
|
||||
return true;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
String path = folder.getPath();
|
||||
String parent = path.substring(0, path.lastIndexOf("/"));
|
||||
if (parent.equals("")) {
|
||||
mDuplicate = false;
|
||||
return false;
|
||||
}
|
||||
return getLocationCodeFromFolder(new File(parent));
|
||||
}
|
||||
|
||||
public boolean isDuplicate() {
|
||||
return mDuplicate;
|
||||
}
|
||||
}
|
@ -0,0 +1,131 @@
|
||||
/*
|
||||
* Copyright 2016 Isaac Hutt <mhutti1@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
package eu.mhutti1.utils.storage;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Environment;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import java.io.File;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.nio.channels.FileLock;
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class StorageDeviceUtils {
|
||||
|
||||
public static ArrayList<StorageDevice> getStorageDevices(Context context, boolean writable) {
|
||||
ArrayList<StorageDevice> storageDevices = new ArrayList<>();
|
||||
|
||||
// Add as many possible mount points as we know about
|
||||
|
||||
// Only add this device if its very likely that we have missed a users sd card
|
||||
if (Environment.isExternalStorageEmulated()) {
|
||||
// This is our internal storage directory
|
||||
storageDevices.add(new StorageDevice(
|
||||
generalisePath(Environment.getExternalStorageDirectory().getPath(), writable), true));
|
||||
} else {
|
||||
// This is an external storage directory
|
||||
storageDevices.add(new StorageDevice(
|
||||
generalisePath(Environment.getExternalStorageDirectory().getPath(), writable), false));
|
||||
}
|
||||
|
||||
// These are possible manufacturer sdcard mount points
|
||||
|
||||
String[] paths = ExternalPaths.getPossiblePaths();
|
||||
|
||||
for (String path : paths) {
|
||||
if (path.endsWith("*")) {
|
||||
File root = new File(path.substring(0, path.length() - 1));
|
||||
File[] directories = root.listFiles(file -> file.isDirectory());
|
||||
if (directories != null) {
|
||||
for (File dir : directories) {
|
||||
storageDevices.add(new StorageDevice(dir, false));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
storageDevices.add(new StorageDevice(path, false));
|
||||
}
|
||||
}
|
||||
|
||||
// Iterate through any sdcards manufacturers may have specified
|
||||
for (File file : ContextCompat.getExternalFilesDirs(context, "")) {
|
||||
if (file != null) {
|
||||
storageDevices.add(new StorageDevice(generalisePath(file.getPath(), writable), false));
|
||||
}
|
||||
}
|
||||
|
||||
// Check all devices exist, we can write to them if required and they are not duplicates
|
||||
return checkStorageValid(writable, storageDevices);
|
||||
}
|
||||
|
||||
// Remove app specific path from directories so that we can search them from the top
|
||||
private static String generalisePath(String path, boolean writable) {
|
||||
if (writable) {
|
||||
return path;
|
||||
}
|
||||
int endIndex = path.lastIndexOf("/Android/data/");
|
||||
if (endIndex != -1) {
|
||||
return path.substring(0, endIndex);
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
private static ArrayList<StorageDevice> checkStorageValid(boolean writable,
|
||||
ArrayList<StorageDevice> storageDevices) {
|
||||
ArrayList<StorageDevice> activeDevices = new ArrayList<>();
|
||||
ArrayList<StorageDevice> devicePaths = new ArrayList<>();
|
||||
for (StorageDevice device : storageDevices) {
|
||||
if (existsAndIsDirAndWritableIfRequiredAndNotDuplicate(writable, devicePaths, device)) {
|
||||
activeDevices.add(device);
|
||||
devicePaths.add(device);
|
||||
}
|
||||
}
|
||||
return activeDevices;
|
||||
}
|
||||
|
||||
private static boolean existsAndIsDirAndWritableIfRequiredAndNotDuplicate(boolean writable,
|
||||
ArrayList<StorageDevice> devicePaths, StorageDevice device) {
|
||||
final File devicePath = device.getPath();
|
||||
return devicePath.exists()
|
||||
&& devicePath.isDirectory()
|
||||
&& (canWrite(devicePath) || !writable)
|
||||
&& !device.isDuplicate()
|
||||
&& !devicePaths.contains(device);
|
||||
}
|
||||
|
||||
// Amazingly file.canWrite() does not always return the correct value
|
||||
private static boolean canWrite(File file) {
|
||||
final String filePath = file + "/test.txt";
|
||||
try {
|
||||
RandomAccessFile randomAccessFile = new RandomAccessFile(filePath, "rw");
|
||||
FileChannel fileChannel = randomAccessFile.getChannel();
|
||||
FileLock fileLock = fileChannel.lock();
|
||||
fileLock.release();
|
||||
fileChannel.close();
|
||||
randomAccessFile.close();
|
||||
return true;
|
||||
} catch (Exception ex) {
|
||||
return false;
|
||||
} finally {
|
||||
new File(filePath).delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright 2016 Isaac Hutt <mhutti1@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
package eu.mhutti1.utils.storage;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.TextView;
|
||||
import java.util.ArrayList;
|
||||
import org.kiwix.kiwixmobile.R;
|
||||
|
||||
class StorageSelectArrayAdapter extends ArrayAdapter<StorageDevice> {
|
||||
|
||||
private final String mInternal;
|
||||
|
||||
private final String mExternal;
|
||||
|
||||
public StorageSelectArrayAdapter(Context context, int resource, ArrayList<StorageDevice> devices,
|
||||
String internal, String external) {
|
||||
super(context, resource, devices);
|
||||
mInternal = internal;
|
||||
mExternal = external;
|
||||
}
|
||||
|
||||
@SuppressLint("SetTextI18n") @Override
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
|
||||
ViewHolder holder;
|
||||
if (convertView == null) {
|
||||
convertView = View.inflate(getContext(), R.layout.device_item, null);
|
||||
holder = new ViewHolder();
|
||||
holder.fileName = convertView.findViewById(R.id.file_name);
|
||||
holder.fileSize = convertView.findViewById(R.id.file_size);
|
||||
convertView.setTag(holder);
|
||||
} else {
|
||||
holder = (ViewHolder) convertView.getTag();
|
||||
}
|
||||
StorageDevice device = getItem(position);
|
||||
if (device.isInternal()) {
|
||||
holder.fileName.setText(mInternal);
|
||||
} else {
|
||||
holder.fileName.setText(mExternal);
|
||||
}
|
||||
holder.fileSize.setText(device.getSize() + " / " + device.getTotalSize());
|
||||
|
||||
return convertView;
|
||||
}
|
||||
|
||||
class ViewHolder {
|
||||
TextView fileName;
|
||||
TextView fileSize;
|
||||
}
|
||||
}
|
@ -0,0 +1,114 @@
|
||||
/*
|
||||
* Copyright 2016 Isaac Hutt <mhutti1@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
package eu.mhutti1.utils.storage;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ListView;
|
||||
import android.widget.TextView;
|
||||
import androidx.fragment.app.DialogFragment;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import java.io.File;
|
||||
import org.kiwix.kiwixmobile.R;
|
||||
|
||||
public class StorageSelectDialog extends DialogFragment implements ListView.OnItemClickListener {
|
||||
|
||||
// Activities/Fragments can create instances of a StorageSelectDialog and bind a listener to get its result
|
||||
|
||||
public static final String STORAGE_DIALOG_THEME = "THEME";
|
||||
|
||||
public static final String STORAGE_DIALOG_INTERNAL = "INTERNAL";
|
||||
|
||||
public static final String STORAGE_DIALOG_EXTERNAL = "EXTERNAL";
|
||||
|
||||
private StorageSelectArrayAdapter mAdapter;
|
||||
|
||||
private OnSelectListener mOnSelectListener;
|
||||
private String mTitle;
|
||||
|
||||
private String mInternal = "Internal";
|
||||
|
||||
private String mExternal = "External";
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
if (getArguments() != null) {
|
||||
// Set string values
|
||||
mInternal = getArguments().getString(STORAGE_DIALOG_INTERNAL, mInternal);
|
||||
mExternal = getArguments().getString(STORAGE_DIALOG_EXTERNAL, mExternal);
|
||||
// Set the theme to a supplied value
|
||||
if (getArguments().containsKey(STORAGE_DIALOG_THEME)) {
|
||||
setStyle(DialogFragment.STYLE_NORMAL, getArguments().getInt(STORAGE_DIALOG_THEME));
|
||||
}
|
||||
}
|
||||
super.onCreate(savedInstanceState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
View rootView = inflater.inflate(R.layout.storage_select_dialog, container, false);
|
||||
TextView title = rootView.findViewById(R.id.title);
|
||||
title.setText(mTitle);
|
||||
ListView listView = rootView.findViewById(R.id.device_list);
|
||||
mAdapter = new StorageSelectArrayAdapter(getActivity(), 0,
|
||||
StorageDeviceUtils.getStorageDevices(getActivity(), true), mInternal, mExternal);
|
||||
listView.setAdapter(mAdapter);
|
||||
listView.setOnItemClickListener(this);
|
||||
Button button = rootView.findViewById(R.id.button);
|
||||
final EditText editText = rootView.findViewById(R.id.editText);
|
||||
button.setOnClickListener(view -> {
|
||||
if (editText.getText().length() != 0) {
|
||||
String path = editText.getText().toString();
|
||||
if (new File(path).exists()) {
|
||||
mAdapter.add(new StorageDevice(path, false));
|
||||
}
|
||||
}
|
||||
});
|
||||
return rootView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
||||
if (mOnSelectListener != null) {
|
||||
mOnSelectListener.selectionCallback(mAdapter.getItem(position));
|
||||
}
|
||||
dismiss();
|
||||
}
|
||||
|
||||
public void setOnSelectListener(OnSelectListener selectListener) {
|
||||
mOnSelectListener = selectListener;
|
||||
}
|
||||
|
||||
public interface OnSelectListener {
|
||||
void selectionCallback(StorageDevice s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void show(FragmentManager fm, String text) {
|
||||
mTitle = text;
|
||||
super.show(fm, text);
|
||||
}
|
||||
}
|
@ -26,7 +26,6 @@ import android.util.Log;
|
||||
import androidx.appcompat.app.AppCompatDelegate;
|
||||
import androidx.multidex.MultiDexApplication;
|
||||
import com.jakewharton.threetenabp.AndroidThreeTen;
|
||||
import com.squareup.leakcanary.LeakCanary;
|
||||
import dagger.android.AndroidInjector;
|
||||
import dagger.android.DispatchingAndroidInjector;
|
||||
import dagger.android.HasActivityInjector;
|
||||
@ -73,11 +72,6 @@ public class KiwixApplication extends MultiDexApplication implements HasActivity
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
if (LeakCanary.isInAnalyzerProcess(this)) {
|
||||
// This process is dedicated to LeakCanary for heap analysis.
|
||||
// You should not init your app in this process.
|
||||
return;
|
||||
}
|
||||
AndroidThreeTen.init(this);
|
||||
if (isExternalStorageWritable()) {
|
||||
File appDirectory = new File(Environment.getExternalStorageDirectory() + "/Kiwix");
|
||||
@ -110,7 +104,6 @@ public class KiwixApplication extends MultiDexApplication implements HasActivity
|
||||
|
||||
Log.d("KIWIX", "Started KiwixApplication");
|
||||
applicationComponent.inject(this);
|
||||
LeakCanary.install(this);
|
||||
if (BuildConfig.DEBUG) {
|
||||
StrictMode.setThreadPolicy(buildThreadPolicy(new StrictMode.ThreadPolicy.Builder()));
|
||||
StrictMode.setVmPolicy(buildVmPolicy(new StrictMode.VmPolicy.Builder()));
|
||||
|
@ -92,7 +92,7 @@ public class BookmarksActivity extends BaseActivity implements BookmarksContract
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
presenter.attachView(this);
|
||||
setContentView(R.layout.activity_bookmarks_history_language);
|
||||
setContentView(R.layout.activity_bookmarks);
|
||||
setSupportActionBar(toolbar);
|
||||
|
||||
ActionBar actionBar = getSupportActionBar();
|
||||
|
@ -24,7 +24,7 @@ data class LanguageEntity(
|
||||
)
|
||||
|
||||
fun toLanguageModel() =
|
||||
Language(locale, active, occurencesOfLanguage)
|
||||
Language(locale, active, occurencesOfLanguage, id)
|
||||
}
|
||||
|
||||
class StringToLocaleConverter : PropertyConverter<Locale, String> {
|
||||
|
@ -12,7 +12,6 @@ import org.kiwix.kiwixmobile.history.HistoryModule;
|
||||
import org.kiwix.kiwixmobile.intro.IntroActivity;
|
||||
import org.kiwix.kiwixmobile.intro.IntroModule;
|
||||
import org.kiwix.kiwixmobile.language.LanguageActivity;
|
||||
import org.kiwix.kiwixmobile.language.LanguageModule;
|
||||
import org.kiwix.kiwixmobile.main.MainActivity;
|
||||
import org.kiwix.kiwixmobile.main.MainModule;
|
||||
import org.kiwix.kiwixmobile.search.SearchActivity;
|
||||
@ -61,7 +60,7 @@ public abstract class ActivityBindingModule {
|
||||
public abstract SplashActivity provideSplashActivity();
|
||||
|
||||
@PerActivity
|
||||
@ContributesAndroidInjector(modules = LanguageModule.class)
|
||||
@ContributesAndroidInjector
|
||||
public abstract LanguageActivity provideLanguageActivity();
|
||||
|
||||
@PerActivity
|
||||
|
@ -36,7 +36,7 @@ import org.kiwix.kiwixmobile.data.remote.UserAgentInterceptor;
|
||||
|
||||
@Provides @Singleton OkHttpClient provideOkHttpClient() {
|
||||
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
|
||||
logging.setLevel(BuildConfig.DEBUG ? Level.BODY : Level.BASIC);
|
||||
logging.setLevel(BuildConfig.DEBUG ? Level.BASIC : Level.NONE);
|
||||
|
||||
return new OkHttpClient().newBuilder().followRedirects(true).followSslRedirects(true)
|
||||
.connectTimeout(10, TimeUnit.SECONDS)
|
||||
|
@ -7,6 +7,7 @@ import dagger.Module
|
||||
import dagger.multibindings.IntoMap
|
||||
import org.kiwix.kiwixmobile.KiwixViewModelFactory
|
||||
import org.kiwix.kiwixmobile.di.ViewModelKey
|
||||
import org.kiwix.kiwixmobile.language.viewmodel.LanguageViewModel
|
||||
import org.kiwix.kiwixmobile.zim_manager.ZimManageViewModel
|
||||
|
||||
/*
|
||||
@ -31,7 +32,12 @@ abstract class ViewModelModule {
|
||||
@Binds
|
||||
@IntoMap
|
||||
@ViewModelKey(ZimManageViewModel::class)
|
||||
internal abstract fun bindUserViewModel(userViewModel: ZimManageViewModel): ViewModel
|
||||
internal abstract fun bindZimManageViewModel(zimManageViewModel: ZimManageViewModel): ViewModel
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@ViewModelKey(LanguageViewModel::class)
|
||||
internal abstract fun bindLanguageViewModel(languageViewModel: LanguageViewModel): ViewModel
|
||||
|
||||
@Binds
|
||||
internal abstract fun bindViewModelFactory(factory: KiwixViewModelFactory): ViewModelProvider.Factory
|
||||
|
@ -23,7 +23,6 @@ import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.ViewModelProviders
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import kotlinx.android.synthetic.main.layout_download_management.download_management_no_downloads
|
||||
@ -31,6 +30,7 @@ import kotlinx.android.synthetic.main.layout_download_management.zim_downloader_
|
||||
import org.kiwix.kiwixmobile.base.BaseFragment
|
||||
import org.kiwix.kiwixmobile.di.components.ActivityComponent
|
||||
import org.kiwix.kiwixmobile.downloader.model.DownloadItem
|
||||
import org.kiwix.kiwixmobile.extensions.viewModel
|
||||
import org.kiwix.kiwixmobile.utils.DialogShower
|
||||
import org.kiwix.kiwixmobile.utils.KiwixDialog.YesNoDialog.StopDownload
|
||||
import org.kiwix.kiwixmobile.utils.SharedPreferenceUtil
|
||||
@ -44,10 +44,10 @@ class DownloadFragment : BaseFragment() {
|
||||
@Inject lateinit var sharedPreferenceUtil: SharedPreferenceUtil
|
||||
@Inject lateinit var downloader: Downloader
|
||||
|
||||
private val zimManageViewModel: ZimManageViewModel by lazy {
|
||||
ViewModelProviders.of(activity!!, viewModelFactory)
|
||||
.get(ZimManageViewModel::class.java)
|
||||
private val zimManageViewModel by lazy {
|
||||
activity!!.viewModel<ZimManageViewModel>(viewModelFactory)
|
||||
}
|
||||
|
||||
private val downloadAdapter = DownloadAdapter {
|
||||
dialogShower.show(StopDownload, { downloader.cancelDownload(it) })
|
||||
}
|
||||
|
@ -1,10 +1,16 @@
|
||||
package org.kiwix.kiwixmobile.extensions
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import android.view.ActionMode
|
||||
import android.view.ActionMode.Callback
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.ViewModelProviders
|
||||
import org.kiwix.kiwixmobile.base.BaseActivity
|
||||
|
||||
fun Activity.startActionMode(
|
||||
menuId: Int,
|
||||
@ -41,3 +47,12 @@ fun Activity.startActionMode(
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
inline fun <reified T : Activity> Activity.start() {
|
||||
startActivity(Intent(this, T::class.java))
|
||||
}
|
||||
|
||||
inline fun <reified T : ViewModel> FragmentActivity.viewModel(viewModelFactory: ViewModelProvider.Factory) =
|
||||
ViewModelProviders.of(this, viewModelFactory)
|
||||
.get(T::class.java)
|
||||
|
||||
|
@ -106,7 +106,7 @@ public class HistoryActivity extends BaseActivity implements HistoryContract.Vie
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
presenter.attachView(this);
|
||||
setContentView(R.layout.activity_bookmarks_history_language);
|
||||
setContentView(R.layout.activity_history);
|
||||
setSupportActionBar(toolbar);
|
||||
|
||||
ActionBar actionBar = getSupportActionBar();
|
||||
|
@ -1,111 +0,0 @@
|
||||
package org.kiwix.kiwixmobile.language;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.widget.Toast;
|
||||
import androidx.appcompat.app.ActionBar;
|
||||
import androidx.appcompat.widget.SearchView;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import butterknife.BindView;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import javax.inject.Inject;
|
||||
import org.kiwix.kiwixmobile.R;
|
||||
import org.kiwix.kiwixmobile.base.BaseActivity;
|
||||
import org.kiwix.kiwixmobile.zim_manager.Language;
|
||||
|
||||
public class LanguageActivity extends BaseActivity implements LanguageContract.View {
|
||||
|
||||
public static final String LANGUAGE_LIST = "languages";
|
||||
private final ArrayList<Language> languages = new ArrayList<>();
|
||||
private final ArrayList<Language> allLanguages = new ArrayList<>();
|
||||
|
||||
@BindView(R.id.toolbar)
|
||||
Toolbar toolbar;
|
||||
@BindView(R.id.recycler_view)
|
||||
RecyclerView recyclerView;
|
||||
@Inject
|
||||
LanguageContract.Presenter presenter;
|
||||
|
||||
private LanguageAdapter languageAdapter;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
presenter.attachView(this);
|
||||
setContentView(R.layout.activity_bookmarks_history_language);
|
||||
setSupportActionBar(toolbar);
|
||||
|
||||
ActionBar actionBar = getSupportActionBar();
|
||||
if (actionBar != null) {
|
||||
actionBar.setDisplayHomeAsUpEnabled(true);
|
||||
actionBar.setHomeAsUpIndicator(R.drawable.ic_clear_white_24dp);
|
||||
actionBar.setTitle(R.string.select_languages);
|
||||
}
|
||||
|
||||
languages.addAll(getIntent().getParcelableArrayListExtra(LANGUAGE_LIST));
|
||||
allLanguages.addAll(languages);
|
||||
languageAdapter = new LanguageAdapter(languages);
|
||||
recyclerView.setAdapter(languageAdapter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
getMenuInflater().inflate(R.menu.menu_language, menu);
|
||||
MenuItem search = menu.findItem(R.id.menu_language_search);
|
||||
((SearchView) search.getActionView()).setOnQueryTextListener(
|
||||
new SearchView.OnQueryTextListener() {
|
||||
@Override
|
||||
public boolean onQueryTextSubmit(String query) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onQueryTextChange(String newText) {
|
||||
languages.clear();
|
||||
languages.addAll(allLanguages);
|
||||
presenter.filerLanguages(languages, newText);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case android.R.id.home:
|
||||
onBackPressed();
|
||||
return true;
|
||||
case R.id.menu_language_save:
|
||||
languages.clear();
|
||||
languages.addAll(allLanguages);
|
||||
presenter.saveLanguages(languages);
|
||||
|
||||
Toast.makeText(this, getString(R.string.languages_saved), Toast.LENGTH_SHORT).show();
|
||||
Intent intent = new Intent();
|
||||
intent.putParcelableArrayListExtra(LANGUAGE_LIST, languages);
|
||||
setResult(RESULT_OK, intent);
|
||||
finish();
|
||||
return true;
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
presenter.detachView();
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyLanguagesFiltered(List<Language> languages) {
|
||||
this.languages.clear();
|
||||
this.languages.addAll(languages);
|
||||
languageAdapter.categorizeLanguages();
|
||||
languageAdapter.notifyDataSetChanged();
|
||||
}
|
||||
}
|
@ -0,0 +1,110 @@
|
||||
package org.kiwix.kiwixmobile.language
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import androidx.appcompat.widget.SearchView
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import io.reactivex.disposables.CompositeDisposable
|
||||
import kotlinx.android.synthetic.main.activity_language.language_progressbar
|
||||
import kotlinx.android.synthetic.main.activity_language.recycler_view
|
||||
import kotlinx.android.synthetic.main.activity_language.toolbar
|
||||
import org.kiwix.kiwixmobile.R
|
||||
import org.kiwix.kiwixmobile.base.BaseActivity
|
||||
import org.kiwix.kiwixmobile.extensions.viewModel
|
||||
import org.kiwix.kiwixmobile.language.viewmodel.Action.Select
|
||||
import org.kiwix.kiwixmobile.language.viewmodel.State.Content
|
||||
import org.kiwix.kiwixmobile.language.viewmodel.State.Loading
|
||||
import org.kiwix.kiwixmobile.language.viewmodel.State.Saving
|
||||
import org.kiwix.kiwixmobile.language.adapter.LanguageAdapter
|
||||
import org.kiwix.kiwixmobile.language.adapter.LanguageDelegate.HeaderDelegate
|
||||
import org.kiwix.kiwixmobile.language.adapter.LanguageDelegate.LanguageItemDelegate
|
||||
import org.kiwix.kiwixmobile.language.viewmodel.Action
|
||||
import org.kiwix.kiwixmobile.language.viewmodel.LanguageViewModel
|
||||
import org.kiwix.kiwixmobile.language.viewmodel.State
|
||||
import org.kiwix.kiwixmobile.zim_manager.SimpleTextListener
|
||||
import javax.inject.Inject
|
||||
|
||||
class LanguageActivity : BaseActivity() {
|
||||
|
||||
private val languageViewModel by lazy { viewModel<LanguageViewModel>(viewModelFactory) }
|
||||
|
||||
@Inject lateinit var viewModelFactory: ViewModelProvider.Factory
|
||||
|
||||
private val compositeDisposable = CompositeDisposable()
|
||||
|
||||
private val languageAdapter =
|
||||
LanguageAdapter(
|
||||
LanguageItemDelegate { languageViewModel.actions.offer(Select(it)) },
|
||||
HeaderDelegate()
|
||||
)
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_language)
|
||||
setSupportActionBar(toolbar)
|
||||
|
||||
supportActionBar?.let {
|
||||
it.setDisplayHomeAsUpEnabled(true)
|
||||
it.setHomeAsUpIndicator(R.drawable.ic_clear_white_24dp)
|
||||
it.setTitle(R.string.select_languages)
|
||||
}
|
||||
recycler_view.run {
|
||||
adapter = languageAdapter
|
||||
layoutManager = LinearLayoutManager(context, RecyclerView.VERTICAL, false)
|
||||
setHasFixedSize(true)
|
||||
}
|
||||
languageViewModel.state.observe(this, Observer {
|
||||
render(it)
|
||||
})
|
||||
compositeDisposable.add(
|
||||
languageViewModel.effects.subscribe(
|
||||
{
|
||||
it.invokeWith(this)
|
||||
},
|
||||
Throwable::printStackTrace
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
compositeDisposable.clear()
|
||||
}
|
||||
|
||||
private fun render(state: State) = when (state) {
|
||||
Loading -> language_progressbar.show()
|
||||
is Content -> {
|
||||
language_progressbar.hide()
|
||||
languageAdapter.items = state.viewItems
|
||||
}
|
||||
Saving -> Unit
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
||||
menuInflater.inflate(R.menu.menu_language, menu)
|
||||
val search = menu.findItem(R.id.menu_language_search)
|
||||
(search.actionView as SearchView).setOnQueryTextListener(SimpleTextListener {
|
||||
languageViewModel.actions.offer(Action.Filter(it))
|
||||
})
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
when (item.itemId) {
|
||||
android.R.id.home -> {
|
||||
onBackPressed()
|
||||
return true
|
||||
}
|
||||
R.id.menu_language_save -> {
|
||||
languageViewModel.actions.offer(Action.SaveAll)
|
||||
return true
|
||||
}
|
||||
}
|
||||
return super.onOptionsItemSelected(item)
|
||||
}
|
||||
|
||||
}
|
@ -1,139 +0,0 @@
|
||||
package org.kiwix.kiwixmobile.language;
|
||||
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.TextView;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import butterknife.BindView;
|
||||
import butterknife.ButterKnife;
|
||||
import java.util.ArrayList;
|
||||
import org.kiwix.kiwixmobile.R;
|
||||
import org.kiwix.kiwixmobile.zim_manager.Language;
|
||||
|
||||
class LanguageAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
|
||||
private static final int TYPE_HEADER = 0;
|
||||
private static final int TYPE_ITEM = 1;
|
||||
private final ArrayList<Language> languages;
|
||||
private final ArrayList<Language> selectedLanguages = new ArrayList<>();
|
||||
private final ArrayList<Language> unselectedLanguages = new ArrayList<>();
|
||||
|
||||
LanguageAdapter(ArrayList<Language> languages) {
|
||||
this.languages = languages;
|
||||
categorizeLanguages();
|
||||
}
|
||||
|
||||
void categorizeLanguages() {
|
||||
selectedLanguages.clear();
|
||||
unselectedLanguages.clear();
|
||||
//for (Language language : languages) {
|
||||
// if (language.active != null && language.active.equals(true)) {
|
||||
// selectedLanguages.add(language);
|
||||
// } else {
|
||||
// language.active = false;
|
||||
// unselectedLanguages.add(language);
|
||||
// }
|
||||
//}
|
||||
//Collections.sort(selectedLanguages);
|
||||
//Collections.sort(unselectedLanguages);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
if (viewType == TYPE_ITEM) {
|
||||
View view =
|
||||
LayoutInflater.from(parent.getContext()).inflate(R.layout.item_language, parent, false);
|
||||
return new ViewHolder(view);
|
||||
}
|
||||
return new Header(
|
||||
LayoutInflater.from(parent.getContext()).inflate(R.layout.header_date, parent, false));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder item, int position) {
|
||||
if (item instanceof Header) {
|
||||
Header header = (Header) item;
|
||||
if (position == 0) {
|
||||
header.header.setText(R.string.your_languages);
|
||||
} else {
|
||||
header.header.setText(R.string.other_languages);
|
||||
}
|
||||
return;
|
||||
}
|
||||
Language language;
|
||||
if (position - 1 < selectedLanguages.size()) {
|
||||
language = selectedLanguages.get(position - 1);
|
||||
} else {
|
||||
language = unselectedLanguages.get(position - selectedLanguages.size() - 2);
|
||||
}
|
||||
ViewHolder holder = (ViewHolder) item;
|
||||
//holder.languageName.setText(language.language);
|
||||
//holder.languageLocalizedName.setText(language.languageLocalized);
|
||||
//holder.booksCount.setText(holder.booksCount.getContext().getResources()
|
||||
// .getQuantityString(R.plurals.books_count, language.booksCount, language.booksCount));
|
||||
//if (language.active == null) {
|
||||
// language.active = false;
|
||||
//}
|
||||
//holder.checkBox.setChecked(language.active);
|
||||
//View.OnClickListener onClickListener = v -> {
|
||||
// language.active = holder.checkBox.isChecked();
|
||||
// if (language.active) {
|
||||
// unselectedLanguages.remove(language);
|
||||
// selectedLanguages.add(language);
|
||||
// } else {
|
||||
// unselectedLanguages.add(language);
|
||||
// selectedLanguages.remove(language);
|
||||
// }
|
||||
// Collections.sort(selectedLanguages);
|
||||
// Collections.sort(unselectedLanguages);
|
||||
// notifyDataSetChanged();
|
||||
//};
|
||||
//holder.itemView.setOnClickListener(v -> {
|
||||
// holder.checkBox.toggle();
|
||||
// onClickListener.onClick(v);
|
||||
//});
|
||||
//holder.checkBox.setOnClickListener(onClickListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemViewType(int position) {
|
||||
if (position == 0 || position == selectedLanguages.size() + 1) {
|
||||
return TYPE_HEADER;
|
||||
}
|
||||
return TYPE_ITEM;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return selectedLanguages.size() + unselectedLanguages.size() + 2;
|
||||
}
|
||||
|
||||
class ViewHolder extends RecyclerView.ViewHolder {
|
||||
@BindView(R.id.item_language_name)
|
||||
TextView languageName;
|
||||
@BindView(R.id.item_language_localized_name)
|
||||
TextView languageLocalizedName;
|
||||
@BindView(R.id.item_language_books_count)
|
||||
TextView booksCount;
|
||||
@BindView(R.id.item_language_checkbox)
|
||||
CheckBox checkBox;
|
||||
|
||||
ViewHolder(View itemView) {
|
||||
super(itemView);
|
||||
ButterKnife.bind(this, itemView);
|
||||
}
|
||||
}
|
||||
|
||||
class Header extends RecyclerView.ViewHolder {
|
||||
@BindView(R.id.header_date)
|
||||
TextView header;
|
||||
|
||||
Header(View itemView) {
|
||||
super(itemView);
|
||||
ButterKnife.bind(this, itemView);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
package org.kiwix.kiwixmobile.language;
|
||||
|
||||
import java.util.List;
|
||||
import org.kiwix.kiwixmobile.base.BaseContract;
|
||||
import org.kiwix.kiwixmobile.zim_manager.Language;
|
||||
|
||||
interface LanguageContract {
|
||||
interface View extends BaseContract.View<Presenter> {
|
||||
void notifyLanguagesFiltered(List<Language> languages);
|
||||
}
|
||||
|
||||
interface Presenter extends BaseContract.Presenter<View> {
|
||||
void filerLanguages(List<Language> languages, String query);
|
||||
|
||||
void saveLanguages(List<Language> languages);
|
||||
}
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
package org.kiwix.kiwixmobile.language;
|
||||
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
import org.kiwix.kiwixmobile.di.PerActivity;
|
||||
|
||||
@Module
|
||||
public class LanguageModule {
|
||||
@PerActivity
|
||||
@Provides
|
||||
LanguageContract.Presenter provideLanguagePresenter(LanguagePresenter presenter) {
|
||||
return presenter;
|
||||
}
|
||||
}
|
@ -1,76 +0,0 @@
|
||||
package org.kiwix.kiwixmobile.language;
|
||||
|
||||
import android.util.Log;
|
||||
import io.reactivex.CompletableObserver;
|
||||
import io.reactivex.Scheduler;
|
||||
import io.reactivex.disposables.Disposable;
|
||||
import java.util.List;
|
||||
import javax.inject.Inject;
|
||||
import org.kiwix.kiwixmobile.base.BasePresenter;
|
||||
import org.kiwix.kiwixmobile.data.DataSource;
|
||||
import org.kiwix.kiwixmobile.di.PerActivity;
|
||||
import org.kiwix.kiwixmobile.di.qualifiers.Computation;
|
||||
import org.kiwix.kiwixmobile.di.qualifiers.MainThread;
|
||||
import org.kiwix.kiwixmobile.zim_manager.Language;
|
||||
|
||||
@PerActivity
|
||||
class LanguagePresenter extends BasePresenter<LanguageContract.View>
|
||||
implements LanguageContract.Presenter {
|
||||
private final Scheduler mainThread;
|
||||
private final Scheduler computation;
|
||||
private final DataSource dataSource;
|
||||
|
||||
@Inject LanguagePresenter(DataSource dataSource, @Computation Scheduler computation,
|
||||
@MainThread Scheduler mainThread) {
|
||||
this.computation = computation;
|
||||
this.mainThread = mainThread;
|
||||
this.dataSource = dataSource;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void filerLanguages(List<Language> languages, String query) {
|
||||
//Observable.fromIterable(languages)
|
||||
// .filter(language -> language.language.toLowerCase().contains(query.toLowerCase()) ||
|
||||
// language.languageLocalized.toLowerCase().contains(query.toLowerCase()))
|
||||
// .toList()
|
||||
// .subscribeOn(computation)
|
||||
// .observeOn(mainThread)
|
||||
// .subscribe(new SingleObserver<List<Language>>() {
|
||||
// @Override
|
||||
// public void onSubscribe(Disposable d) {
|
||||
// compositeDisposable.add(d);
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void onSuccess(List<Language> languages) {
|
||||
// view.notifyLanguagesFiltered(languages);
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void onError(Throwable e) {
|
||||
// Log.e("LanguagePresenter", e.toString());
|
||||
// }
|
||||
// });
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveLanguages(List<Language> languages) {
|
||||
dataSource.saveLanguages(languages)
|
||||
.subscribe(new CompletableObserver() {
|
||||
@Override
|
||||
public void onSubscribe(Disposable d) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onComplete() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Throwable e) {
|
||||
Log.e("LanguagePresenter", e.toString());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Kiwix Android
|
||||
* Copyright (C) 2018 Kiwix <android.kiwix.org>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.kiwix.kiwixmobile.language.adapter
|
||||
|
||||
import org.kiwix.kiwixmobile.zim_manager.library_view.adapter.base.AdapterDelegate
|
||||
import org.kiwix.kiwixmobile.zim_manager.library_view.adapter.base.BaseDelegateAdapter
|
||||
|
||||
class LanguageAdapter(
|
||||
vararg delegates: AdapterDelegate<LanguageListItem>
|
||||
) : BaseDelegateAdapter<LanguageListItem>(*delegates) {
|
||||
override fun getIdFor(item: LanguageListItem) = item.id
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
package org.kiwix.kiwixmobile.language.adapter
|
||||
|
||||
import android.view.ViewGroup
|
||||
import org.kiwix.kiwixmobile.R
|
||||
import org.kiwix.kiwixmobile.extensions.inflate
|
||||
import org.kiwix.kiwixmobile.language.adapter.LanguageListItem.HeaderItem
|
||||
import org.kiwix.kiwixmobile.language.adapter.LanguageListItem.LanguageItem
|
||||
import org.kiwix.kiwixmobile.language.adapter.LanguageListViewHolder.HeaderViewHolder
|
||||
import org.kiwix.kiwixmobile.language.adapter.LanguageListViewHolder.LanguageViewHolder
|
||||
import org.kiwix.kiwixmobile.zim_manager.library_view.adapter.base.AbsDelegateAdapter
|
||||
|
||||
sealed class LanguageDelegate<I : LanguageListItem, VH : LanguageListViewHolder<I>> :
|
||||
AbsDelegateAdapter<I, LanguageListItem, VH> {
|
||||
|
||||
class HeaderDelegate : LanguageDelegate<HeaderItem, HeaderViewHolder>() {
|
||||
override val itemClass = HeaderItem::class.java
|
||||
|
||||
override fun createViewHolder(parent: ViewGroup) =
|
||||
HeaderViewHolder(parent.inflate(R.layout.header_date, false))
|
||||
|
||||
}
|
||||
|
||||
class LanguageItemDelegate(private val clickAction: (LanguageItem) -> Unit) : LanguageDelegate<LanguageItem, LanguageViewHolder>() {
|
||||
override val itemClass = LanguageItem::class.java
|
||||
|
||||
override fun createViewHolder(parent: ViewGroup) =
|
||||
LanguageViewHolder(parent.inflate(R.layout.item_language, false), clickAction)
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
package org.kiwix.kiwixmobile.language.adapter
|
||||
|
||||
import org.kiwix.kiwixmobile.zim_manager.Language
|
||||
|
||||
sealed class LanguageListItem {
|
||||
abstract val id: Long
|
||||
|
||||
data class HeaderItem constructor(
|
||||
override val id: Long
|
||||
) : LanguageListItem() {
|
||||
companion object {
|
||||
const val SELECTED = Long.MAX_VALUE
|
||||
const val OTHER = Long.MIN_VALUE
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
data class LanguageItem(
|
||||
val language: Language,
|
||||
override val id: Long = language.id
|
||||
) : LanguageListItem()
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
package org.kiwix.kiwixmobile.language.adapter
|
||||
|
||||
import android.view.View
|
||||
import kotlinx.android.synthetic.main.header_date.header_date
|
||||
import kotlinx.android.synthetic.main.item_language.item_language_books_count
|
||||
import kotlinx.android.synthetic.main.item_language.item_language_checkbox
|
||||
import kotlinx.android.synthetic.main.item_language.item_language_clickable_area
|
||||
import kotlinx.android.synthetic.main.item_language.item_language_localized_name
|
||||
import kotlinx.android.synthetic.main.item_language.item_language_name
|
||||
import org.kiwix.kiwixmobile.R
|
||||
import org.kiwix.kiwixmobile.language.adapter.LanguageListItem.HeaderItem
|
||||
import org.kiwix.kiwixmobile.language.adapter.LanguageListItem.LanguageItem
|
||||
import org.kiwix.kiwixmobile.zim_manager.library_view.adapter.base.BaseViewHolder
|
||||
|
||||
sealed class LanguageListViewHolder<T : LanguageListItem>(override val containerView: View) :
|
||||
BaseViewHolder<T>(containerView) {
|
||||
class HeaderViewHolder(view: View) : LanguageListViewHolder<HeaderItem>(view) {
|
||||
override fun bind(item: HeaderItem) {
|
||||
header_date.setText(
|
||||
if (item.id == HeaderItem.SELECTED) R.string.your_languages
|
||||
else R.string.other_languages
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class LanguageViewHolder(
|
||||
view: View,
|
||||
val clickAction: (LanguageItem) -> Unit
|
||||
) : LanguageListViewHolder<LanguageItem>(view) {
|
||||
override fun bind(item: LanguageItem) {
|
||||
val language = item.language
|
||||
item_language_name.text = language.language
|
||||
item_language_localized_name.text = language.languageLocalized
|
||||
item_language_books_count.text = containerView.resources.getQuantityString(
|
||||
R.plurals.books_count, language.occurencesOfLanguage, language.occurencesOfLanguage
|
||||
)
|
||||
item_language_checkbox.isChecked = language.active
|
||||
item_language_clickable_area.setOnClickListener { clickAction(item) }
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
package org.kiwix.kiwixmobile.language.viewmodel
|
||||
|
||||
import org.kiwix.kiwixmobile.language.adapter.LanguageListItem.LanguageItem
|
||||
import org.kiwix.kiwixmobile.zim_manager.Language
|
||||
|
||||
sealed class Action {
|
||||
data class UpdateLanguages(val languages: List<Language>) : Action()
|
||||
data class Filter(val filter: String) : Action()
|
||||
data class Select(val language: LanguageItem) : Action()
|
||||
object SaveAll : Action()
|
||||
}
|
@ -0,0 +1,91 @@
|
||||
package org.kiwix.kiwixmobile.language.viewmodel
|
||||
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import io.reactivex.disposables.CompositeDisposable
|
||||
import io.reactivex.processors.PublishProcessor
|
||||
import org.kiwix.kiwixmobile.database.newdb.dao.NewLanguagesDao
|
||||
import org.kiwix.kiwixmobile.language.adapter.LanguageListItem.LanguageItem
|
||||
import org.kiwix.kiwixmobile.language.viewmodel.Action.Filter
|
||||
import org.kiwix.kiwixmobile.language.viewmodel.Action.SaveAll
|
||||
import org.kiwix.kiwixmobile.language.viewmodel.Action.Select
|
||||
import org.kiwix.kiwixmobile.language.viewmodel.Action.UpdateLanguages
|
||||
import org.kiwix.kiwixmobile.language.viewmodel.State.Content
|
||||
import org.kiwix.kiwixmobile.language.viewmodel.State.Loading
|
||||
import org.kiwix.kiwixmobile.language.viewmodel.State.Saving
|
||||
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.effects.SideEffect
|
||||
import javax.inject.Inject
|
||||
|
||||
class LanguageViewModel @Inject constructor(
|
||||
private val languageDao: NewLanguagesDao
|
||||
) : ViewModel() {
|
||||
|
||||
val state = MutableLiveData<State>().apply { value = Loading }
|
||||
val actions = PublishProcessor.create<Action>()
|
||||
val effects = PublishProcessor.create<SideEffect<*>>()
|
||||
|
||||
private val compositeDisposable = CompositeDisposable()
|
||||
|
||||
init {
|
||||
compositeDisposable.addAll(
|
||||
actions.map { reduce(it, state.value!!) }
|
||||
.distinctUntilChanged()
|
||||
.subscribe(state::postValue, Throwable::printStackTrace),
|
||||
languageDao.languages().filter { it.isNotEmpty() }
|
||||
.subscribe(
|
||||
{
|
||||
actions.offer(UpdateLanguages(it))
|
||||
},
|
||||
Throwable::printStackTrace
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private fun reduce(
|
||||
action: Action,
|
||||
currentState: State
|
||||
): State {
|
||||
return when (action) {
|
||||
is UpdateLanguages -> when (currentState) {
|
||||
Loading -> Content(action.languages)
|
||||
else -> currentState
|
||||
}
|
||||
is Filter -> {
|
||||
when (currentState) {
|
||||
is Content -> filterContent(action.filter, currentState)
|
||||
else -> currentState
|
||||
}
|
||||
}
|
||||
is Select ->
|
||||
when (currentState) {
|
||||
is Content -> updateSelection(action.language, currentState)
|
||||
else -> currentState
|
||||
}
|
||||
SaveAll ->
|
||||
when (currentState) {
|
||||
is Content -> saveAll(currentState)
|
||||
else -> currentState
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun saveAll(currentState: Content): State {
|
||||
effects.offer(
|
||||
SaveLanguagesAndFinish(
|
||||
currentState.items, languageDao
|
||||
)
|
||||
)
|
||||
return Saving
|
||||
}
|
||||
|
||||
private fun updateSelection(
|
||||
languageItem: LanguageItem,
|
||||
currentState: Content
|
||||
) = currentState.select(languageItem)
|
||||
|
||||
private fun filterContent(
|
||||
filter: String,
|
||||
currentState: Content
|
||||
) = currentState.updateFilter(filter)
|
||||
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Kiwix Android
|
||||
* Copyright (C) 2018 Kiwix <android.kiwix.org>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.kiwix.kiwixmobile.language.viewmodel
|
||||
|
||||
import android.app.Activity
|
||||
import io.reactivex.Flowable
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import org.kiwix.kiwixmobile.database.newdb.dao.NewLanguagesDao
|
||||
import org.kiwix.kiwixmobile.zim_manager.Language
|
||||
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.effects.SideEffect
|
||||
|
||||
data class SaveLanguagesAndFinish(
|
||||
val languages: List<Language>,
|
||||
val languageDao: NewLanguagesDao
|
||||
) : SideEffect<Unit> {
|
||||
|
||||
override fun invokeWith(activity: Activity) {
|
||||
Flowable.fromCallable { languageDao.insert(languages) }
|
||||
.subscribeOn(Schedulers.io())
|
||||
.subscribe({
|
||||
activity.finish()
|
||||
}, Throwable::printStackTrace)
|
||||
}
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
package org.kiwix.kiwixmobile.language.viewmodel
|
||||
|
||||
import org.kiwix.kiwixmobile.language.adapter.LanguageListItem
|
||||
import org.kiwix.kiwixmobile.language.adapter.LanguageListItem.HeaderItem
|
||||
import org.kiwix.kiwixmobile.language.adapter.LanguageListItem.LanguageItem
|
||||
import org.kiwix.kiwixmobile.zim_manager.Language
|
||||
|
||||
sealed class State {
|
||||
object Loading : State()
|
||||
object Saving : State()
|
||||
data class Content(
|
||||
val items: List<Language>,
|
||||
val filter: String = "",
|
||||
val viewItems: List<LanguageListItem> = createViewList(
|
||||
items, filter
|
||||
)
|
||||
) : State() {
|
||||
fun select(languageItem: LanguageItem) = Content(
|
||||
items.map { if (it.id == languageItem.id) it.copy(active = !it.active) else it },
|
||||
filter
|
||||
)
|
||||
|
||||
fun updateFilter(filter: String) = Content(items, filter)
|
||||
|
||||
companion object {
|
||||
internal fun createViewList(
|
||||
items: List<Language>,
|
||||
filter: String
|
||||
) = activeItems(
|
||||
items, filter
|
||||
) + otherItems(items, filter)
|
||||
|
||||
private fun activeItems(
|
||||
items: List<Language>,
|
||||
filter: String
|
||||
) =
|
||||
createLanguageSection(
|
||||
items, filter, { it.active }, HeaderItem.SELECTED
|
||||
)
|
||||
|
||||
private fun otherItems(
|
||||
items: List<Language>,
|
||||
filter: String
|
||||
) =
|
||||
createLanguageSection(
|
||||
items, filter, { !it.active }, HeaderItem.OTHER
|
||||
)
|
||||
|
||||
private fun createLanguageSection(
|
||||
items: List<Language>,
|
||||
filter: String,
|
||||
filterCondition: (Language) -> Boolean,
|
||||
headerId: Long
|
||||
) = items.filter(filterCondition)
|
||||
.filter { filter.isEmpty() or it.matches(filter) }
|
||||
.takeIf { it.isNotEmpty() }
|
||||
?.let { listOf(HeaderItem(headerId)) + it.map { language -> LanguageItem(language) } }
|
||||
?: emptyList()
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -39,7 +39,6 @@ import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
|
||||
import butterknife.BindView;
|
||||
import butterknife.ButterKnife;
|
||||
import butterknife.Unbinder;
|
||||
@ -49,13 +48,14 @@ import static org.kiwix.kiwixmobile.utils.Constants.NOTES_DIRECTORY;
|
||||
/**
|
||||
* Created by @author Aditya-Sood (21/05/19) as a part of GSoC 2019
|
||||
*
|
||||
* AddNoteDialog extends DialogFragment and is used to display the note corresponding to a particular
|
||||
* article (of a particular zim file/wiki/book) as a full-screen dialog fragment.
|
||||
* AddNoteDialog extends DialogFragment and is used to display the note corresponding to a
|
||||
* particular article (of a particular zim file/wiki/book) as a full-screen dialog fragment.
|
||||
*
|
||||
* Notes are saved as text files at location: "{External Storage}/Kiwix/Notes/ZimFileName/ArticleUrl.txt"
|
||||
* */
|
||||
*/
|
||||
|
||||
public class AddNoteDialog extends DialogFragment implements ConfirmationAlertDialogFragment.UserClickListener {
|
||||
public class AddNoteDialog extends DialogFragment
|
||||
implements ConfirmationAlertDialogFragment.UserClickListener {
|
||||
|
||||
public static final String TAG = "AddNoteDialog";
|
||||
|
||||
@ -70,36 +70,51 @@ public class AddNoteDialog extends DialogFragment implements ConfirmationAlertDi
|
||||
|
||||
private Unbinder unbinder;
|
||||
|
||||
private String zimFileName;
|
||||
private String zimFileTitle;
|
||||
private String articleTitle;
|
||||
private String zimNoteDirectoryName; // Corresponds to "ZimFileName" of "{External Storage}/Kiwix/Notes/ZimFileName/ArticleUrl.txt"
|
||||
private String articleNotefileName; // Corresponds to "ArticleUrl" of "{External Storage}/Kiwix/Notes/ZimFileName/ArticleUrl.txt"
|
||||
// Corresponds to "ZimFileName" of "{External Storage}/Kiwix/Notes/ZimFileName/ArticleUrl.txt"
|
||||
private String zimNoteDirectoryName;
|
||||
// Corresponds to "ArticleUrl" of "{External Storage}/Kiwix/Notes/ZimFileName/ArticleUrl.txt"
|
||||
private String articleNotefileName;
|
||||
private boolean noteFileExists = false;
|
||||
private boolean noteEdited = false; // Keeps track of state of the note (whether edited since last save)
|
||||
boolean noteEdited = false; // Keeps track of state of the note (whether edited since last save)
|
||||
|
||||
private String ZIM_NOTES_DIRECTORY; // Stores path to directory for the currently open zim's notes
|
||||
private String ZIM_NOTES_DIRECTORY; // Stores path to directory for the currently open zim's notes
|
||||
|
||||
public AddNoteDialog(SharedPreferenceUtil sharedPreferenceUtil) {
|
||||
public AddNoteDialog(@NonNull SharedPreferenceUtil sharedPreferenceUtil) {
|
||||
this.sharedPreferenceUtil = sharedPreferenceUtil;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
setStyle(DialogFragment.STYLE_NORMAL, sharedPreferenceUtil.nightMode() ? R.style.AddNoteDialogStyle_Night : R.style.AddNoteDialogStyle);
|
||||
setStyle(DialogFragment.STYLE_NORMAL,
|
||||
sharedPreferenceUtil.nightMode() ? R.style.AddNoteDialogStyle_Night
|
||||
: R.style.AddNoteDialogStyle);
|
||||
|
||||
zimFileTitle = ZimContentProvider.getZimFileTitle();
|
||||
articleTitle = ((MainActivity)getActivity()).getCurrentWebView().getTitle();
|
||||
// Returns name of the form ".../Kiwix/granbluefantasy_en_all_all_nopic_2018-10.zim"
|
||||
zimFileName = ZimContentProvider.getZimFile();
|
||||
|
||||
zimNoteDirectoryName = getZimNoteDirectoryName();
|
||||
articleNotefileName = getArticleNotefileName();
|
||||
if (zimFileName != null) { // No zim file currently opened
|
||||
zimFileTitle = ZimContentProvider.getZimFileTitle();
|
||||
articleTitle = ((MainActivity) getActivity()).getCurrentWebView().getTitle();
|
||||
|
||||
ZIM_NOTES_DIRECTORY = NOTES_DIRECTORY + zimNoteDirectoryName + "/";
|
||||
zimNoteDirectoryName = getZimNoteDirectoryName();
|
||||
articleNotefileName = getArticleNotefileName();
|
||||
|
||||
ZIM_NOTES_DIRECTORY = NOTES_DIRECTORY + zimNoteDirectoryName + "/";
|
||||
} else {
|
||||
showToast(R.string.error_filenotfound, Toast.LENGTH_LONG);
|
||||
closeKeyboard();
|
||||
getFragmentManager().beginTransaction().remove(AddNoteDialog.this).commit();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
public @NonNull View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
|
||||
@Nullable Bundle savedInstanceState) {
|
||||
super.onCreateView(inflater, container, savedInstanceState);
|
||||
View view = inflater.inflate(R.layout.dialog_add_note, container, false);
|
||||
unbinder = ButterKnife.bind(this, view);
|
||||
@ -142,7 +157,8 @@ public class AddNoteDialog extends DialogFragment implements ConfirmationAlertDi
|
||||
|
||||
addNoteEditText.addTextChangedListener(new TextWatcher() {
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
|
||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||
@ -152,26 +168,26 @@ public class AddNoteDialog extends DialogFragment implements ConfirmationAlertDi
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable s) {}
|
||||
public void afterTextChanged(Editable s) {
|
||||
}
|
||||
});
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
private @NonNull String getZimNoteDirectoryName() {
|
||||
String zimFileName = ZimContentProvider.getZimFile(); // Returns name of the form ".../Kiwix/granbluefantasy_en_all_all_nopic_2018-10.zim"
|
||||
|
||||
String noteDirectoryName = getTextAfterLastSlashWithoutExtension(zimFileName);
|
||||
|
||||
return (!noteDirectoryName.isEmpty()) ? noteDirectoryName : zimFileTitle; // Incase the required ZIM file name couldn't be extracted
|
||||
return (!noteDirectoryName.isEmpty()) ? noteDirectoryName : zimFileTitle;
|
||||
}
|
||||
|
||||
private @NonNull String getArticleNotefileName() {
|
||||
String articleUrl = ((MainActivity) getActivity()).getCurrentWebView().getUrl(); // Returns url of the form: "content://org.kiwix.kiwixmobile.zim.base/A/Main_Page.html"
|
||||
// Returns url of the form: "content://org.kiwix.kiwixmobile.zim.base/A/Main_Page.html"
|
||||
String articleUrl = ((MainActivity) getActivity()).getCurrentWebView().getUrl();
|
||||
|
||||
String notefileName = getTextAfterLastSlashWithoutExtension(articleUrl);
|
||||
|
||||
return (!notefileName.isEmpty()) ? notefileName : articleTitle; // Incase the required html file name couldn't be extracted
|
||||
return (!notefileName.isEmpty()) ? notefileName : articleTitle;
|
||||
}
|
||||
|
||||
private @NonNull String getTextAfterLastSlashWithoutExtension(@NonNull String path) {
|
||||
@ -185,8 +201,8 @@ public class AddNoteDialog extends DialogFragment implements ConfirmationAlertDi
|
||||
int rightmostSlash = path.lastIndexOf('/');
|
||||
int rightmostDot = path.lastIndexOf('.');
|
||||
|
||||
if(rightmostSlash > -1 && rightmostDot > -1) {
|
||||
return (path.substring(rightmostSlash+1, rightmostDot));
|
||||
if (rightmostSlash > -1 && rightmostDot > -1) {
|
||||
return (path.substring(rightmostSlash + 1, rightmostDot));
|
||||
}
|
||||
|
||||
return ""; // If couldn't find the dot and/or slash
|
||||
@ -204,16 +220,18 @@ public class AddNoteDialog extends DialogFragment implements ConfirmationAlertDi
|
||||
};
|
||||
}
|
||||
|
||||
private void exitAddNoteDialog() {
|
||||
if(noteEdited) {
|
||||
Fragment previousInstance = getActivity().getSupportFragmentManager().findFragmentByTag(ConfirmationAlertDialogFragment.TAG);
|
||||
void exitAddNoteDialog() {
|
||||
if (noteEdited) {
|
||||
Fragment previousInstance = getActivity().getSupportFragmentManager()
|
||||
.findFragmentByTag(ConfirmationAlertDialogFragment.TAG);
|
||||
|
||||
if(previousInstance == null) {
|
||||
if (previousInstance == null) {
|
||||
// Custom AlertDialog for taking user confirmation before closing note dialog in case of unsaved changes
|
||||
DialogFragment newFragment = new ConfirmationAlertDialogFragment(sharedPreferenceUtil, TAG, R.string.confirmation_alert_dialog_message);
|
||||
newFragment.show(getActivity().getSupportFragmentManager(), ConfirmationAlertDialogFragment.TAG);
|
||||
DialogFragment newFragment = new ConfirmationAlertDialogFragment(sharedPreferenceUtil, TAG,
|
||||
R.string.confirmation_alert_dialog_message);
|
||||
newFragment.show(getActivity().getSupportFragmentManager(),
|
||||
ConfirmationAlertDialogFragment.TAG);
|
||||
}
|
||||
|
||||
} else {
|
||||
// Closing unedited note dialog straightaway
|
||||
dismissAddNoteDialog();
|
||||
@ -221,36 +239,33 @@ public class AddNoteDialog extends DialogFragment implements ConfirmationAlertDi
|
||||
}
|
||||
|
||||
private void disableMenuItems() {
|
||||
if(toolbar.getMenu() != null) {
|
||||
if (toolbar.getMenu() != null) {
|
||||
MenuItem saveItem = toolbar.getMenu().findItem(R.id.save_note);
|
||||
MenuItem shareItem = toolbar.getMenu().findItem(R.id.share_note);
|
||||
saveItem.setEnabled(false);
|
||||
shareItem.setEnabled(false);
|
||||
saveItem.getIcon().setAlpha(130);
|
||||
shareItem.getIcon().setAlpha(130);
|
||||
|
||||
} else {
|
||||
Log.d(TAG, "Toolbar without inflated menu");
|
||||
}
|
||||
}
|
||||
|
||||
private void enableSaveNoteMenuItem() {
|
||||
if(toolbar.getMenu() != null) {
|
||||
void enableSaveNoteMenuItem() {
|
||||
if (toolbar.getMenu() != null) {
|
||||
MenuItem saveItem = toolbar.getMenu().findItem(R.id.save_note);
|
||||
saveItem.setEnabled(true);
|
||||
saveItem.getIcon().setAlpha(255);
|
||||
|
||||
} else {
|
||||
Log.d(TAG, "Toolbar without inflated menu");
|
||||
}
|
||||
}
|
||||
|
||||
private void enableShareNoteMenuItem() {
|
||||
if(toolbar.getMenu() != null) {
|
||||
void enableShareNoteMenuItem() {
|
||||
if (toolbar.getMenu() != null) {
|
||||
MenuItem shareItem = toolbar.getMenu().findItem(R.id.share_note);
|
||||
shareItem.setEnabled(true);
|
||||
shareItem.getIcon().setAlpha(255);
|
||||
|
||||
} else {
|
||||
Log.d(TAG, "Toolbar without inflated menu");
|
||||
}
|
||||
@ -260,32 +275,35 @@ public class AddNoteDialog extends DialogFragment implements ConfirmationAlertDi
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
|
||||
if(!noteFileExists) {
|
||||
if (!noteFileExists) {
|
||||
// Prepare for input in case of empty/new note
|
||||
addNoteEditText.requestFocus();
|
||||
showKeyboard();
|
||||
}
|
||||
}
|
||||
|
||||
public void showKeyboard(){
|
||||
InputMethodManager inputMethodManager = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
private void showKeyboard() {
|
||||
InputMethodManager inputMethodManager =
|
||||
(InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
inputMethodManager.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0);
|
||||
}
|
||||
|
||||
public void closeKeyboard(){
|
||||
InputMethodManager inputMethodManager = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
void closeKeyboard() {
|
||||
InputMethodManager inputMethodManager =
|
||||
(InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
inputMethodManager.toggleSoftInput(InputMethodManager.HIDE_IMPLICIT_ONLY, 0);
|
||||
}
|
||||
|
||||
private void saveNote(String noteText) {
|
||||
void saveNote(String noteText) {
|
||||
|
||||
/* String content of the EditText, given by noteText, is saved into the text file given by:
|
||||
* "{External Storage}/Kiwix/Notes/ZimFileTitle/ArticleTitle.txt"
|
||||
* */
|
||||
|
||||
if(isExternalStorageWritable()) {
|
||||
if (isExternalStorageWritable()) {
|
||||
|
||||
if(ContextCompat.checkSelfPermission(getContext(), Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
|
||||
if (ContextCompat.checkSelfPermission(getContext(),
|
||||
Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
|
||||
Log.d(TAG, "WRITE_EXTERNAL_STORAGE permission not granted");
|
||||
showToast(R.string.note_save_unsuccessful, Toast.LENGTH_LONG);
|
||||
return;
|
||||
@ -294,12 +312,12 @@ public class AddNoteDialog extends DialogFragment implements ConfirmationAlertDi
|
||||
File notesFolder = new File(ZIM_NOTES_DIRECTORY);
|
||||
boolean folderExists = true;
|
||||
|
||||
if(!notesFolder.exists()) {
|
||||
if (!notesFolder.exists()) {
|
||||
// Try creating folder if it doesn't exist
|
||||
folderExists = notesFolder.mkdirs();
|
||||
}
|
||||
|
||||
if(folderExists) {
|
||||
if (folderExists) {
|
||||
File noteFile = new File(notesFolder.getAbsolutePath(), articleNotefileName + ".txt");
|
||||
|
||||
// Save note text-file code:
|
||||
@ -309,21 +327,17 @@ public class AddNoteDialog extends DialogFragment implements ConfirmationAlertDi
|
||||
fileOutputStream.close();
|
||||
showToast(R.string.note_save_successful, Toast.LENGTH_SHORT);
|
||||
noteEdited = false; // As no unsaved changes remain
|
||||
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
showToast(R.string.note_save_unsuccessful, Toast.LENGTH_LONG);
|
||||
}
|
||||
|
||||
} else {
|
||||
showToast(R.string.note_save_unsuccessful, Toast.LENGTH_LONG);
|
||||
Log.d(TAG, "Required folder doesn't exist");
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
showToast(R.string.note_save_error_storage_not_writable, Toast.LENGTH_LONG);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void displayNote() {
|
||||
@ -335,30 +349,20 @@ public class AddNoteDialog extends DialogFragment implements ConfirmationAlertDi
|
||||
|
||||
File noteFile = new File(ZIM_NOTES_DIRECTORY + articleNotefileName + ".txt");
|
||||
|
||||
if(noteFile.exists()) {
|
||||
if (noteFile.exists()) {
|
||||
noteFileExists = true;
|
||||
|
||||
StringBuilder contents = new StringBuilder();
|
||||
try {
|
||||
try (BufferedReader input = new BufferedReader(new java.io.FileReader(noteFile))) {
|
||||
String line = null;
|
||||
|
||||
BufferedReader input = new BufferedReader(new java.io.FileReader(noteFile));
|
||||
try {
|
||||
String line = null;
|
||||
|
||||
while((line = input.readLine()) != null) {
|
||||
contents.append(line);
|
||||
contents.append(System.getProperty("line.separator"));
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
Log.d(TAG, "Error reading line with BufferedReader");
|
||||
} finally {
|
||||
input.close();
|
||||
while ((line = input.readLine()) != null) {
|
||||
contents.append(line);
|
||||
contents.append(System.getProperty("line.separator"));
|
||||
}
|
||||
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
Log.d(TAG, "Error closing BufferedReader");
|
||||
Log.d(TAG, "Error reading line with BufferedReader");
|
||||
}
|
||||
|
||||
addNoteEditText.setText(contents.toString()); // Display the note content
|
||||
@ -369,49 +373,51 @@ public class AddNoteDialog extends DialogFragment implements ConfirmationAlertDi
|
||||
// No action in case the note file for the currently open article doesn't exist
|
||||
}
|
||||
|
||||
private void shareNote() {
|
||||
void shareNote() {
|
||||
|
||||
/* The note text file corresponding to the currently open article, given at:
|
||||
* "{External Storage}/Kiwix/Notes/ZimFileTitle/ArticleTitle.txt"
|
||||
* is shared via an app-chooser intent
|
||||
* */
|
||||
|
||||
if(noteEdited) {
|
||||
saveNote(addNoteEditText.getText().toString()); // Save edited note before sharing the text file
|
||||
if (noteEdited) {
|
||||
saveNote(
|
||||
addNoteEditText.getText().toString()); // Save edited note before sharing the text file
|
||||
}
|
||||
|
||||
File noteFile = new File(ZIM_NOTES_DIRECTORY + articleNotefileName + ".txt");
|
||||
|
||||
Uri noteFileUri = null;
|
||||
if(noteFile.exists()) {
|
||||
if (noteFile.exists()) {
|
||||
|
||||
if (Build.VERSION.SDK_INT >= 24) {
|
||||
// From Nougat 7 (API 24) access to files is shared temporarily with other apps
|
||||
// Need to use FileProvider for the same
|
||||
noteFileUri = FileProvider.getUriForFile(getContext(), BuildConfig.APPLICATION_ID+".fileprovider", noteFile);
|
||||
|
||||
noteFileUri =
|
||||
FileProvider.getUriForFile(getContext(), BuildConfig.APPLICATION_ID + ".fileprovider",
|
||||
noteFile);
|
||||
} else {
|
||||
noteFileUri = Uri.fromFile(noteFile);
|
||||
}
|
||||
|
||||
} else {
|
||||
showToast(R.string.note_share_error_file_missing, Toast.LENGTH_SHORT);
|
||||
}
|
||||
|
||||
if(noteFileUri != null) {
|
||||
if (noteFileUri != null) {
|
||||
Intent noteFileShareIntent = new Intent(Intent.ACTION_SEND);
|
||||
noteFileShareIntent.setType("application/octet-stream");
|
||||
noteFileShareIntent.putExtra(Intent.EXTRA_STREAM, noteFileUri);
|
||||
noteFileShareIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||
Intent shareChooser = Intent.createChooser(noteFileShareIntent, getString(R.string.note_share_app_chooser_title));
|
||||
Intent shareChooser = Intent.createChooser(noteFileShareIntent,
|
||||
getString(R.string.note_share_app_chooser_title));
|
||||
|
||||
if(noteFileShareIntent.resolveActivity(getActivity().getPackageManager()) != null) {
|
||||
if (noteFileShareIntent.resolveActivity(getActivity().getPackageManager()) != null) {
|
||||
startActivity(shareChooser);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isExternalStorageWritable() {
|
||||
static boolean isExternalStorageWritable() {
|
||||
return Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState());
|
||||
}
|
||||
|
||||
@ -440,7 +446,7 @@ public class AddNoteDialog extends DialogFragment implements ConfirmationAlertDi
|
||||
super.onStart();
|
||||
|
||||
Dialog dialog = getDialog();
|
||||
if(dialog != null) {
|
||||
if (dialog != null) {
|
||||
int width = ViewGroup.LayoutParams.MATCH_PARENT;
|
||||
int height = ViewGroup.LayoutParams.MATCH_PARENT;
|
||||
dialog.getWindow().setLayout(width, height);
|
||||
@ -454,5 +460,4 @@ public class AddNoteDialog extends DialogFragment implements ConfirmationAlertDi
|
||||
unbinder.unbind();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -20,6 +20,7 @@
|
||||
package org.kiwix.kiwixmobile.main;
|
||||
|
||||
import android.Manifest;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Activity;
|
||||
import android.appwidget.AppWidgetManager;
|
||||
import android.content.ActivityNotFoundException;
|
||||
@ -154,7 +155,7 @@ import static org.kiwix.kiwixmobile.utils.StyleUtils.dialogStyle;
|
||||
import static org.kiwix.kiwixmobile.utils.UpdateUtils.reformatProviderUrl;
|
||||
|
||||
public class MainActivity extends BaseActivity implements WebViewCallback,
|
||||
MainContract.View{
|
||||
MainContract.View {
|
||||
|
||||
private static final String NEW_TAB = "NEW_TAB";
|
||||
private static final String HOME_URL = "file:///android_asset/home.html";
|
||||
@ -276,7 +277,6 @@ public class MainActivity extends BaseActivity implements WebViewCallback,
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
private static void updateWidgets(Context context) {
|
||||
Intent intent = new Intent(context.getApplicationContext(), KiwixSearchWidget.class);
|
||||
intent.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
|
||||
@ -321,6 +321,7 @@ public class MainActivity extends BaseActivity implements WebViewCallback,
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
@ -333,6 +334,15 @@ public class MainActivity extends BaseActivity implements WebViewCallback,
|
||||
setSupportActionBar(toolbar);
|
||||
actionBar = getSupportActionBar();
|
||||
|
||||
toolbar.setOnTouchListener(new OnSwipeTouchListener(this) {
|
||||
|
||||
@Override
|
||||
@SuppressLint("SyntheticAccessor")
|
||||
public void onSwipeBottom() {
|
||||
showTabSwitcher();
|
||||
}
|
||||
});
|
||||
|
||||
tableDrawerRight =
|
||||
tableDrawerRightContainer.getHeaderView(0).findViewById(R.id.right_drawer_list);
|
||||
|
||||
@ -359,13 +369,13 @@ public class MainActivity extends BaseActivity implements WebViewCallback,
|
||||
|
||||
wasHideToolbar = isHideToolbar;
|
||||
booksAdapter = new BooksOnDiskAdapter(
|
||||
new BookOnDiskDelegate.BookDelegate(sharedPreferenceUtil,
|
||||
bookOnDiskItem -> {
|
||||
open(bookOnDiskItem);
|
||||
return Unit.INSTANCE;
|
||||
},
|
||||
null,
|
||||
null),
|
||||
new BookOnDiskDelegate.BookDelegate(sharedPreferenceUtil,
|
||||
bookOnDiskItem -> {
|
||||
open(bookOnDiskItem);
|
||||
return Unit.INSTANCE;
|
||||
},
|
||||
null,
|
||||
null),
|
||||
BookOnDiskDelegate.LanguageDelegate.INSTANCE
|
||||
);
|
||||
|
||||
@ -846,14 +856,14 @@ public class MainActivity extends BaseActivity implements WebViewCallback,
|
||||
break;
|
||||
|
||||
case R.id.menu_add_note:
|
||||
if(requestExternalStorageWritePermissionForNotes()) {
|
||||
if (requestExternalStorageWritePermissionForNotes()) {
|
||||
// Check permission since notes are stored in the public-external storage
|
||||
showAddNoteDialog();
|
||||
}
|
||||
break;
|
||||
|
||||
case R.id.menu_clear_notes:
|
||||
if(requestExternalStorageWritePermissionForNotes()) { // Check permission since notes are stored in the public-external storage
|
||||
if (requestExternalStorageWritePermissionForNotes()) { // Check permission since notes are stored in the public-external storage
|
||||
showClearAllNotesDialog();
|
||||
}
|
||||
break;
|
||||
@ -939,12 +949,13 @@ public class MainActivity extends BaseActivity implements WebViewCallback,
|
||||
}
|
||||
|
||||
/** Method to delete all user notes */
|
||||
private void clearAllNotes() {
|
||||
void clearAllNotes() {
|
||||
|
||||
boolean result = true; // Result of all delete() calls is &&-ed to this variable
|
||||
|
||||
if(AddNoteDialog.isExternalStorageWritable()) {
|
||||
if(ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
|
||||
if (AddNoteDialog.isExternalStorageWritable()) {
|
||||
if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
||||
!= PackageManager.PERMISSION_GRANTED) {
|
||||
Log.d("MainActivity", "WRITE_EXTERNAL_STORAGE permission not granted");
|
||||
showToast(R.string.ext_storage_permission_not_granted, Toast.LENGTH_LONG);
|
||||
return;
|
||||
@ -955,17 +966,17 @@ public class MainActivity extends BaseActivity implements WebViewCallback,
|
||||
File notesDirectory = new File(NOTES_DIRECTORY);
|
||||
File[] filesInNotesDirectory = notesDirectory.listFiles();
|
||||
|
||||
if(filesInNotesDirectory == null) { // Notes folder doesn't exist
|
||||
if (filesInNotesDirectory == null) { // Notes folder doesn't exist
|
||||
showToast(R.string.notes_deletion_none_found, Toast.LENGTH_LONG);
|
||||
return;
|
||||
}
|
||||
|
||||
for(File wikiFileDirectory : filesInNotesDirectory) {
|
||||
if(wikiFileDirectory.isDirectory()) {
|
||||
for (File wikiFileDirectory : filesInNotesDirectory) {
|
||||
if (wikiFileDirectory.isDirectory()) {
|
||||
File[] filesInWikiDirectory = wikiFileDirectory.listFiles();
|
||||
|
||||
for(File noteFile : filesInWikiDirectory) {
|
||||
if(noteFile.isFile()) {
|
||||
for (File noteFile : filesInWikiDirectory) {
|
||||
if (noteFile.isFile()) {
|
||||
result = result && noteFile.delete();
|
||||
}
|
||||
}
|
||||
@ -974,10 +985,11 @@ public class MainActivity extends BaseActivity implements WebViewCallback,
|
||||
result = result && wikiFileDirectory.delete(); // Wiki specific notes directory deleted
|
||||
}
|
||||
|
||||
result = result && notesDirectory.delete(); // "{External Storage}/Kiwix/Notes" directory deleted
|
||||
result =
|
||||
result && notesDirectory.delete(); // "{External Storage}/Kiwix/Notes" directory deleted
|
||||
}
|
||||
|
||||
if(result) {
|
||||
if (result) {
|
||||
showToast(R.string.notes_deletion_successful, Toast.LENGTH_SHORT);
|
||||
} else {
|
||||
showToast(R.string.notes_deletion_unsuccessful, Toast.LENGTH_SHORT);
|
||||
@ -990,23 +1002,24 @@ public class MainActivity extends BaseActivity implements WebViewCallback,
|
||||
Fragment previousInstance = getSupportFragmentManager().findFragmentByTag(AddNoteDialog.TAG);
|
||||
|
||||
// To prevent multiple instances of the DialogFragment
|
||||
if(previousInstance == null) {
|
||||
if (previousInstance == null) {
|
||||
/* Since the DialogFragment is never added to the back-stack, so findFragmentByTag()
|
||||
* returning null means that the AddNoteDialog is currently not on display (as doesn't exist)
|
||||
**/
|
||||
* returning null means that the AddNoteDialog is currently not on display (as doesn't exist)
|
||||
**/
|
||||
AddNoteDialog dialogFragment = new AddNoteDialog(sharedPreferenceUtil);
|
||||
dialogFragment.show(fragmentTransaction, AddNoteDialog.TAG); // For DialogFragments, show() handles the fragment commit and display
|
||||
dialogFragment.show(fragmentTransaction, AddNoteDialog.TAG);
|
||||
// For DialogFragments, show() handles the fragment commit and display
|
||||
}
|
||||
}
|
||||
|
||||
private boolean requestExternalStorageWritePermissionForNotes() {
|
||||
if(Build.VERSION.SDK_INT >= 23) { // For Marshmallow & higher API levels
|
||||
if (Build.VERSION.SDK_INT >= 23) { // For Marshmallow & higher API levels
|
||||
|
||||
if(checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
|
||||
if (checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
||||
== PackageManager.PERMISSION_GRANTED) {
|
||||
return true;
|
||||
|
||||
} else {
|
||||
if(shouldShowRequestPermissionRationale(Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
|
||||
if (shouldShowRequestPermissionRationale(Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
|
||||
/* shouldShowRequestPermissionRationale() returns false when:
|
||||
* 1) User has previously checked on "Don't ask me again", and/or
|
||||
* 2) Permission has been disabled on device
|
||||
@ -1014,9 +1027,9 @@ public class MainActivity extends BaseActivity implements WebViewCallback,
|
||||
showToast(R.string.ext_storage_permission_rationale_add_note, Toast.LENGTH_LONG);
|
||||
}
|
||||
|
||||
requestPermissions(new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_WRITE_STORAGE_PERMISSION_ADD_NOTE);
|
||||
requestPermissions(new String[] { Manifest.permission.WRITE_EXTERNAL_STORAGE },
|
||||
REQUEST_WRITE_STORAGE_PERMISSION_ADD_NOTE);
|
||||
}
|
||||
|
||||
} else { // For Android versions below Marshmallow 6.0 (API 23)
|
||||
return true; // As already requested at install time
|
||||
}
|
||||
@ -1202,12 +1215,12 @@ public class MainActivity extends BaseActivity implements WebViewCallback,
|
||||
|
||||
case REQUEST_WRITE_STORAGE_PERMISSION_ADD_NOTE: {
|
||||
|
||||
if(grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||
// Successfully granted permission, so opening the note keeper
|
||||
showAddNoteDialog();
|
||||
|
||||
} else {
|
||||
Toast.makeText(getApplicationContext(), getString(R.string.ext_storage_write_permission_denied_add_note), Toast.LENGTH_LONG);
|
||||
Toast.makeText(getApplicationContext(),
|
||||
getString(R.string.ext_storage_write_permission_denied_add_note), Toast.LENGTH_LONG);
|
||||
}
|
||||
|
||||
break;
|
||||
@ -1327,7 +1340,8 @@ public class MainActivity extends BaseActivity implements WebViewCallback,
|
||||
//Check maybe need refresh
|
||||
String articleUrl = getCurrentWebView().getUrl();
|
||||
boolean isBookmark = false;
|
||||
BookmarkItem bookmark = BookmarkItem.fromZimContentProvider(getCurrentWebView().getTitle(),articleUrl);
|
||||
BookmarkItem bookmark =
|
||||
BookmarkItem.fromZimContentProvider(getCurrentWebView().getTitle(), articleUrl);
|
||||
if (articleUrl != null && !bookmarks.contains(articleUrl)) {
|
||||
if (ZimContentProvider.getId() != null) {
|
||||
presenter.saveBookmark(bookmark);
|
||||
@ -1442,12 +1456,11 @@ public class MainActivity extends BaseActivity implements WebViewCallback,
|
||||
|
||||
private void updateBottomToolbarVisibility() {
|
||||
if (checkNull(bottomToolbar)) {
|
||||
if (sharedPreferenceUtil.getPrefBottomToolbar() && !HOME_URL.equals(
|
||||
if (!HOME_URL.equals(
|
||||
getCurrentWebView().getUrl())
|
||||
&& tabSwitcherRoot.getVisibility() != View.VISIBLE) {
|
||||
bottomToolbar.setVisibility(View.VISIBLE);
|
||||
if (getCurrentWebView() instanceof ToolbarStaticKiwixWebView
|
||||
&& sharedPreferenceUtil.getPrefBottomToolbar()) {
|
||||
if (getCurrentWebView() instanceof ToolbarStaticKiwixWebView) {
|
||||
contentFrame.setPadding(0, 0, 0,
|
||||
(int) getResources().getDimension(R.dimen.bottom_toolbar_height));
|
||||
} else {
|
||||
@ -1979,7 +1992,8 @@ public class MainActivity extends BaseActivity implements WebViewCallback,
|
||||
String url = getCurrentWebView().getUrl();
|
||||
if (url != null && !url.equals(HOME_URL)) {
|
||||
final long timeStamp = System.currentTimeMillis();
|
||||
SimpleDateFormat sdf = new SimpleDateFormat("d MMM yyyy", LanguageUtils.getCurrentLocale(this));
|
||||
SimpleDateFormat sdf =
|
||||
new SimpleDateFormat("d MMM yyyy", LanguageUtils.getCurrentLocale(this));
|
||||
HistoryListItem.HistoryItem history = new HistoryListItem.HistoryItem(
|
||||
0L,
|
||||
ZimContentProvider.getId(),
|
||||
|
@ -0,0 +1,73 @@
|
||||
package org.kiwix.kiwixmobile.main;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.view.GestureDetector;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
|
||||
public class OnSwipeTouchListener implements View.OnTouchListener {
|
||||
private final GestureDetector gestureDetector;
|
||||
|
||||
@SuppressLint("SyntheticAccessor")
|
||||
public OnSwipeTouchListener(Context ctx) {
|
||||
gestureDetector = new GestureDetector(ctx, new GestureListener());
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
public boolean onTouch(View v, MotionEvent event) {
|
||||
return gestureDetector.onTouchEvent(event);
|
||||
}
|
||||
|
||||
private final class GestureListener extends GestureDetector.SimpleOnGestureListener {
|
||||
private static final int SWIPE_THRESHOLD = 100;
|
||||
private static final int SWIPE_VELOCITY_THRESHOLD = 100;
|
||||
|
||||
@Override
|
||||
public boolean onDown(MotionEvent e) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
|
||||
boolean result = false;
|
||||
try {
|
||||
float diffY = e2.getY() - e1.getY();
|
||||
float diffX = e2.getX() - e1.getX();
|
||||
if (Math.abs(diffX) > Math.abs(diffY)) {
|
||||
if (Math.abs(diffX) > SWIPE_THRESHOLD && Math.abs(velocityX) > SWIPE_VELOCITY_THRESHOLD) {
|
||||
if (diffX > 0) {
|
||||
onSwipeRight();
|
||||
} else {
|
||||
onSwipeLeft();
|
||||
}
|
||||
result = true;
|
||||
}
|
||||
} else if (Math.abs(diffY) > SWIPE_THRESHOLD && Math.abs(velocityY) > SWIPE_VELOCITY_THRESHOLD) {
|
||||
if (diffY > 0) {
|
||||
onSwipeBottom();
|
||||
} else {
|
||||
onSwipeTop();
|
||||
}
|
||||
result = true;
|
||||
}
|
||||
} catch (Exception exception) {
|
||||
exception.printStackTrace();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public void onSwipeRight() {
|
||||
}
|
||||
|
||||
public void onSwipeLeft() {
|
||||
}
|
||||
|
||||
public void onSwipeTop() {
|
||||
}
|
||||
|
||||
public void onSwipeBottom() {
|
||||
}
|
||||
}
|
@ -19,9 +19,9 @@
|
||||
|
||||
package org.kiwix.kiwixmobile.settings;
|
||||
|
||||
import android.app.FragmentManager;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Bundle;
|
||||
import android.os.Environment;
|
||||
import android.preference.EditTextPreference;
|
||||
@ -35,6 +35,7 @@ import android.webkit.WebView;
|
||||
import android.widget.BaseAdapter;
|
||||
import android.widget.Toast;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import eu.mhutti1.utils.storage.StorageDevice;
|
||||
import eu.mhutti1.utils.storage.StorageSelectDialog;
|
||||
@ -200,7 +201,7 @@ public class KiwixSettingsActivity extends BaseActivity {
|
||||
String selectedLang = sharedPreferenceUtil.getPrefLanguage(Locale.getDefault().toString());
|
||||
List<String> languageCodeList = new LanguageUtils(getActivity()).getKeys();
|
||||
selectedLang = languageCodeList.contains(selectedLang) ? selectedLang : "en";
|
||||
String code[] = languageCodeList.toArray(new String[languageCodeList.size()]);
|
||||
String code[] = languageCodeList.toArray(new String[0]);
|
||||
String[] entries = new String[code.length];
|
||||
for (int index = 0; index < code.length; index++) {
|
||||
Locale locale = new Locale(code[index]);
|
||||
@ -229,11 +230,16 @@ public class KiwixSettingsActivity extends BaseActivity {
|
||||
}
|
||||
|
||||
private void setAppVersionNumber() {
|
||||
String version;
|
||||
version = BuildConfig.VERSION_NAME + " Build: " + BuildConfig.VERSION_CODE;
|
||||
EditTextPreference versionPref = (EditTextPreference) PrefsFragment.this
|
||||
.findPreference(PREF_VERSION);
|
||||
versionPref.setSummary(version);
|
||||
EditTextPreference versionPref = (EditTextPreference) findPreference(PREF_VERSION);
|
||||
versionPref.setSummary(BuildConfig.VERSION_NAME + " Build: " + getVersionCode());
|
||||
}
|
||||
|
||||
private int getVersionCode() {
|
||||
try {
|
||||
return getActivity().getPackageManager().getPackageInfo(getActivity().getPackageName(), 0).versionCode;
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -322,7 +328,6 @@ public class KiwixSettingsActivity extends BaseActivity {
|
||||
}
|
||||
|
||||
public void openFolderSelect() {
|
||||
FragmentManager fm = getFragmentManager();
|
||||
StorageSelectDialog dialogFragment = new StorageSelectDialog();
|
||||
Bundle b = new Bundle();
|
||||
b.putString(StorageSelectDialog.STORAGE_DIALOG_INTERNAL,
|
||||
@ -332,7 +337,7 @@ public class KiwixSettingsActivity extends BaseActivity {
|
||||
b.putInt(StorageSelectDialog.STORAGE_DIALOG_THEME, StyleUtils.dialogStyle());
|
||||
dialogFragment.setArguments(b);
|
||||
dialogFragment.setOnSelectListener(this);
|
||||
dialogFragment.show(fm, getResources().getString(R.string.pref_storage));
|
||||
dialogFragment.show(((AppCompatActivity) getActivity()).getSupportFragmentManager(), getResources().getString(R.string.pref_storage));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -66,8 +66,6 @@ public final class Constants {
|
||||
|
||||
public static final String PREF_WIFI_ONLY = "pref_wifi_only";
|
||||
|
||||
public static final String PREF_BOTTOM_TOOLBAR = "pref_bottomtoolbar";
|
||||
|
||||
public static final String PREF_KIWIX_MOBILE = "kiwix-mobile";
|
||||
|
||||
public static final String PREF_BACK_TO_TOP = "pref_backtotop";
|
||||
|
@ -4,14 +4,15 @@ import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Environment;
|
||||
import android.preference.PreferenceManager;
|
||||
import io.reactivex.Flowable;
|
||||
import io.reactivex.processors.BehaviorProcessor;
|
||||
import io.reactivex.processors.PublishProcessor;
|
||||
import java.util.Calendar;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import static org.kiwix.kiwixmobile.utils.Constants.PREF_AUTONIGHTMODE;
|
||||
import static org.kiwix.kiwixmobile.utils.Constants.PREF_BACK_TO_TOP;
|
||||
import static org.kiwix.kiwixmobile.utils.Constants.PREF_BOTTOM_TOOLBAR;
|
||||
import static org.kiwix.kiwixmobile.utils.Constants.PREF_EXTERNAL_LINK_POPUP;
|
||||
import static org.kiwix.kiwixmobile.utils.Constants.PREF_FULLSCREEN;
|
||||
import static org.kiwix.kiwixmobile.utils.Constants.PREF_HIDE_TOOLBAR;
|
||||
@ -35,12 +36,11 @@ public class SharedPreferenceUtil {
|
||||
private static final String PREF_SHOW_BOOKMARKS_CURRENT_BOOK = "show_bookmarks_current_book";
|
||||
private static final String PREF_SHOW_HISTORY_CURRENT_BOOK = "show_history_current_book";
|
||||
private SharedPreferences sharedPreferences;
|
||||
public final BehaviorProcessor<String> prefStorages;
|
||||
private final PublishProcessor<String> prefStorages = PublishProcessor.create();
|
||||
|
||||
@Inject
|
||||
public SharedPreferenceUtil(Context context) {
|
||||
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
prefStorages = BehaviorProcessor.createDefault(getPrefStorage());
|
||||
}
|
||||
|
||||
public boolean getPrefWifiOnly() {
|
||||
@ -59,9 +59,6 @@ public class SharedPreferenceUtil {
|
||||
return sharedPreferences.getBoolean(PREF_FULLSCREEN, false);
|
||||
}
|
||||
|
||||
public boolean getPrefBottomToolbar() {
|
||||
return sharedPreferences.getBoolean(PREF_BOTTOM_TOOLBAR, false);
|
||||
}
|
||||
|
||||
public boolean getPrefBackToTop() {
|
||||
return sharedPreferences.getBoolean(PREF_BACK_TO_TOP, false);
|
||||
@ -130,6 +127,10 @@ public class SharedPreferenceUtil {
|
||||
prefStorages.onNext(storage);
|
||||
}
|
||||
|
||||
public Flowable<String> getPrefStorages(){
|
||||
return prefStorages.startWith(getPrefStorage());
|
||||
}
|
||||
|
||||
public void putPrefFullScreen(boolean fullScreen) {
|
||||
sharedPreferences.edit().putBoolean(PREF_FULLSCREEN, fullScreen).apply();
|
||||
}
|
||||
|
@ -26,6 +26,7 @@ import android.provider.MediaStore.MediaColumns
|
||||
import eu.mhutti1.utils.storage.StorageDeviceUtils
|
||||
import io.reactivex.Flowable
|
||||
import io.reactivex.functions.BiFunction
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import org.kiwix.kiwixmobile.extensions.forEachRow
|
||||
import org.kiwix.kiwixmobile.extensions.get
|
||||
import java.io.File
|
||||
@ -37,8 +38,8 @@ class FileSearch @Inject constructor(private val context: Context) {
|
||||
|
||||
fun scan(defaultPath: String) =
|
||||
Flowable.combineLatest(
|
||||
Flowable.fromCallable { scanFileSystem(defaultPath) },
|
||||
Flowable.fromCallable(this::scanMediaStore),
|
||||
Flowable.fromCallable { scanFileSystem(defaultPath) }.subscribeOn(Schedulers.io()),
|
||||
Flowable.fromCallable(this::scanMediaStore).subscribeOn(Schedulers.io()),
|
||||
BiFunction<List<File>, List<File>, List<File>> { filesSystemFiles, mediaStoreFiles ->
|
||||
filesSystemFiles + mediaStoreFiles
|
||||
}
|
||||
@ -74,13 +75,19 @@ class FileSearch @Inject constructor(private val context: Context) {
|
||||
*StorageDeviceUtils.getStorageDevices(context, false).map { it.name }.toTypedArray()
|
||||
)
|
||||
|
||||
private fun scanDirectory(directory: String) = filesMatchingExtensions(directory) ?: emptyList()
|
||||
|
||||
private fun filesMatchingExtensions(directory: String) = File(directory)
|
||||
.listFiles { _, name -> name.endsWithAny(*zimFileExtensions) }
|
||||
?.toList()
|
||||
private fun scanDirectory(directory: String): List<File> = File(directory).listFiles()
|
||||
?.fold(
|
||||
mutableListOf(), { acc, file ->
|
||||
acc.apply {
|
||||
if (file.isDirectory) {
|
||||
addAll(scanDirectory(file.path))
|
||||
} else if (file.extension.isAny(*zimFileExtensions)) {
|
||||
add(file)
|
||||
}
|
||||
}
|
||||
}) ?: emptyList()
|
||||
|
||||
}
|
||||
|
||||
internal fun String.endsWithAny(vararg suffixes: String) =
|
||||
suffixes.fold(false, { acc, s -> acc or endsWith(s) })
|
||||
internal fun String.isAny(vararg suffixes: String) =
|
||||
suffixes.firstOrNull { endsWith(it) } != null
|
||||
|
@ -1,34 +0,0 @@
|
||||
package org.kiwix.kiwixmobile.views
|
||||
|
||||
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import org.kiwix.kiwixmobile.R
|
||||
import org.kiwix.kiwixmobile.extensions.inflate
|
||||
import org.kiwix.kiwixmobile.zim_manager.Language
|
||||
|
||||
class LanguageAdapter(val listItems: MutableList<Language>) : RecyclerView.Adapter<LanguageViewHolder>() {
|
||||
|
||||
override fun onCreateViewHolder(
|
||||
parent: ViewGroup,
|
||||
viewType: Int
|
||||
) = LanguageViewHolder(
|
||||
parent.inflate(R.layout.item_language, false),
|
||||
this::toggleItemAt
|
||||
)
|
||||
|
||||
override fun getItemCount() = listItems.size
|
||||
|
||||
override fun onBindViewHolder(
|
||||
holder: LanguageViewHolder,
|
||||
position: Int
|
||||
) {
|
||||
holder.bind(listItems[position], position)
|
||||
}
|
||||
|
||||
private fun toggleItemAt(position: Int) {
|
||||
listItems[position] = listItems[position].also { it.active = !it.active }
|
||||
notifyItemChanged(position)
|
||||
}
|
||||
}
|
||||
|
@ -1,66 +0,0 @@
|
||||
/*
|
||||
* Kiwix Android
|
||||
* Copyright (C) 2018 Kiwix <android.kiwix.org>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.kiwix.kiwixmobile.views
|
||||
|
||||
import android.content.Context
|
||||
import android.view.View
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import kotlinx.android.extensions.LayoutContainer
|
||||
import kotlinx.android.synthetic.main.language_selection.language_check_view
|
||||
import org.kiwix.kiwixmobile.R
|
||||
import org.kiwix.kiwixmobile.zim_manager.Language
|
||||
|
||||
/**
|
||||
* Created by judebrauer on 12/6/17
|
||||
*/
|
||||
|
||||
class LanguageSelectDialog constructor(
|
||||
context: Context
|
||||
) : AlertDialog(context) {
|
||||
|
||||
class Builder : AlertDialog.Builder, LayoutContainer {
|
||||
lateinit var dialogView: View
|
||||
override val containerView: View? by lazy { dialogView }
|
||||
lateinit var onOkClicked: (List<Language>) -> Unit
|
||||
var languages: List<Language> = listOf()
|
||||
|
||||
constructor(context: Context) : super(context)
|
||||
|
||||
constructor(
|
||||
context: Context,
|
||||
themeResId: Int
|
||||
) : super(context, themeResId)
|
||||
|
||||
override fun create(): AlertDialog {
|
||||
dialogView = View.inflate(context, R.layout.language_selection, null)
|
||||
val languageArrayAdapter = LanguageAdapter(languages.toMutableList())
|
||||
language_check_view.run {
|
||||
adapter = languageArrayAdapter
|
||||
layoutManager = LinearLayoutManager(context, RecyclerView.VERTICAL, false)
|
||||
setHasFixedSize(true)
|
||||
}
|
||||
setView(dialogView)
|
||||
setPositiveButton(android.R.string.ok) { _, _ ->
|
||||
onOkClicked.invoke(languageArrayAdapter.listItems)
|
||||
}
|
||||
return super.create()
|
||||
}
|
||||
}
|
||||
}
|
@ -1,45 +0,0 @@
|
||||
package org.kiwix.kiwixmobile.views
|
||||
|
||||
import android.graphics.Typeface
|
||||
import android.view.View
|
||||
import androidx.recyclerview.widget.RecyclerView.ViewHolder
|
||||
import kotlinx.android.extensions.LayoutContainer
|
||||
import kotlinx.android.synthetic.main.item_language.item_language_books_count
|
||||
import kotlinx.android.synthetic.main.item_language.item_language_checkbox
|
||||
import kotlinx.android.synthetic.main.item_language.item_language_localized_name
|
||||
import kotlinx.android.synthetic.main.item_language.item_language_name
|
||||
import org.kiwix.kiwixmobile.R
|
||||
import org.kiwix.kiwixmobile.utils.LanguageUtils
|
||||
import org.kiwix.kiwixmobile.zim_manager.Language
|
||||
|
||||
class LanguageViewHolder(
|
||||
override val containerView: View,
|
||||
private val onCheckboxChecked: (Int) -> Unit
|
||||
) : ViewHolder(containerView),
|
||||
LayoutContainer {
|
||||
fun bind(
|
||||
language: Language,
|
||||
position: Int
|
||||
) {
|
||||
val context = containerView.context
|
||||
item_language_name.text = language.language
|
||||
item_language_localized_name.text = context.getString(
|
||||
R.string.language_localized,
|
||||
language.languageLocalized
|
||||
)
|
||||
item_language_localized_name.typeface = Typeface.createFromAsset(
|
||||
context.assets,
|
||||
LanguageUtils.getTypeface(language.languageCode)
|
||||
)
|
||||
item_language_books_count.text =
|
||||
context.getString(R.string.language_count, language.occurencesOfLanguage)
|
||||
item_language_checkbox.setOnCheckedChangeListener(null)
|
||||
item_language_checkbox.isChecked = language.active
|
||||
item_language_checkbox.setOnCheckedChangeListener { _, _ ->
|
||||
onCheckboxChecked.invoke(position)
|
||||
}
|
||||
containerView.setOnClickListener {
|
||||
item_language_checkbox.toggle()
|
||||
}
|
||||
}
|
||||
}
|
@ -6,6 +6,7 @@ import java.util.Locale
|
||||
|
||||
@Parcelize
|
||||
data class Language constructor(
|
||||
val id: Long = 0,
|
||||
var active: Boolean,
|
||||
var occurencesOfLanguage: Int,
|
||||
var language: String,
|
||||
@ -13,11 +14,14 @@ data class Language constructor(
|
||||
var languageCode: String,
|
||||
var languageCodeISO2: String
|
||||
) : Parcelable {
|
||||
|
||||
constructor(
|
||||
locale: Locale,
|
||||
active: Boolean,
|
||||
occurrencesOfLanguage: Int
|
||||
occurrencesOfLanguage: Int,
|
||||
id: Long = 0
|
||||
) : this(
|
||||
id,
|
||||
active,
|
||||
occurrencesOfLanguage,
|
||||
locale.displayLanguage,
|
||||
@ -35,4 +39,7 @@ data class Language constructor(
|
||||
override fun equals(other: Any?): Boolean {
|
||||
return (other as Language).language == language && other.active == active
|
||||
}
|
||||
|
||||
fun matches(filter: String) =
|
||||
language.contains(filter, true) or languageLocalized.contains(filter, true)
|
||||
}
|
||||
|
@ -26,33 +26,26 @@ import android.util.Log
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import androidx.appcompat.widget.SearchView
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.ViewModelProviders
|
||||
import io.reactivex.Flowable
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import kotlinx.android.synthetic.main.zim_manager.manageViewPager
|
||||
import kotlinx.android.synthetic.main.zim_manager.tabs
|
||||
import kotlinx.android.synthetic.main.zim_manager.toolbar
|
||||
import org.kiwix.kiwixmobile.R
|
||||
import org.kiwix.kiwixmobile.base.BaseActivity
|
||||
import org.kiwix.kiwixmobile.database.newdb.dao.NewLanguagesDao
|
||||
import org.kiwix.kiwixmobile.extensions.toast
|
||||
import org.kiwix.kiwixmobile.extensions.start
|
||||
import org.kiwix.kiwixmobile.extensions.viewModel
|
||||
import org.kiwix.kiwixmobile.language.LanguageActivity
|
||||
import org.kiwix.kiwixmobile.main.MainActivity
|
||||
import org.kiwix.kiwixmobile.utils.Constants.TAG_KIWIX
|
||||
import org.kiwix.kiwixmobile.utils.LanguageUtils
|
||||
import org.kiwix.kiwixmobile.utils.StyleUtils.dialogStyle
|
||||
import org.kiwix.kiwixmobile.views.LanguageSelectDialog
|
||||
import java.io.File
|
||||
import javax.inject.Inject
|
||||
import org.kiwix.kiwixmobile.zim_manager.local_file_transfer.LocalFileTransferActivity;
|
||||
|
||||
class ZimManageActivity : BaseActivity() {
|
||||
|
||||
private val zimManageViewModel: ZimManageViewModel by lazy {
|
||||
ViewModelProviders.of(this, viewModelFactory)
|
||||
.get(ZimManageViewModel::class.java)
|
||||
}
|
||||
private val zimManageViewModel by lazy { viewModel<ZimManageViewModel>(viewModelFactory) }
|
||||
private val mSectionsPagerAdapter: SectionsPagerAdapter by lazy {
|
||||
SectionsPagerAdapter(this, supportFragmentManager)
|
||||
}
|
||||
@ -79,9 +72,6 @@ class ZimManageActivity : BaseActivity() {
|
||||
tabs.setupWithViewPager(this)
|
||||
addOnPageChangeListener(SimplePageChangeListener(this@ZimManageActivity::updateMenu))
|
||||
}
|
||||
zimManageViewModel.languageItems.observe(this, Observer {
|
||||
onLanguageItemsForDialogUpdated(it!!)
|
||||
})
|
||||
setViewPagerPositionFromIntent(intent)
|
||||
}
|
||||
|
||||
@ -96,24 +86,6 @@ class ZimManageActivity : BaseActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun onLanguageItemsForDialogUpdated(languages: List<Language>) {
|
||||
if (languages.isEmpty()) {
|
||||
toast(R.string.wait_for_load)
|
||||
} else {
|
||||
LanguageSelectDialog.Builder(this, dialogStyle())
|
||||
.apply {
|
||||
onOkClicked = {
|
||||
Flowable.fromCallable {
|
||||
languagesDao.insert(it)
|
||||
}
|
||||
.subscribeOn(Schedulers.io())
|
||||
.subscribe()
|
||||
}
|
||||
this.languages = languages
|
||||
}
|
||||
.show()
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateMenu(position: Int) {
|
||||
searchItem?.isVisible = position == 1
|
||||
@ -156,9 +128,8 @@ class ZimManageActivity : BaseActivity() {
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
when (item.itemId) {
|
||||
R.id.select_language -> {
|
||||
zimManageViewModel.requestLanguagesDialog.onNext(Unit)
|
||||
}
|
||||
R.id.select_language -> start<LanguageActivity>()
|
||||
|
||||
R.id.get_zim_nearby_device -> {
|
||||
startActivity(Intent(this, LocalFileTransferActivity::class.java));
|
||||
}
|
||||
|
@ -110,13 +110,11 @@ class ZimManageViewModel @Inject constructor(
|
||||
val deviceListIsRefreshing = MutableLiveData<Boolean>()
|
||||
val libraryListIsRefreshing = MutableLiveData<Boolean>()
|
||||
val networkStates = MutableLiveData<NetworkState>()
|
||||
val languageItems = MutableLiveData<List<Language>>()
|
||||
|
||||
val requestFileSystemCheck = PublishProcessor.create<Unit>()
|
||||
val fileSelectActions = PublishProcessor.create<FileSelectActions>()
|
||||
val requestDownloadLibrary = BehaviorProcessor.createDefault<Unit>(Unit)
|
||||
val requestFiltering = BehaviorProcessor.createDefault<String>("")
|
||||
val requestLanguagesDialog = PublishProcessor.create<Unit>()
|
||||
|
||||
private val compositeDisposable = CompositeDisposable()
|
||||
|
||||
@ -151,7 +149,6 @@ class ZimManageViewModel @Inject constructor(
|
||||
updateLibraryItems(booksFromDao, downloads, networkLibrary, languages),
|
||||
updateLanguagesInDao(networkLibrary, languages),
|
||||
updateNetworkStates(),
|
||||
updateLanguageItemsForDialog(languages),
|
||||
requestsAndConnectivtyChangesToLibraryRequests(networkLibrary),
|
||||
fileSelectActions()
|
||||
)
|
||||
@ -286,16 +283,6 @@ class ZimManageViewModel @Inject constructor(
|
||||
)
|
||||
}
|
||||
|
||||
private fun updateLanguageItemsForDialog(languages: Flowable<List<Language>>) =
|
||||
requestLanguagesDialog
|
||||
.withLatestFrom(
|
||||
languages,
|
||||
BiFunction<Unit, List<Language>, List<Language>> { _, langs -> langs })
|
||||
.subscribe(
|
||||
languageItems::postValue,
|
||||
Throwable::printStackTrace
|
||||
)
|
||||
|
||||
private fun updateNetworkStates() =
|
||||
connectivityBroadcastReceiver.networkStates.subscribe(
|
||||
networkStates::postValue, Throwable::printStackTrace
|
||||
|
@ -14,11 +14,11 @@ import javax.inject.Inject
|
||||
|
||||
class StorageObserver @Inject constructor(
|
||||
private val sharedPreferenceUtil: SharedPreferenceUtil,
|
||||
downloadDao: NewDownloadDao,
|
||||
private val downloadDao: NewDownloadDao,
|
||||
private val fileSearch: FileSearch
|
||||
) {
|
||||
|
||||
val booksOnFileSystem = scanFiles()
|
||||
val booksOnFileSystem get() = scanFiles()
|
||||
.withLatestFrom(
|
||||
downloadDao.downloads(),
|
||||
BiFunction(this::toFilesThatAreNotDownloading)
|
||||
|
@ -29,7 +29,6 @@ import android.view.ViewGroup
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.ViewModelProviders
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import io.reactivex.disposables.CompositeDisposable
|
||||
@ -40,6 +39,7 @@ import org.kiwix.kiwixmobile.R
|
||||
import org.kiwix.kiwixmobile.base.BaseFragment
|
||||
import org.kiwix.kiwixmobile.di.components.ActivityComponent
|
||||
import org.kiwix.kiwixmobile.extensions.toast
|
||||
import org.kiwix.kiwixmobile.extensions.viewModel
|
||||
import org.kiwix.kiwixmobile.utils.Constants.REQUEST_STORAGE_PERMISSION
|
||||
import org.kiwix.kiwixmobile.utils.LanguageUtils
|
||||
import org.kiwix.kiwixmobile.utils.SharedPreferenceUtil
|
||||
@ -61,11 +61,9 @@ class ZimFileSelectFragment : BaseFragment() {
|
||||
private var actionMode: ActionMode? = null
|
||||
val disposable = CompositeDisposable()
|
||||
|
||||
private val zimManageViewModel: ZimManageViewModel by lazy {
|
||||
ViewModelProviders.of(activity!!, viewModelFactory)
|
||||
.get(ZimManageViewModel::class.java)
|
||||
private val zimManageViewModel by lazy {
|
||||
activity!!.viewModel<ZimManageViewModel>(viewModelFactory)
|
||||
}
|
||||
|
||||
private val bookDelegate: BookDelegate by lazy {
|
||||
BookDelegate(sharedPreferenceUtil,
|
||||
{ offerAction(RequestOpen(it)) },
|
||||
|
@ -47,7 +47,7 @@ sealed class BookOnDiskDelegate<I : BooksOnDiskListItem, VH : BookOnDiskViewHold
|
||||
viewHolder: ViewHolder,
|
||||
itemToBind: BooksOnDiskListItem
|
||||
) {
|
||||
(viewHolder as BookOnDiskViewHolder.BookViewHolder).bind((itemToBind as BookOnDisk), selectionMode)
|
||||
(viewHolder as BookViewHolder).bind((itemToBind as BookOnDisk), selectionMode)
|
||||
}
|
||||
|
||||
override fun createViewHolder(parent: ViewGroup) =
|
||||
|
@ -5,6 +5,7 @@ import android.view.View
|
||||
import kotlinx.android.synthetic.main.header_language.header_language
|
||||
import kotlinx.android.synthetic.main.item_book.itemBookCheckbox
|
||||
import kotlinx.android.synthetic.main.item_book.item_book_article_count
|
||||
import kotlinx.android.synthetic.main.item_book.item_book_clickable_area
|
||||
import kotlinx.android.synthetic.main.item_book.item_book_date
|
||||
import kotlinx.android.synthetic.main.item_book.item_book_description
|
||||
import kotlinx.android.synthetic.main.item_book.item_book_icon
|
||||
@ -74,13 +75,13 @@ sealed class BookOnDiskViewHolder<T : BooksOnDiskListItem>(containerView: View)
|
||||
when (selectionMode) {
|
||||
MULTI -> {
|
||||
itemBookCheckbox.visibility = View.VISIBLE
|
||||
containerView.setOnClickListener { multiSelectAction?.invoke(item) }
|
||||
containerView.setOnLongClickListener(null)
|
||||
item_book_clickable_area.setOnClickListener { multiSelectAction?.invoke(item) }
|
||||
item_book_clickable_area.setOnLongClickListener(null)
|
||||
}
|
||||
NORMAL -> {
|
||||
itemBookCheckbox.visibility = View.GONE
|
||||
containerView.setOnClickListener { clickAction.invoke(item) }
|
||||
containerView.setOnLongClickListener {
|
||||
item_book_clickable_area.setOnClickListener { clickAction.invoke(item) }
|
||||
item_book_clickable_area.setOnLongClickListener {
|
||||
longClickAction?.invoke(item)
|
||||
return@setOnLongClickListener true
|
||||
}
|
||||
|
@ -17,6 +17,7 @@
|
||||
*/
|
||||
package org.kiwix.kiwixmobile.zim_manager.library_view
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.net.ConnectivityManager
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
@ -26,11 +27,10 @@ import android.view.View.VISIBLE
|
||||
import android.view.ViewGroup
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.ViewModelProviders
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import eu.mhutti1.utils.storage.StorageDevice
|
||||
import eu.mhutti1.utils.storage.support.StorageSelectDialog
|
||||
import eu.mhutti1.utils.storage.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
|
||||
@ -41,6 +41,7 @@ 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.extensions.viewModel
|
||||
import org.kiwix.kiwixmobile.library.entity.LibraryNetworkEntity.Book
|
||||
import org.kiwix.kiwixmobile.main.MainActivity
|
||||
import org.kiwix.kiwixmobile.utils.BookUtils
|
||||
@ -71,10 +72,10 @@ class LibraryFragment : BaseFragment() {
|
||||
@Inject lateinit var viewModelFactory: ViewModelProvider.Factory
|
||||
@Inject lateinit var bookUtils: BookUtils
|
||||
|
||||
private val zimManageViewModel: ZimManageViewModel by lazy {
|
||||
ViewModelProviders.of(activity!!, viewModelFactory)
|
||||
.get(ZimManageViewModel::class.java)
|
||||
private val zimManageViewModel by lazy {
|
||||
activity!!.viewModel<ZimManageViewModel>(viewModelFactory)
|
||||
}
|
||||
|
||||
private val libraryAdapter: LibraryAdapter by lazy {
|
||||
LibraryAdapter(
|
||||
BookDelegate(bookUtils, this::onBookItemClick), DividerDelegate
|
||||
@ -209,8 +210,10 @@ class LibraryFragment : BaseFragment() {
|
||||
private fun notEnoughSpaceAvailable(item: BookItem) =
|
||||
spaceAvailable < item.book.size.toLong() * 1024f
|
||||
|
||||
@SuppressLint("ImplicitSamInstance")
|
||||
private fun showStorageSelectDialog() {
|
||||
StorageSelectDialog().apply {
|
||||
StorageSelectDialog()
|
||||
.apply {
|
||||
arguments = Bundle().apply {
|
||||
putString(
|
||||
StorageSelectDialog.STORAGE_DIALOG_INTERNAL,
|
||||
|
@ -25,7 +25,10 @@ interface AbsDelegateAdapter<INSTANCE : SUPERTYPE,
|
||||
SUPERTYPE : Any,
|
||||
VIEWHOLDER : BaseViewHolder<INSTANCE>> :
|
||||
AdapterDelegate<SUPERTYPE> {
|
||||
abstract val itemClass: Class<INSTANCE>
|
||||
|
||||
val itemClass: Class<INSTANCE>
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun bind(
|
||||
viewHolder: RecyclerView.ViewHolder,
|
||||
itemToBind: SUPERTYPE
|
||||
|
@ -27,7 +27,7 @@ class AdapterDelegateManager<T>() {
|
||||
private fun getDelegateIndexFor(item: T): Int {
|
||||
for (index in 0..delegates.size()) {
|
||||
val valueAt = delegates.valueAt(index)
|
||||
if (valueAt.isFor(item)) {
|
||||
if (valueAt?.isFor(item) == true) {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,6 @@
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="?attr/listBackground"
|
||||
android:fitsSystemWindows="true"
|
||||
>
|
||||
|
||||
@ -26,7 +25,7 @@
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/recycler_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
@ -34,4 +33,5 @@
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/appbar"
|
||||
/>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
37
app/src/main/res/layout/activity_history.xml
Normal file
37
app/src/main/res/layout/activity_history.xml
Normal file
@ -0,0 +1,37 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:fitsSystemWindows="true"
|
||||
>
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:id="@+id/appbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:theme="@style/AppTheme.AppBarOverlay"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
>
|
||||
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
android:theme="@style/AppTheme.AppBarOverlay"
|
||||
app:popupTheme="@style/AppTheme.PopupOverlay"
|
||||
/>
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/recycler_view"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/appbar"
|
||||
/>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
47
app/src/main/res/layout/activity_language.xml
Normal file
47
app/src/main/res/layout/activity_language.xml
Normal file
@ -0,0 +1,47 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:fitsSystemWindows="true"
|
||||
>
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:id="@+id/appbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:theme="@style/AppTheme.AppBarOverlay"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
>
|
||||
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
android:theme="@style/AppTheme.AppBarOverlay"
|
||||
app:popupTheme="@style/AppTheme.PopupOverlay"
|
||||
/>
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/recycler_view"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/appbar"
|
||||
/>
|
||||
|
||||
<androidx.core.widget.ContentLoadingProgressBar
|
||||
android:id="@+id/language_progressbar"
|
||||
style="?android:attr/progressBarStyleLarge"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/appbar"
|
||||
/>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
29
app/src/main/res/layout/device_item.xml
Normal file
29
app/src/main/res/layout/device_item.xml
Normal file
@ -0,0 +1,29 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="horizontal"
|
||||
>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/file_name"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="10dp"
|
||||
android:textSize="18sp"
|
||||
/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/file_size"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_toEndOf="@id/file_name"
|
||||
android:gravity="end"
|
||||
android:padding="10dp"
|
||||
android:textSize="18sp"
|
||||
android:layout_toRightOf="@id/file_name"
|
||||
/>
|
||||
|
||||
</RelativeLayout>
|
@ -4,7 +4,6 @@
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:paddingEnd="0dp"
|
||||
android:paddingLeft="0dp"
|
||||
android:paddingRight="0dp"
|
||||
@ -135,5 +134,14 @@
|
||||
app:layout_constraintStart_toStartOf="@id/item_book_title"
|
||||
app:layout_constraintTop_toBottomOf="@id/item_book_label_picture"
|
||||
/>
|
||||
|
||||
<View
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:id="@+id/item_book_clickable_area"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
/>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
@ -57,4 +57,9 @@
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="9 books"
|
||||
/>
|
||||
<View
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:id="@+id/item_language_clickable_area"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"/>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
@ -1,12 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/language_check_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"/>
|
||||
|
||||
</LinearLayout>
|
@ -15,7 +15,6 @@
|
||||
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/toolbar_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?android:attr/actionBarSize"
|
||||
>
|
||||
|
53
app/src/main/res/layout/storage_select_dialog.xml
Normal file
53
app/src/main/res/layout/storage_select_dialog.xml
Normal file
@ -0,0 +1,53 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:padding="10dp"
|
||||
>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"
|
||||
android:textAlignment="center"
|
||||
android:textSize="22sp"
|
||||
/>
|
||||
|
||||
<ListView
|
||||
android:id="@+id/device_list"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
>
|
||||
|
||||
|
||||
</ListView>
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
>
|
||||
<EditText
|
||||
android:id="@+id/editText"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:ems="10"
|
||||
android:inputType="textPersonName"
|
||||
android:text="@string/slash"
|
||||
android:importantForAutofill="no"
|
||||
tools:targetApi="o"
|
||||
/>
|
||||
<Button
|
||||
android:id="@+id/button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="@string/plus"
|
||||
/>
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
</LinearLayout>
|
@ -94,7 +94,6 @@
|
||||
<string name="zim_novid">لا يوجد فيديو</string>
|
||||
<string name="no_network_connection">لا يوجد اتصال بالشبكة</string>
|
||||
<string name="get_library_over_network">استخدم الشبكة لتنزيل قائمة المحتويات. (حوالي 6 ميغابايت)</string>
|
||||
<string name="wait_for_load">لا يزال يتم تحميل المحتوى</string>
|
||||
<string name="help_2">ماذا يفعل كيويكس؟</string>
|
||||
<string name="help_3">كيويكس قارئ محتوى غير متصل، وهو يعمل إلى حد كبير مثل المتصفح، ولكن بدلا من الوصول إلى صفحات الويب على الإنترنت، فإنه يقرأ المحتوى من ملف بتنسيق ZIM.</string>
|
||||
<string name="help_4">بينما كان كيويكس مصمما في الأصل لتوفير ويكيبيديا في وضع عدم الاتصال، فإنه يقرأ أيضا محتويات أخرى.</string>
|
||||
@ -138,8 +137,6 @@
|
||||
<string name="time_minute">دق</string>
|
||||
<string name="time_second">ث</string>
|
||||
<string name="time_left">متبقي</string>
|
||||
<string name="pref_bottomtoolbar">تمكين شريط الأدوات السفلي</string>
|
||||
<string name="pref_bottomtoolbar_summary">عرض شريط أدوات بإجراءات سريعة في الجزء السفلي من الشاشة</string>
|
||||
<string name="pref_autonightmode_summary">التبديل تلقائيا بين وضع اليلي والنهاري.</string>
|
||||
<string name="pref_autonightmode">الوضع الليلي الآلي</string>
|
||||
<string name="pref_external_link_popup_title">التحذير عند إدخال وصلات خارجية</string>
|
||||
|
@ -94,7 +94,6 @@
|
||||
<string name="zim_novid">Без видео клипове</string>
|
||||
<string name="no_network_connection">Няма връзка с мрежата</string>
|
||||
<string name="get_library_over_network">Използвайте мрежата за даунлоуд на списъка със съдържанието.\n(Приблизително 6MB)</string>
|
||||
<string name="wait_for_load">Съдържанието все още се зарежда</string>
|
||||
<string name="help_2">Какво прави Kiwix?</string>
|
||||
<string name="help_3">Kiwix е офлайн четец на съдържание. Той действа като браузър, но вместо да зарежда съдържанието от онлайн уеб страници, той зарежда съдържанието от файл в ZIM формат.</string>
|
||||
<string name="help_4">Въпреки че Kiwix е проектиран първоначално, да предоставя достъп до Уикипедия офлайн, той е способен, да предоставя достъп и до друго съдържание.</string>
|
||||
@ -138,8 +137,6 @@
|
||||
<string name="time_minute">мин.</string>
|
||||
<string name="time_second">сек.</string>
|
||||
<string name="time_left">остават</string>
|
||||
<string name="pref_bottomtoolbar">Позволете туулбара от долната страна</string>
|
||||
<string name="pref_bottomtoolbar_summary">Показва туулбар с бързи действия от долната страна на екрана</string>
|
||||
<string name="pref_autonightmode_summary">Превключвай автоматично между дневен и нощен режим.</string>
|
||||
<string name="pref_autonightmode">Автоматичен нощен режим.</string>
|
||||
<string name="pref_external_link_popup_title">При зареждане на външни препратки</string>
|
||||
|
@ -94,7 +94,6 @@
|
||||
<string name="zim_novid">কোন ভিডিও নেই</string>
|
||||
<string name="no_network_connection">কোন নেটওয়ার্ক সংযোগ নেই</string>
|
||||
<string name="get_library_over_network">বিষয়বস্তুর তালিকা ডাউনলোড করতে নেটওয়ার্ক ব্যবহার করুন (আনুমানিক ৬ MB)</string>
|
||||
<string name="wait_for_load">বিষয়বস্তু এখনো লোড হচ্ছে</string>
|
||||
<string name="help_2">কিউইক্স কি কাজ করে?</string>
|
||||
<string name="help_3">কিউইক্স একটি অফলাইনভিত্তিক নিবন্ধ পাঠক। এটি একটি ব্রাউজারের মত অনেকটা কাজ করে কিন্তু অনলাইনে ওয়েব পৃষ্ঠাগুলিতে ঢুকার পরিবর্তে, এটি ZIM ফরম্যাটে একটি ফাইল থেকে বিষয়বস্তু পড়ে।</string>
|
||||
<string name="help_4">কিউইক্স মূলত উইকিপিডিয়াকে অফলাইনে সরবরাহ করার জন্য নকশা করা হয়েছে, তবে এটি অন্যান্য বিষয়গুলিও পড়ে থাকে।</string>
|
||||
@ -140,7 +139,6 @@
|
||||
<string name="time_left">বামে</string>
|
||||
<string name="pref_autonightmode_summary">স্বয়ংক্রিয়ভাবে দিন এবং রাতের মোডে স্যুইচ করুন।</string>
|
||||
<string name="pref_autonightmode">স্বয়ংক্রিয় রাত্রি মোড</string>
|
||||
<string name="language_count">(%1$dটি)</string>
|
||||
<string name="do_not_ask_anymore">আর জিজ্ঞাসা করবেন না</string>
|
||||
<string name="your_languages">নির্বাচিত ভাষা</string>
|
||||
<string name="other_languages">অন্যান্য ভাষাসমূহ:</string>
|
||||
|
@ -94,7 +94,6 @@
|
||||
<string name="zim_novid">Video ebet</string>
|
||||
<string name="no_network_connection">N\'eus kevreadur rouedad ebet</string>
|
||||
<string name="get_library_over_network">Implijout ar rouedad evit pellgargañ ar roll endalc\'had (war-dro 6Mo).</string>
|
||||
<string name="wait_for_load">O kargañ emañ an endalc\'had c\'hoazh</string>
|
||||
<string name="help_2">Petra a ra Kiwix ?</string>
|
||||
<string name="help_3">Ul lenner endalc\'had ezlinenn eo Kiwix. Ober a ra traoù hogos evel ur merdeer, met e-lec\'h diraez pajennoù web enlinenn e lenn an endalc\'had diwar ur restr er furmad ZIM.</string>
|
||||
<string name="help_4">Daoust ma\'z eo bet krouet Kiwix evit pourchas Wikipedia ezlinenn e lenn endalc\'hioù all ivez.</string>
|
||||
@ -135,8 +134,6 @@
|
||||
<string name="pref_wifi_only">Pellgargañ an endalc\'had dre WiFi hepken</string>
|
||||
<string name="time_day">deiz</string>
|
||||
<string name="time_left">kleiz</string>
|
||||
<string name="pref_bottomtoolbar">Gweredekaat ar varrenn ostilhoù en traoñ</string>
|
||||
<string name="pref_bottomtoolbar_summary">Diskwel ur varrenn ostilhoù gant oberoù prim e traoñ ar skramm</string>
|
||||
<string name="pref_autonightmode_summary">Cheñch ent emgefre etre ar modoù noz ha deiz.</string>
|
||||
<string name="pref_autonightmode">Mod noz emgefreek</string>
|
||||
<string name="pref_external_link_popup_title">Kemenn pa\'z eer liammoù diavaez</string>
|
||||
|
@ -89,7 +89,6 @@
|
||||
<string name="zim_novid">Ingen videoer</string>
|
||||
<string name="no_network_connection">Ingen netværksforbindelse</string>
|
||||
<string name="get_library_over_network">Brug netværk til at hente indholdsliste. (Cirka 6 MB)</string>
|
||||
<string name="wait_for_load">Indhold indlæses stadig</string>
|
||||
<string name="help_2">Hvad gør Kiwix?</string>
|
||||
<string name="help_3">Kiwix er en lokal indholdlæser. Den fungerer meget lig en browser, men i stedet for at tilgå internetsider, så læser den indhold fra en fil i ZIM-formatet.</string>
|
||||
<string name="help_4">Selvom Kiwix oprindelig blev designet for at tilbyde Wikipedia lokalt, så læser det også andet indhold.</string>
|
||||
@ -129,8 +128,6 @@
|
||||
<string name="time_day">dag</string>
|
||||
<string name="time_hour">t</string>
|
||||
<string name="time_left">tilbage</string>
|
||||
<string name="pref_bottomtoolbar">Aktiver knapværktøjslinje</string>
|
||||
<string name="pref_bottomtoolbar_summary">Vis en værktøjslinje med hurtige handling i bunden af skærmen</string>
|
||||
<string name="pref_autonightmode_summary">Skift automatisk mellem dag- og nattilstand.</string>
|
||||
<string name="pref_autonightmode">Automatisk nattilstand</string>
|
||||
<string name="pref_external_link_popup_title">Advar når man bruger eksterne henvisninger</string>
|
||||
|
@ -90,7 +90,6 @@
|
||||
<string name="zim_novid">Keine Videos</string>
|
||||
<string name="no_network_connection">Keine Netzwerkverbindung</string>
|
||||
<string name="get_library_over_network">Netzwerk verwenden, um die Inhaltsliste herunterzuladen (ungefähr 6 MB).</string>
|
||||
<string name="wait_for_load">Inhalt lädt noch</string>
|
||||
<string name="help_2">Was macht Kiwix?</string>
|
||||
<string name="help_3">Kiwix ist eine Lesesoftware für Offlineinhalte. Sie funktioniert wie ein Browser, aber anstatt auf Webseiten online zuzugreifen, liest sie Inhalte von einer Datei im ZIM-Format.</string>
|
||||
<string name="help_4">Obwohl Kiwix ursprünglich entwickelt wurde, um Wikipedia offline zur Verfügung zu stellen, liest es auch andere Inhalte.</string>
|
||||
@ -134,8 +133,6 @@
|
||||
<string name="time_minute">Minuten</string>
|
||||
<string name="time_second">Sekunden</string>
|
||||
<string name="time_left">verbleibend</string>
|
||||
<string name="pref_bottomtoolbar">Untere Werkzeugleiste aktivieren</string>
|
||||
<string name="pref_bottomtoolbar_summary">Eine Werkzeugleiste mit Schnellzugriffsaktionen am unteren Bildschirmrand anzeigen</string>
|
||||
<string name="pref_autonightmode_summary">Automatisch zwischen Tag- und Nachtmodus umschalten.</string>
|
||||
<string name="pref_autonightmode">Automatischer Nachtmodus</string>
|
||||
<string name="pref_external_link_popup_title">Beim Einfügen von externen Links warnen</string>
|
||||
|
@ -85,7 +85,6 @@
|
||||
<string name="zim_novid">Χωρίς βίντεο</string>
|
||||
<string name="no_network_connection">Δεν υπάρχει σύνδεση στο δίκτυο</string>
|
||||
<string name="get_library_over_network">Χρησιμοποιήστε δίκτυο για να κατεβάσετε την λίστα περιεχομένων. (Περίπου 6MB)</string>
|
||||
<string name="wait_for_load">Το περιεχόμενο φορτώνει ακόμα</string>
|
||||
<string name="help_2">Τι κάνει το Kiwix;</string>
|
||||
<string name="help_3">Το Kiwix είναι ένας αναγνώστης περιεχομένου εκτός σύνδεσης. Φέρεται πολύ σαν περιηγητής αλλά αντί να έχει πρόσβαση σε διαδικτυακές σελίδες, διαβάζει περιεχόμενο από ένα αρχείο σε μορφοποίηση ZIM.</string>
|
||||
<string name="help_5">Που είναι το περιεχόμενο;</string>
|
||||
|
@ -94,7 +94,6 @@
|
||||
<string name="zim_novid">Neniu Videaĵo</string>
|
||||
<string name="no_network_connection">Reta konekto mankas</string>
|
||||
<string name="get_library_over_network">Uzi reton por elŝuti liston de enhavo (proksimume 6 megabajtoj)</string>
|
||||
<string name="wait_for_load">Enhavo Estas Ankoraŭ Ŝarĝata</string>
|
||||
<string name="help_2">Kion faras Kiwix?</string>
|
||||
<string name="help_3">Kiwix estas senkonekta legilo de enhavo. Ĝi estas simila al TTT-legilo, sed anstataŭ legi retpaĝojn konektite, ĝi legas enhavon en ZIM-dosiero.</string>
|
||||
<string name="help_4">Kiwix estis originale farita por senkonekta legado de Vikipedio, sed ĝi povas legi alian enhavon.</string>
|
||||
@ -135,8 +134,6 @@
|
||||
<string name="pref_wifi_only">Elŝuti nur per vifio</string>
|
||||
<string name="time_day">tago</string>
|
||||
<string name="time_left">restas</string>
|
||||
<string name="pref_bottomtoolbar">Ŝalti malsupran ilobreton</string>
|
||||
<string name="pref_bottomtoolbar_summary">Montri ilobreton kun rapidaj agoj ĉe la malsupro de la ekrano</string>
|
||||
<string name="pref_autonightmode_summary">Aŭtomate ŝalti tagan/noktan reĝimon</string>
|
||||
<string name="pref_autonightmode">Aŭtomata nokta reĝimo</string>
|
||||
<string name="pref_external_link_popup_title">Averti pri aliro al eksteraj ligiloj</string>
|
||||
|
@ -92,7 +92,6 @@
|
||||
<string name="zim_novid">Ningún video</string>
|
||||
<string name="no_network_connection">Ninguna conexión de red</string>
|
||||
<string name="get_library_over_network">Usar la red para descargar la lista de contenido. (Aproximadamente 6MB)</string>
|
||||
<string name="wait_for_load">Todavía está cargando el contenido</string>
|
||||
<string name="help_2">¿Qué hace Kiwix?</string>
|
||||
<string name="help_3">Kiwix es un lector de contenido desconectado. Actúa mucho como un navegador pero en vez de acceder páginas web en línea, lee contenido de un archivo en formato ZIM.</string>
|
||||
<string name="help_4">Si bien Kiwix se diseñó originalmente para proporcionar una Wikipedia sin conexión, también puede leer otros contenidos.</string>
|
||||
@ -132,8 +131,6 @@
|
||||
<string name="pref_wifi_only">Descargar contenido solo mediante wifi</string>
|
||||
<string name="time_day">día</string>
|
||||
<string name="time_left">faltan</string>
|
||||
<string name="pref_bottomtoolbar">Activar barra de herramientas inferior</string>
|
||||
<string name="pref_bottomtoolbar_summary">Mostrar una barra con acciones rápidas en la parte inferior de la pantalla</string>
|
||||
<string name="pref_autonightmode_summary">Cambiar automáticamente entre el modo diurno y el nocturno</string>
|
||||
<string name="pref_autonightmode">Modo nocturno automatizado</string>
|
||||
<string name="pref_external_link_popup_title">Advertir al seguir enlaces externos</string>
|
||||
|
@ -93,7 +93,6 @@
|
||||
<string name="zim_novid">Bideorik ez</string>
|
||||
<string name="no_network_connection">Ez dago konexiorik</string>
|
||||
<string name="get_library_over_network">Sarea erabili eduki zerrenda deskargatzeko. (6MB inguru)</string>
|
||||
<string name="wait_for_load">Oraindik Edukia Kargatzen</string>
|
||||
<string name="help_2">Zer egiten du Kiwix-ek?</string>
|
||||
<string name="help_3">Kiwix konexio gabeko eduki irakurlea da. Nabigatzaile baten moduan erabiltzen da, baina sarean dauden web orrialdeetan sartu beharrean, ZIM formatoko edukia irakurtzen du.</string>
|
||||
<string name="help_4">Nahiz eta, Kiwix, berez, Wikipedia konexiorik gabe erabiltzeko sortu, beste edukiak ere eskeintzen ditu.</string>
|
||||
@ -134,8 +133,6 @@
|
||||
<string name="time_day">eguna</string>
|
||||
<string name="time_second">k</string>
|
||||
<string name="time_left">joanda</string>
|
||||
<string name="pref_bottomtoolbar">Gaitu beheko tresna-barra</string>
|
||||
<string name="pref_bottomtoolbar_summary">Gaitu ekintza azkarretako tresna-barra pantailaren beheko partean</string>
|
||||
<string name="pref_autonightmode_summary">Automatikoki piztu egun eta gauaren arteko modoa.</string>
|
||||
<string name="pref_autonightmode">Gaueko modo automatikoa</string>
|
||||
<string name="pref_external_link_popup_title">Kanpo esteketara sartzerakoan ohartarazi</string>
|
||||
|
@ -87,7 +87,6 @@
|
||||
<string name="zim_novid">Ei videoita</string>
|
||||
<string name="no_network_connection">Ei verkkoyhteyttä</string>
|
||||
<string name="get_library_over_network">Lataa sisältöluettelo käyttämällä verkkoa. (Noin 6 Mt)</string>
|
||||
<string name="wait_for_load">Sisältö latautuu edelleen</string>
|
||||
<string name="help_2">Mitä Kiwix tekee?</string>
|
||||
<string name="help_5">Missä sisältö on?</string>
|
||||
<string name="help_6">Sisältöämme ylläpidetään Kiwixin verkkosivulla.</string>
|
||||
|
@ -87,7 +87,6 @@
|
||||
<string name="zim_novid">Aucune vidéo</string>
|
||||
<string name="no_network_connection">Aucune connexion réseau</string>
|
||||
<string name="get_library_over_network">Utiliser le réseau pour télécharger la liste de contenu (environ 6Mo).</string>
|
||||
<string name="wait_for_load">Le contenu est encore en cours de chargement</string>
|
||||
<string name="help_2">Que fait Kiwix ?</string>
|
||||
<string name="help_3">Kiwix est un lecteur de contenu hors connexion. Il agit quasiment comme un navigateur, mais au lieu d’accéder à des pages web en ligne, il lit le contenu depuis un fichier au format ZIM.</string>
|
||||
<string name="help_4">Bien que Kiwix ait été conçu au départ pour fournir Wikipédia hors connexion, il lit aussi d’autre contenu.</string>
|
||||
@ -126,8 +125,6 @@
|
||||
<string name="pref_wifi_only">Télécharger le contenu uniquement via WiFi</string>
|
||||
<string name="time_day">jour</string>
|
||||
<string name="time_left">restante(s)</string>
|
||||
<string name="pref_bottomtoolbar">Activer la barre d’outils en bas</string>
|
||||
<string name="pref_bottomtoolbar_summary">Afficher une barre d’outils avec des actions rapides en bas de l’écran</string>
|
||||
<string name="pref_autonightmode_summary">Basculer automatiquement entre les modes jour et nuit.</string>
|
||||
<string name="pref_autonightmode">Mode nuit automatisé</string>
|
||||
<string name="pref_external_link_popup_title">Avertir lors de l\'accès à des liens externes</string>
|
||||
|
@ -91,7 +91,6 @@
|
||||
<string name="zim_novid">Sen vídeos</string>
|
||||
<string name="no_network_connection">Ningunha conexión de rede</string>
|
||||
<string name="get_library_over_network">Usar a rede para descargar a lista de contido. (Aproximadamente 6MB)</string>
|
||||
<string name="wait_for_load">Aínda está cargando o contido</string>
|
||||
<string name="help_2">Que fai Kiwix?</string>
|
||||
<string name="help_3">Kiwix é un lector de contido desconectado. Actúa como un navegador web, pero en vez de acceder a páxinas web en liña, le o contido dun ficheiro en formato ZIM.</string>
|
||||
<string name="help_4">Se ben Kiwix deseñouse orixinalmente para proporcionar Wikipedia sen conexión, tamén pode ler outros contidos.</string>
|
||||
@ -132,8 +131,6 @@
|
||||
<string name="pref_wifi_only">Descargar contido só mediante WiFi</string>
|
||||
<string name="time_day">día</string>
|
||||
<string name="time_left">esquerda</string>
|
||||
<string name="pref_bottomtoolbar">Activar a barra de ferramentas inferior</string>
|
||||
<string name="pref_bottomtoolbar_summary">Mostra unha barra de ferramentas con as accións rápidas na parte inferior da pantalla</string>
|
||||
<string name="pref_autonightmode_summary">Cambiar automaticamente entre o modo diurno e o nocturno</string>
|
||||
<string name="pref_autonightmode">Modo nocturno automatizado</string>
|
||||
<string name="pref_external_link_popup_title">Avisar cando introducir ligazóns externas</string>
|
||||
|
@ -94,7 +94,6 @@
|
||||
<string name="zim_novid">चलचित्र नहीं</string>
|
||||
<string name="no_network_connection">नेटवर्क कनेक्शन नहीं</string>
|
||||
<string name="get_library_over_network">सामग्री सूची डाउनलोड करने के लिए नेटवर्क का उपयोग करें। (लगभग 6 एमबी</string>
|
||||
<string name="wait_for_load">सामग्री अभी भी लोड हो रही</string>
|
||||
<string name="help_2">किविक्स क्या करता है?</string>
|
||||
<string name="help_3">किवीक्स ऑफ़लाइन सामग्री रीडर है यह एक ब्राउज़र की तरह बहुत ज्यादा काम करता है लेकिन ऑनलाइन वेब पेजों को एक्सेस करने के बजाय, यह ZIM प्रारूप में फ़ाइल से सामग्री पढ़ता है।</string>
|
||||
<string name="help_4">जबकि किवीक्स को मूल रूप से विकिपीडिया ऑफ़लाइन उपलब्ध कराने के लिए डिज़ाइन किया गया है, यह अन्य सामग्री भी पढ़ता है।</string>
|
||||
@ -138,8 +137,6 @@
|
||||
<string name="time_minute">मिन</string>
|
||||
<string name="time_second">स्</string>
|
||||
<string name="time_left">बाएँ ओर</string>
|
||||
<string name="pref_bottomtoolbar">तलस्त साधनपट्टी को सक्षम करें</string>
|
||||
<string name="pref_bottomtoolbar_summary">स्क्रीन के ताल में त्वरित-कार्य सुविधा युक्त साधनपट्टी को प्रदर्शित करें</string>
|
||||
<string name="pref_autonightmode_summary">स्वचालित रूप से दिन और रात मोड के बीच स्विच करें।</string>
|
||||
<string name="pref_autonightmode">स्वचालित रात मोड</string>
|
||||
<string name="pref_external_link_popup_title">बाह्य कड़ियों में प्रवेश करने पर सावधान करें</string>
|
||||
|
@ -92,7 +92,6 @@
|
||||
<string name="zim_novid">Nincsenek videók</string>
|
||||
<string name="no_network_connection">Nincs hálózati kapcsolat</string>
|
||||
<string name="get_library_over_network">Használd a hálózatot a tartalomlista letöltéséhez. (Körülbelül 6MB)</string>
|
||||
<string name="wait_for_load">A tartalom még töltődik</string>
|
||||
<string name="help_2">Mit csinál a Kiwix?</string>
|
||||
<string name="help_3">A Kiwix egy offline tartalom olvasó. Úgy viselkedik, mint egy böngésző, de az online weboldalak betöltése helyett a tartalmát egy ZIM formátumú fájlból olvassa.</string>
|
||||
<string name="help_4">Bár a Kiwix eredetileg az offline Wikipédia megvalósítására lett tervezve, más tartalmakat is olvas.</string>
|
||||
@ -136,8 +135,6 @@
|
||||
<string name="time_minute">perc</string>
|
||||
<string name="time_second">másodperc</string>
|
||||
<string name="time_left">maradt</string>
|
||||
<string name="pref_bottomtoolbar">Alsó eszköztár engedélyezése</string>
|
||||
<string name="pref_bottomtoolbar_summary">Jeleníts meg a képernyő alján egy eszköztárat a gyors műveleteknek</string>
|
||||
<string name="pref_autonightmode_summary">Automatikusan vált nappali és éjszakai mód között.</string>
|
||||
<string name="pref_autonightmode">Automatikus éjszakai mód</string>
|
||||
<string name="pref_external_link_popup_title">Figyelmeztess külső hivatkozások indítása esetén</string>
|
||||
|
@ -94,7 +94,6 @@
|
||||
<string name="zim_novid">동영상 없음</string>
|
||||
<string name="no_network_connection">네트워크 연결 없음</string>
|
||||
<string name="get_library_over_network">콘텐츠 목록의 다운로드를 위해 네트워크를 사용합니다. (약 6MB)</string>
|
||||
<string name="wait_for_load">내용을 계속 불러오고 있습니다</string>
|
||||
<string name="help_2">Kiwix의 역할이 무엇입니까?</string>
|
||||
<string name="help_3">Kiwix는 오프라인 콘텐츠 리더입니다. 브라우저와 매우 비슷하게 동작하지만 온라인 웹 페이지에 접근하는 대신 ZIM 포맷의 파일로부터 내용을 읽습니다.</string>
|
||||
<string name="help_4">Kiwix가 본래 오프라인 상태에서 위키백과를 제공하도록 설계되었으나 다른 콘텐츠도 읽을 수 있습니다.</string>
|
||||
@ -138,8 +137,6 @@
|
||||
<string name="time_minute">분</string>
|
||||
<string name="time_second">초</string>
|
||||
<string name="time_left">남음</string>
|
||||
<string name="pref_bottomtoolbar">하단 도구 모음 사용</string>
|
||||
<string name="pref_bottomtoolbar_summary">화면 하단에 빠른 동작들이 포함된 도구 모음을 표시합니다</string>
|
||||
<string name="pref_autonightmode_summary">야간 모드와 낮 모드를 자동으로 전환합니다.</string>
|
||||
<string name="pref_autonightmode">자동 야간 모드</string>
|
||||
<string name="pref_external_link_popup_title">외부 링크에 진입할 때 경고합니다</string>
|
||||
|
@ -96,8 +96,6 @@
|
||||
<string name="time_hour">o</string>
|
||||
<string name="time_minute">m</string>
|
||||
<string name="time_left">links</string>
|
||||
<string name="pref_bottomtoolbar">Zèt de gereidsjapsbalk óngeraan aan</string>
|
||||
<string name="pref_bottomtoolbar_summary">Tuin \'ne gereidsjapsbalk mit snel hanjelinge óngeraan \'t sjerm</string>
|
||||
<string name="pref_autonightmode_summary">Sjakel autematis tösse daag- en nachmodus</string>
|
||||
<string name="pref_autonightmode">Automatiseerde nachmodus</string>
|
||||
<string name="pref_external_link_popup_title">Waorsjoewing mich wen ich nao \'nen externe link gaon</string>
|
||||
|
@ -94,7 +94,6 @@
|
||||
<string name="zim_novid">Нема видеа</string>
|
||||
<string name="no_network_connection">Немате мрежна врска</string>
|
||||
<string name="get_library_over_network">Користи мрежа за преземање на списокот содржини (прибл. 6 МБ)</string>
|
||||
<string name="wait_for_load">Содржината сè уште се вчитува</string>
|
||||
<string name="help_2">Што прави Kiwix?</string>
|
||||
<string name="help_3">Kiwix е вонмрежен читач на содржини. Работи како прелистувач но чита содржини од податотека во ZIM-формат место од семрежјето.</string>
|
||||
<string name="help_4">Иако Kiwix е првично предвиден како вонмрежен читач на Википедија, но сега чита и други содржини.</string>
|
||||
@ -138,8 +137,6 @@
|
||||
<string name="time_minute">мин</string>
|
||||
<string name="time_second">с</string>
|
||||
<string name="time_left">лево</string>
|
||||
<string name="pref_bottomtoolbar">Вклучи алатник најдолу</string>
|
||||
<string name="pref_bottomtoolbar_summary">Прикажи алатник за брзи дејства најдолу во екранот.</string>
|
||||
<string name="pref_autonightmode_summary">Автоматски префрлај меѓу дневен и ноќен режим.</string>
|
||||
<string name="pref_autonightmode">Автоматски ноќен режим</string>
|
||||
<string name="pref_external_link_popup_title">Предупреди ме кога се внесуваат надворешни врски</string>
|
||||
|
@ -109,8 +109,6 @@
|
||||
<string name="pref_wifi_only">Pobierz zawartość tylko poprzez WiFi</string>
|
||||
<string name="time_day">dzień</string>
|
||||
<string name="time_left">do lewej</string>
|
||||
<string name="pref_bottomtoolbar">Włącz dolny pasek narzędzi</string>
|
||||
<string name="pref_bottomtoolbar_summary">Wyświetl pasek narzędzi z szybkimi akcjami u dołu ekranu</string>
|
||||
<string name="pref_autonightmode_summary">Przełączaj automatycznie między trybami dnia i nocy.</string>
|
||||
<string name="pref_autonightmode">Zautomatyzowany tryb nocny.</string>
|
||||
<string name="do_not_ask_anymore">Nie pytaj więcej</string>
|
||||
|
@ -60,7 +60,6 @@
|
||||
<string name="zim_simple">اسان</string>
|
||||
<string name="zim_nopic">هيڅ دوتنه نشته</string>
|
||||
<string name="zim_novid">هيڅ ويډيو نسته</string>
|
||||
<string name="wait_for_load">منځپانګې تر وسه هم حرکت کوي</string>
|
||||
<string name="tts_pause">تفریحي درونه</string>
|
||||
<string name="tts_resume">انتظارول</string>
|
||||
<string name="tts_stop">درول</string>
|
||||
|
@ -93,7 +93,6 @@
|
||||
<string name="zim_novid">Sem vídeos</string>
|
||||
<string name="no_network_connection">Nenhuma ligação de rede</string>
|
||||
<string name="get_library_over_network">Usar a rede para descarregar a lista de conteúdos (aproximadamente 6 MB).</string>
|
||||
<string name="wait_for_load">Ainda está a carregar o conteúdo</string>
|
||||
<string name="help_2">O que faz o Kiwix?</string>
|
||||
<string name="help_3">O Kiwix é um leitor de conteúdo offline. Funciona tal como um navegador mas, em vez de aceder a páginas da Internet online, lê o conteúdo de um ficheiro no formato ZIM.</string>
|
||||
<string name="help_4">Embora o Kiwix tenha sido originalmente desenhado para fornecer a Wikipédia offline, também lê outros conteúdos.</string>
|
||||
@ -134,8 +133,6 @@
|
||||
<string name="pref_wifi_only">Descarregar conteúdo apenas por WiFi</string>
|
||||
<string name="time_day">dia</string>
|
||||
<string name="time_left">esquerda</string>
|
||||
<string name="pref_bottomtoolbar">Ativar a barra de ferramentas na parte inferior</string>
|
||||
<string name="pref_bottomtoolbar_summary">Mostrar uma barra de ferramentas com as ações rápidas na parte inferior do ecrã</string>
|
||||
<string name="pref_autonightmode_summary">Alternar automaticamente entre o modo diurno e noturno.</string>
|
||||
<string name="pref_autonightmode">Modo noturno automático</string>
|
||||
<string name="pref_external_link_popup_title">Avisar quando introduzir hiperligações externas</string>
|
||||
|
@ -91,7 +91,6 @@
|
||||
<string name="zim_novid">Fără video</string>
|
||||
<string name="no_network_connection">Nu există conexiune de rețea</string>
|
||||
<string name="get_library_over_network">Folosește rețeaua să descarci lista de conținut. (Aproximativ 6MB)</string>
|
||||
<string name="wait_for_load">Conținutul încă se încarcă</string>
|
||||
<string name="help_2">Ce face Kiwix?</string>
|
||||
<string name="help_3">Kiwix este un server de citit conținut off-line. Este aproape ca un browser dar în loc să acceseze paginile web online citește conținut de la un fișier în formatul ZIM.</string>
|
||||
<string name="help_4">În timp ce Kiwix a fost făcut inițial să ofere Wikipedia off-line de asemenea citește și alt conținut.</string>
|
||||
@ -130,8 +129,6 @@
|
||||
<string name="pref_wifi_only">Descarcă conținut numai prin WiFi</string>
|
||||
<string name="time_day">zi</string>
|
||||
<string name="time_left">stânga</string>
|
||||
<string name="pref_bottomtoolbar">Activează bara de unelte de jos</string>
|
||||
<string name="pref_bottomtoolbar_summary">Arată o bară de unelte cu acțiuni rapide din partea de jos a ecranului</string>
|
||||
<string name="pref_autonightmode_summary">Schimbă automat între modul de zi și de noapte.</string>
|
||||
<string name="pref_autonightmode">Mod automat de noapte</string>
|
||||
<string name="pref_external_link_popup_title">Atenționați când introduceți legături extern</string>
|
||||
|
@ -94,7 +94,6 @@
|
||||
<string name="zim_novid">Нет видео</string>
|
||||
<string name="no_network_connection">Отсутствует сетевое подключение</string>
|
||||
<string name="get_library_over_network">Используйте сеть, чтобы загрузить список контента. (Около 6MB)</string>
|
||||
<string name="wait_for_load">Содержимое Ещё Загружается</string>
|
||||
<string name="help_2">Что делает Kiwix?</string>
|
||||
<string name="help_3">Kiwix - это автономный контент-ридер. Он действует схоже с браузером, но вместо обращения к онлайн страницам, он считывает содержимое из файла в формате ZIM.</string>
|
||||
<string name="help_4">Хотя Kiwix и был изначально разработан, чтобы просматривать Wikipedia офлайн, он также пригоден и для другого содержимого.</string>
|
||||
@ -138,8 +137,6 @@
|
||||
<string name="time_minute">м</string>
|
||||
<string name="time_second">с</string>
|
||||
<string name="time_left">осталось</string>
|
||||
<string name="pref_bottomtoolbar">Включить нижнюю панель управления</string>
|
||||
<string name="pref_bottomtoolbar_summary">Отобразить панель управления с быстрыми действиями внизу экрана</string>
|
||||
<string name="pref_autonightmode_summary">Автоматически переключать между дневным и ночным режимами.</string>
|
||||
<string name="pref_autonightmode">Автоматический ночной режим</string>
|
||||
<string name="pref_external_link_popup_title">Предупреждать когда происходит ввод внешних ссылок</string>
|
||||
|
@ -93,7 +93,6 @@
|
||||
<string name="zim_novid">Perunu vìdeu</string>
|
||||
<string name="no_network_connection">Peruna connessione de retza</string>
|
||||
<string name="get_library_over_network">Imprea sa retza pro iscarrigare sa lista de cuntenutos (pagu prus o mancu 6MB)</string>
|
||||
<string name="wait_for_load">Cuntenutu galu in carrigamentu</string>
|
||||
<string name="help_2">Ite faghet Kiwix?</string>
|
||||
<string name="help_3">Kiwix est unu leghidore de cuntenutu non in lìnia. Funtzionat comente unu navigadore ma imbetzes de intrare in pàginas web in lìnia leghet cuntenutu dae unu documentu in formatu ZIM.</string>
|
||||
<string name="help_4">Fintzas si Kiwix est istadu creadu, in orìgine, pro lèghere Wikipèdia chene lìnia, leghet fintzas àteros cuntenutos.</string>
|
||||
@ -135,8 +134,6 @@
|
||||
<string name="time_day">die</string>
|
||||
<string name="time_hour">o</string>
|
||||
<string name="time_left">galu</string>
|
||||
<string name="pref_bottomtoolbar">Abìlita sa barra de sas ainas in fundu</string>
|
||||
<string name="pref_bottomtoolbar_summary">Ammustra una barra de sas ainas in fundu a s\'ischermu</string>
|
||||
<string name="pref_autonightmode_summary">Cola in manera automàtica intre sa modalidade die e note.</string>
|
||||
<string name="pref_autonightmode">Modalidade note automàtica</string>
|
||||
<string name="pref_external_link_popup_title">Avisa cando ses aberende unu ligàmene esternu</string>
|
||||
|
@ -66,7 +66,6 @@
|
||||
<string name="zim_simple">Enostavno</string>
|
||||
<string name="zim_nopic">Ni slik</string>
|
||||
<string name="zim_novid">Ni videoposnetkov</string>
|
||||
<string name="wait_for_load">Vsebina se še nalaga</string>
|
||||
<string name="pref_storage">Shranjevanje</string>
|
||||
<string name="pref_current_folder">Trenutna mapa</string>
|
||||
<string name="tts_pause">premor</string>
|
||||
|
@ -92,7 +92,6 @@
|
||||
<string name="zim_novid">S\’ka Video</string>
|
||||
<string name="no_network_connection">S\’ka lidhje rrjeti</string>
|
||||
<string name="get_library_over_network">Përdor rrjetin për shkarkim liste lënde. (Afërsisht 6MB)</string>
|
||||
<string name="wait_for_load">Lëndë Ende Në Ngarkim</string>
|
||||
<string name="help_2">Ç’kryen Kiwix-i?</string>
|
||||
<string name="help_3">Kiwix është një lexues lënde pa internet. Funksionon shumë ngjashëm me një shfletues, por në vend të përdorimit\n“online” të faqeve web, lëndën e lexon që nga një kartelë në formatin ZIM.</string>
|
||||
<string name="help_4">Edhe pse fillimisht qe konceptuar për të lejuar përdorimin pa internet të Wikipedia-s, mund të përdoret edhe për lëndë tjetër.</string>
|
||||
@ -134,8 +133,6 @@
|
||||
<string name="time_day">ditë</string>
|
||||
<string name="time_minute">min.</string>
|
||||
<string name="time_left">edhe</string>
|
||||
<string name="pref_bottomtoolbar">Aktivizo panel të poshtëm</string>
|
||||
<string name="pref_bottomtoolbar_summary">Shfaq në fund të ekranit një panel me butona veprimesh të shpejta</string>
|
||||
<string name="pref_autonightmode_summary">Mënyrat ditë dhe mbrëmje këmbeji automatikisht.</string>
|
||||
<string name="pref_autonightmode">Mënyrë mbrëmje automatike</string>
|
||||
<string name="pref_external_link_popup_title">Sinjalizo kur hyhet në lidhje të jashtme</string>
|
||||
|
@ -92,7 +92,6 @@
|
||||
<string name="zim_novid">Inga videor</string>
|
||||
<string name="no_network_connection">Ingen nätverksanslutning</string>
|
||||
<string name="get_library_over_network">Använd nätverket för att ladda ned innehållslistan. (Uppskattningsvis 6MB)</string>
|
||||
<string name="wait_for_load">Läser fortfarande in innehåll</string>
|
||||
<string name="help_2">Vad gör Kiwix?</string>
|
||||
<string name="help_3">Kiwix är en innehållsläsare för offlineanvändning. Den fungerar som en webbläsare, men istället för att komma åt webbsidor online läser den innehåll från en fil i ZIM-format.</string>
|
||||
<string name="help_4">Även om Kiwix ursprungligen skapades för att tillhandahålla Wikipedia offline läser den även annat innehåll.</string>
|
||||
@ -133,8 +132,6 @@
|
||||
<string name="pref_wifi_only">Ladda ned endast via WiFi</string>
|
||||
<string name="time_day">dag</string>
|
||||
<string name="time_left">kvar</string>
|
||||
<string name="pref_bottomtoolbar">Aktivera verktygsfält i nederkant</string>
|
||||
<string name="pref_bottomtoolbar_summary">Visa ett verktygsfält med snabbåtgärder längst ned på skärmen</string>
|
||||
<string name="pref_autonightmode_summary">Växla automatiskt mellan dag- och nattläge.</string>
|
||||
<string name="pref_autonightmode">Automatiskt nattläge</string>
|
||||
<string name="pref_external_link_popup_title">Varna vid externa länkar</string>
|
||||
|
@ -94,7 +94,6 @@
|
||||
<string name="zim_novid">Video yok</string>
|
||||
<string name="no_network_connection">Ağ bağlantısı yok</string>
|
||||
<string name="get_library_over_network">İçerik listesini indirmek için ağı kullan. (Yaklaşık 6MB)</string>
|
||||
<string name="wait_for_load">İçerik hâlâ yükleniyor</string>
|
||||
<string name="help_2">Kiwix ne yapar?</string>
|
||||
<string name="help_3">Kiwix bir çevrimdışı içerik okuyucusudur. Aynı bir ağ tarayıcısı gibi çalışır ama web sayfalarına çevrimiçi erişmek yerine içeriği ZIM formatındaki bir dosyadan okur.</string>
|
||||
<string name="help_4">Kiwix orijinalde Vikipedi\'yi çevrimdışı olarak okuması için tasarlanmış olsa da diğer içerikleri de okuyabilir.</string>
|
||||
@ -138,8 +137,6 @@
|
||||
<string name="time_minute">dk</string>
|
||||
<string name="time_second">sn</string>
|
||||
<string name="time_left">kaldı</string>
|
||||
<string name="pref_bottomtoolbar">Alt araç çubuğunu etkinleştir</string>
|
||||
<string name="pref_bottomtoolbar_summary">Ekranın altındaki hızlı hareketlerle bir araç çubuğu sergileyin</string>
|
||||
<string name="pref_autonightmode_summary">Gece ve gündüz modu arasında otomatik geçiş.</string>
|
||||
<string name="pref_autonightmode">Otomatik gece modu</string>
|
||||
<string name="pref_external_link_popup_title">Harici bağlantı girilirken uyarı ver</string>
|
||||
|
@ -94,7 +94,6 @@
|
||||
<string name="zim_novid">Немає відео</string>
|
||||
<string name="no_network_connection">Відсутнє з\'єднання з мережею</string>
|
||||
<string name="get_library_over_network">Скористатись мережею, щоб завантажити список контенту (приблизно 6MБ)</string>
|
||||
<string name="wait_for_load">Контент все ще завантажується</string>
|
||||
<string name="help_2">Що робить Kiwix?</string>
|
||||
<string name="help_3">Kiwix — це офлайновий читач контенту. Він багато в чому поводиться як браузер, але замість доступу до онлайнових веб-сторінок, він зчитує контент із файлу у форматі ZIM.</string>
|
||||
<string name="help_4">Тоді як початково Kiwix було розроблено для забезпечення доступу до Вікіпедії в режимі офлайн, він також зчитує інші типи контенту.</string>
|
||||
@ -138,8 +137,6 @@
|
||||
<string name="time_minute">хв</string>
|
||||
<string name="time_second">с</string>
|
||||
<string name="time_left">залишилось</string>
|
||||
<string name="pref_bottomtoolbar">Увімкнути нижню панель</string>
|
||||
<string name="pref_bottomtoolbar_summary">Відображати знизу панель з швидкими діями</string>
|
||||
<string name="pref_autonightmode_summary">Автоматично перемикатися між денним та нічним режимами.</string>
|
||||
<string name="pref_autonightmode">Автоматичний нічний режим</string>
|
||||
<string name="pref_external_link_popup_title">Попереджувати, коли введено зовнішнє посилання</string>
|
||||
|
@ -93,7 +93,6 @@
|
||||
<string name="zim_novid">Không có video</string>
|
||||
<string name="no_network_connection">Không có kết nối mạng</string>
|
||||
<string name="get_library_over_network">Dùng mạng để tải về danh sách nội dung (khoảng 6MB)</string>
|
||||
<string name="wait_for_load">Vẫn đang tải nội dung</string>
|
||||
<string name="help_2">Kiwix dùng để làm gì?</string>
|
||||
<string name="help_3">Kiwix là một chương trình đọc nội dung ngoại tuyến. Kiwix hoạt động như trình duyệt, tuy nhiên, thay vì truy cập các trang web trực tuyến thì Kiwix lại đọc nội dung từ các tập tin định dạng ZIM.</string>
|
||||
<string name="help_4">Mặc dù Kiwix lúc ban đầu được tạo ra để giúp bạn truy cập Wikipedia ngoại tuyến, chương trình cũng có thể đọc các nội dung khác nữa.</string>
|
||||
@ -135,8 +134,6 @@
|
||||
<string name="time_minute">phút</string>
|
||||
<string name="time_second">giây</string>
|
||||
<string name="time_left">còn lại</string>
|
||||
<string name="pref_bottomtoolbar">Bật thanh tác vụ bên dưới</string>
|
||||
<string name="pref_bottomtoolbar_summary">Hiển thị một thanh tác vụ nhanh nằm ở dưới màn hình</string>
|
||||
<string name="pref_autonightmode_summary">Tự động chuyển qua lại giữa chế độ đêm và ngày.</string>
|
||||
<string name="pref_autonightmode">Chế độ đêm tự động</string>
|
||||
<string name="pref_external_link_popup_title">Cảnh báo khi truy cập đường dẫn ngoài</string>
|
||||
|
@ -99,7 +99,6 @@
|
||||
<string name="zim_novid">No Videos</string>
|
||||
<string name="no_network_connection">No network connection</string>
|
||||
<string name="get_library_over_network">Use network to download content list. (Approximately 6MB)</string>
|
||||
<string name="wait_for_load">Content Still Loading</string>
|
||||
<string name="help_2">What does Kiwix do?</string>
|
||||
<string name="help_3">Kiwix is an offline content reader. It acts very much like a browser but instead of accessing online web pages, it reads content from a file in ZIM format.</string>
|
||||
<string name="help_4">While Kiwix has been originally designed to provide Wikipedia offline, it also reads other contents.</string>
|
||||
@ -146,12 +145,8 @@
|
||||
<string name="time_left">left</string>
|
||||
<string name="time_today">Today</string>
|
||||
<string name="time_yesterday">Yesterday</string>
|
||||
<string name="pref_bottomtoolbar">Enable bottom toolbar</string>
|
||||
<string name="pref_bottomtoolbar_summary">Display a toolbar with quick actions at the bottom of the screen</string>
|
||||
<string name="pref_autonightmode_summary">Automatically switch between day and night mode.</string>
|
||||
<string name="pref_autonightmode">Automated night mode</string>
|
||||
<string name="language_count">(%1$d)</string>
|
||||
<string name="language_localized">(%s)</string>
|
||||
<string name="pref_external_link_popup_title">Warn when entering external links</string>
|
||||
<string name="pref_external_link_popup_summary">Display popup to warn about additional costs or not working in offline links.</string>
|
||||
<string name="external_link_popup_dialog_title">Entering External Link</string>
|
||||
@ -190,7 +185,6 @@
|
||||
<string name="table_of_contents">Table of contents</string>
|
||||
<string name="select_languages">Select languages</string>
|
||||
<string name="save_languages">Save languages</string>
|
||||
<string name="languages_saved">Languages saved</string>
|
||||
<string name="send_feedback">Send feedback</string>
|
||||
<string name="expand">Expand</string>
|
||||
<string name="history">History</string>
|
||||
@ -261,6 +255,10 @@
|
||||
<string name="notes_deletion_none_found">No notes found for deletion</string>
|
||||
<string name="notes_deletion_successful">Entire notes folder deleted</string>
|
||||
<string name="notes_deletion_unsuccessful">Some files not deleted</string>
|
||||
<plurals name="books_count">
|
||||
<item quantity="one">%d book</item>
|
||||
<item quantity="other">%d books</item>
|
||||
</plurals>
|
||||
<string name="discovery_initiated">Discovery initiated</string>
|
||||
<string name="discovery_failed">Discovery failed</string>
|
||||
<string name="severe_loss_error">Severe error! Try Disable/Re-enable WiFi P2P</string>
|
||||
|
@ -6,4 +6,6 @@
|
||||
<item>large</item>
|
||||
</string-array>
|
||||
|
||||
</resources>
|
||||
<string name="slash">/</string>
|
||||
<string name="plus">+</string>
|
||||
</resources>
|
||||
|
@ -27,12 +27,6 @@
|
||||
android:summary="@string/pref_hidetoolbar_summary"
|
||||
android:title="@string/pref_hidetoolbar"/>
|
||||
|
||||
<org.kiwix.kiwixmobile.settings.CustomSwitchPreference
|
||||
android:defaultValue="false"
|
||||
android:key="pref_bottomtoolbar"
|
||||
android:summary="@string/pref_bottomtoolbar_summary"
|
||||
android:title="@string/pref_bottomtoolbar"/>
|
||||
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
|
@ -21,7 +21,9 @@ import org.kiwix.kiwixmobile.downloader.model.DownloadModel
|
||||
import org.kiwix.kiwixmobile.downloader.model.DownloadState
|
||||
import org.kiwix.kiwixmobile.downloader.model.DownloadState.Pending
|
||||
import org.kiwix.kiwixmobile.downloader.model.DownloadStatus
|
||||
import org.kiwix.kiwixmobile.language.adapter.LanguageListItem.LanguageItem
|
||||
import org.kiwix.kiwixmobile.library.entity.LibraryNetworkEntity.Book
|
||||
import org.kiwix.kiwixmobile.zim_manager.Language
|
||||
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.adapter.BooksOnDiskListItem.BookOnDisk
|
||||
import java.io.File
|
||||
|
||||
@ -76,3 +78,19 @@ fun downloadModel(
|
||||
downloadId: Long = 1L,
|
||||
book: Book = book()
|
||||
) = DownloadModel(databaseId, downloadId, book)
|
||||
|
||||
fun language(
|
||||
id: Long = 0,
|
||||
isActive: Boolean = false,
|
||||
occurencesOfLanguage: Int = 0,
|
||||
language: String = "",
|
||||
languageLocalized: String = "",
|
||||
languageCode: String = "",
|
||||
languageCodeISO2: String = ""
|
||||
) = Language(
|
||||
id, isActive, occurencesOfLanguage, language, languageLocalized, languageCode,
|
||||
languageCodeISO2
|
||||
)
|
||||
|
||||
fun languageItem(language: Language = language()) =
|
||||
LanguageItem(language)
|
||||
|
@ -0,0 +1,72 @@
|
||||
package org.kiwix.kiwixmobile.language.adapter
|
||||
|
||||
import android.view.ViewGroup
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import io.mockk.mockkStatic
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.jupiter.api.Nested
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.kiwix.kiwixmobile.R
|
||||
import org.kiwix.kiwixmobile.extensions.inflate
|
||||
import org.kiwix.kiwixmobile.language.adapter.LanguageDelegate.HeaderDelegate
|
||||
import org.kiwix.kiwixmobile.language.adapter.LanguageDelegate.LanguageItemDelegate
|
||||
import org.kiwix.kiwixmobile.language.adapter.LanguageListItem.HeaderItem
|
||||
import org.kiwix.kiwixmobile.language.adapter.LanguageListItem.LanguageItem
|
||||
import org.kiwix.kiwixmobile.language.adapter.LanguageListViewHolder.HeaderViewHolder
|
||||
import org.kiwix.kiwixmobile.language.adapter.LanguageListViewHolder.LanguageViewHolder
|
||||
|
||||
/*
|
||||
* Kiwix Android
|
||||
* Copyright (C) 2018 Kiwix <android.kiwix.org>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
class LanguageDelegateTest {
|
||||
@Nested
|
||||
inner class HeaderDelegateTests {
|
||||
@Test
|
||||
fun `class is header item`() {
|
||||
assertThat(HeaderDelegate().itemClass).isEqualTo(HeaderItem::class.java)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `creates HeaderViewHolder`() {
|
||||
val parent = mockk<ViewGroup>()
|
||||
mockkStatic("org.kiwix.kiwixmobile.extensions.ViewGroupExtensionsKt")
|
||||
every { parent.inflate(R.layout.header_date, false) } returns mockk(relaxed = true)
|
||||
assertThat(HeaderDelegate().createViewHolder(parent))
|
||||
.isInstanceOf(HeaderViewHolder::class.java)
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class LanguageItemDelegateTests {
|
||||
@Test
|
||||
fun `class is lanuguage item`() {
|
||||
assertThat(LanguageItemDelegate({}).itemClass).isEqualTo(LanguageItem::class.java)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `creates HeaderViewHolder`() {
|
||||
val parent = mockk<ViewGroup>()
|
||||
mockkStatic("org.kiwix.kiwixmobile.extensions.ViewGroupExtensionsKt")
|
||||
every { parent.inflate(R.layout.item_language, false) } returns mockk(relaxed = true)
|
||||
val clickAction = mockk<(LanguageItem) -> Unit>()
|
||||
assertThat(LanguageItemDelegate(clickAction).createViewHolder(parent))
|
||||
.isInstanceOf(LanguageViewHolder::class.java)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,157 @@
|
||||
/*
|
||||
* Kiwix Android
|
||||
* Copyright (C) 2018 Kiwix <android.kiwix.org>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.kiwix.kiwixmobile.language.viewmodel
|
||||
|
||||
import com.jraska.livedata.test
|
||||
import io.mockk.clearAllMocks
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import io.reactivex.processors.PublishProcessor
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import org.junit.jupiter.api.AfterAll
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.extension.ExtendWith
|
||||
import org.kiwix.kiwixmobile.InstantExecutorExtension
|
||||
import org.kiwix.kiwixmobile.database.newdb.dao.NewLanguagesDao
|
||||
import org.kiwix.kiwixmobile.language
|
||||
import org.kiwix.kiwixmobile.language.viewmodel.Action.Filter
|
||||
import org.kiwix.kiwixmobile.language.viewmodel.Action.SaveAll
|
||||
import org.kiwix.kiwixmobile.language.viewmodel.Action.Select
|
||||
import org.kiwix.kiwixmobile.language.viewmodel.Action.UpdateLanguages
|
||||
import org.kiwix.kiwixmobile.language.viewmodel.State.Content
|
||||
import org.kiwix.kiwixmobile.language.viewmodel.State.Loading
|
||||
import org.kiwix.kiwixmobile.language.viewmodel.State.Saving
|
||||
import org.kiwix.kiwixmobile.languageItem
|
||||
import org.kiwix.kiwixmobile.resetSchedulers
|
||||
import org.kiwix.kiwixmobile.setScheduler
|
||||
import org.kiwix.kiwixmobile.zim_manager.Language
|
||||
|
||||
@ExtendWith(InstantExecutorExtension::class)
|
||||
class LanguageViewModelTest {
|
||||
init {
|
||||
setScheduler(Schedulers.trampoline())
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
fun teardown() {
|
||||
resetSchedulers()
|
||||
}
|
||||
|
||||
private val newLanguagesDao: NewLanguagesDao = mockk()
|
||||
private lateinit var languageViewModel: LanguageViewModel
|
||||
|
||||
private val languages: PublishProcessor<List<Language>> = PublishProcessor.create()
|
||||
|
||||
@BeforeEach
|
||||
fun init() {
|
||||
clearAllMocks()
|
||||
every { newLanguagesDao.languages() } returns languages
|
||||
languageViewModel = LanguageViewModel(newLanguagesDao)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `initial state is Loading`() {
|
||||
languageViewModel.state.test()
|
||||
.assertValueHistory(Loading)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `an empty languages emission does not send update action`() {
|
||||
languageViewModel.actions.test()
|
||||
.also {
|
||||
languages.offer(listOf())
|
||||
}
|
||||
.assertValues()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `a languages emission sends update action`() {
|
||||
val expectedList = listOf(language())
|
||||
languageViewModel.actions.test()
|
||||
.also {
|
||||
languages.offer(expectedList)
|
||||
}
|
||||
.assertValues(UpdateLanguages(expectedList))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `UpdateLanguages Action changes state to Content when Loading`() {
|
||||
languageViewModel.actions.offer(UpdateLanguages(listOf()))
|
||||
languageViewModel.state.test()
|
||||
.assertValueHistory(Content(listOf()))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `UpdateLanguages Action has no effect on other states`() {
|
||||
languageViewModel.actions.offer(UpdateLanguages(listOf()))
|
||||
languageViewModel.actions.offer(UpdateLanguages(listOf()))
|
||||
languageViewModel.state.test()
|
||||
.assertValueHistory(Content(listOf()))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Filter Action updates Content state `() {
|
||||
languageViewModel.actions.offer(UpdateLanguages(listOf()))
|
||||
languageViewModel.actions.offer(Filter("filter"))
|
||||
languageViewModel.state.test()
|
||||
.assertValueHistory(Content(listOf(), filter = "filter"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Filter Action has no effect on other states`() {
|
||||
languageViewModel.actions.offer(Filter(""))
|
||||
languageViewModel.state.test()
|
||||
.assertValueHistory(Loading)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Select Action updates Content state`() {
|
||||
languageViewModel.actions.offer(UpdateLanguages(listOf(language())))
|
||||
languageViewModel.actions.offer(Select(languageItem()))
|
||||
languageViewModel.state.test()
|
||||
.assertValueHistory(Content(listOf(language(isActive = true))))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Select Action has no effect on other states`() {
|
||||
languageViewModel.actions.offer(Select(languageItem()))
|
||||
languageViewModel.state.test()
|
||||
.assertValueHistory(Loading)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `SaveAll changes Content to Saving with SideEffect SaveLanguagesAndFinish`() {
|
||||
languageViewModel.actions.offer(UpdateLanguages(listOf()))
|
||||
languageViewModel.effects.test()
|
||||
.also {
|
||||
languageViewModel.actions.offer(SaveAll)
|
||||
}
|
||||
.assertValues(SaveLanguagesAndFinish(listOf(), newLanguagesDao))
|
||||
languageViewModel.state.test()
|
||||
.assertValueHistory(Saving)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `SaveAll has no effect on other states`() {
|
||||
languageViewModel.actions.offer(SaveAll)
|
||||
languageViewModel.state.test()
|
||||
.assertValueHistory(Loading)
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user