Merge pull request #1185 from kiwix/macgills/2.5-kotlin

2.5
This commit is contained in:
macgills 2019-06-14 14:56:26 +01:00 committed by GitHub
commit 5fd479c9fb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
173 changed files with 5867 additions and 3311 deletions

3
.gitignore vendored
View File

@ -53,4 +53,5 @@ glassify
.project
.classpath
.vscode
.vscode
captures/

View File

@ -4,29 +4,90 @@ jdk: oraclejdk8
sudo: required
env:
global:
- ANDROID_TARGET=android-22
- ANDROID_ABI=armeabi-v7a
before_install:
- openssl aes-256-cbc -K $encrypted_82adfa9c3806_key -iv $encrypted_82adfa9c3806_iv -in secrets.tar.enc -out secrets.tar -d
- tar xvf secrets.tar
install:
- pip install --user 'requests[security]'
- wget -r -nH -nd -np -R index.html* robots.txt* http://download.kiwix.org/dev/android/api/licenses/ -e robots=off -P $ANDROID_HOME/licenses || true
addons:
apt:
packages:
- lynx
before_cache:
- rm -f $HOME/.gradle/caches/modules-2/modules-2.lock
- rm -fr $HOME/.gradle/caches/*/plugin-resolution/
cache:
directories:
- $HOME/.gradle/caches/
- $HOME/.gradle/wrapper/
- $HOME/.android/build-cache
- "$HOME/.gradle/caches/"
- "$HOME/.gradle/wrapper/"
- "$HOME/.android/build-cache"
android:
components:
- tools
- platform-tools
- tools
- build-tools-27.0.3
- build-tools-28.0.3
- android-27
- extra-android-m2repository
- $ANDROID_TARGET
- sys-img-${ANDROID_ABI}-${ANDROID_TARGET}
licenses:
- '.+'
- ".+"
script: ./gradlew assembleKiwixRelease testdroidUploadKiwix && ./testdroid.py
script:
- ./gradlew lintKiwixDebug jacocoTestKiwixDebugUnitTestReport
- echo no | android create avd --force -n test -t $ANDROID_TARGET --abi $ANDROID_ABI -c 100M
- emulator -avd test -no-window &
- android-wait-for-emulator
- adb shell setprop dalvik.vm.dexopt-flags v=n,o=v
- adb shell input keyevent 82 & # unlock screen by pressing menu button
- adb -e logcat *:D > logcat.log &
- ./gradlew createKiwixDebugCoverageReport
after_success:
- bash <(curl -s https://codecov.io/bash)
- ./gradlew testdroidUploadKiwixDebug
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;
- echo " LOGCAT "; echo "========"; cat logcat.log; pkill -KILL -f adb
before_deploy:
- export APP_CHANGELOG=$(cat app/src/kiwix/play/release-notes/en-US/default.txt)
deploy:
#publish on github releases
- provider: releases
api_key: "$GITHUB_TOKEN"
file: app/build/outputs/apk/kiwix/release/*
file_glob: true
skip_cleanup: true
overwrite: true
body: "$APP_CHANGELOG"
draft: true
on:
tags: true
condition: $TRAVIS_BRANCH =~ ^release|master
#publish on play store
- provider: script
skip_cleanup: true
script: ./gradlew publishKiwixRelease
on:
tags: true
condition: $TRAVIS_BRANCH =~ ^release|master

View File

@ -1,3 +1,7 @@
2.5
NEW: Downloads are now using the DownloadManager
NEW: Downloads/Device/Library completely rewritten
2.4
FIX: External SD card problems
FIX: Some UI translation

View File

@ -40,4 +40,47 @@ Our process for accepting changes operates by [Pull Request (PR)](https://help.g
1. Once you have integrated comments, or waited for feedback, a Lieutenant should merge your changes in!
### Building
The default build is `debug`, with this variant you can use a debugger while developing. To install the application click the `run` button in Android Studio with the `app` configuration selected while you have a device connected. All other build types but `release` can be ignored, the `release` build is what gets uploaded to the Google Play store and can be built locally with the dummy credentials/keystore provided.
### Testing
Unit tests are located in `app/src/test` and to run them locally you
can use the gradle command:
$ gradlew testKiwixDebugUnitTest
or the abbreviated:
$ gradlew tKDUT
Automated tests that require a connected device (UI related tests) are located in `app/src/androidTest` & `app/src/androidTestKiwix`, to run them locally you can use the gradle command:
$ gradlew connectedKiwixDebugAndroidTest
or the abbreviated:
$ gradlew cKDAT
All local test results can be seen under `app/build/reports/`
### Code coverage
To generate coverage reports for your unit tests run:
$ gradlew jacocoTestKiwixDebugUnitTest
To generate coverage reports for your automated tests run:
$ gradlew createKiwixDebugCoverageReport
Code coverage results can be seen under `app/build/reports/`
### Continous Integration
All PRs will have all these tests run and a combined coverage report will be attached, if coverage is to go down the PR will be marked failed. On Travis CI the automated tests are run on an emulator. To
learn more about the commands run on the CI please refer to [.travis.yml](https://github.com/kiwix/kiwix-android/blob/master/.travis.yml)
_These guidelines are based on [Tools for Government Data Archiving](https://github.com/edgi-govdata-archiving/overview/blob/master/CONTRIBUTING.md)'s._

View File

@ -7,6 +7,7 @@ Kiwix is an offline reader for Web content. One of its main purposes is to make
[![Build Status](https://travis-ci.org/kiwix/kiwix-android.svg?branch=master)](https://travis-ci.org/kiwix/kiwix-android)
[![IRC Web](https://img.shields.io/badge/chat-on%20freenode-brightgreen.svg)](http://chat.kiwix.org)
[![codecov](https://codecov.io/gh/kiwix/kiwix-android/branch/master/graph/badge.svg)](https://codecov.io/gh/kiwix/kiwix-android)
---
## Build Instructions
@ -20,16 +21,15 @@ We utilize different build variants (flavours) to build various different versio
## Libraries Used
- [Dagger 2](https://github.com/google/dagger) - A fast dependency injector for Android and Java
- [SquiDb](https://github.com/yahoo/squidb) - SquiDB is a SQLite database library for Android and iOS
- [Retrofit](http://square.github.io/retrofit/) - Retrofit turns your REST API into a Java interface
- [OkHttp](https://github.com/square/okhttp) - An HTTP+SPDY client for Android and Java applications
- [Butterknife](http://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
- [Guava](https://github.com/google/guava) - Collections, caching, primitives support, concurrency libraries, common annotations, string processing, I/O, and so forth.
- [Apache](https://github.com/apache/commons-io) - The Apache Commons IO library contains utility classes, stream implementations, file filters, file comparators, endian transformation classes, and much more.
- [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
## Contributing

View File

@ -1,3 +1,4 @@
import com.android.build.OutputFile
import groovy.json.JsonSlurper
buildscript {
@ -8,15 +9,23 @@ buildscript {
}
dependencies {
classpath 'com.android.tools.build:gradle:3.1.0'
classpath 'com.testdroid:gradle:1.5.0'
classpath 'org.apache.httpcomponents:httpclient-android:4.3.3'
}
}
apply plugin: 'com.android.application'
apply plugin: 'checkstyle'
plugins {
id 'com.android.application'
id 'kotlin-android'
id 'kotlin-android-extensions'
id 'kotlin-kapt'
id 'checkstyle'
id 'io.objectbox'
id 'com.github.triplet.play' version '2.2.1'
}
apply plugin: 'testdroid'
apply plugin: 'jacoco-android'
repositories {
mavenCentral()
@ -27,13 +36,12 @@ repositories {
google()
}
String[] archs = ['arm64-v8a', 'armeabi', 'mips', 'mips64', 'x86', 'x86_64']
String[] archs = ['arm64-v8a', 'armeabi-v7a', 'x86', 'x86_64']
dependencies {
// Get kiwixlib online if it is not populated locally
if (file("../kiwixlib/src/main").list().length == 1) {
implementation 'org.kiwix.kiwixlib:kiwixlib:1.0.11'
implementation 'org.kiwix.kiwixlib:kiwixlib:1.0.12'
} else {
implementation project(':kiwixlib')
archs = file("../kiwixlib/src/main/jniLibs").list()
@ -48,9 +56,6 @@ dependencies {
implementation "com.android.support:support-v4:$supportLibraryVersion"
implementation "com.android.support:design:$supportLibraryVersion"
implementation "com.android.support:cardview-v7:$supportLibraryVersion"
implementation 'com.android.support:multidex:1.0.2'
compile 'com.android.support.constraint:constraint-layout:1.0.2'
androidTestImplementation 'com.android.support.test.uiautomator:uiautomator-v18:2.1.3'
@ -68,7 +73,7 @@ dependencies {
exclude group: 'com.android.support', module: 'recyclerview-v7'
}
androidTestCompile('com.schibsted.spain:barista:2.4.0') {
androidTestImplementation('com.schibsted.spain:barista:2.4.0') {
exclude group: 'com.android.support'
}
@ -76,25 +81,19 @@ dependencies {
androidTestImplementation 'com.android.support.test:runner:1.0.1'
androidTestImplementation 'com.android.support.test:rules:1.0.1'
// Guava
implementation group: 'com.google.guava', name: 'guava', version: '21.0'
// Dagger
compileOnly "javax.annotation:javax.annotation-api:$javaxAnnotationVersion"
androidTestCompileOnly "javax.annotation:javax.annotation-api:$javaxAnnotationVersion"
implementation "com.google.dagger:dagger:$daggerVersion"
implementation "com.google.dagger:dagger-android:$daggerVersion"
annotationProcessor "com.google.dagger:dagger-compiler:$daggerVersion"
annotationProcessor "com.google.dagger:dagger-android-processor:$daggerVersion"
androidTestAnnotationProcessor "com.google.dagger:dagger-compiler:$daggerVersion"
kapt "com.google.dagger:dagger-compiler:$daggerVersion"
kapt "com.google.dagger:dagger-android-processor:$daggerVersion"
kaptAndroidTest "com.google.dagger:dagger-compiler:$daggerVersion"
// SquiDB
implementation 'com.yahoo.squidb:squidb:2.0.0'
implementation 'com.yahoo.squidb:squidb-annotations:2.0.0'
annotationProcessor 'com.yahoo.squidb:squidb-processor:2.0.0'
// Apache
implementation 'commons-io:commons-io:2.5'
kapt 'com.yahoo.squidb:squidb-processor:2.0.0'
// Square
implementation "com.squareup.okhttp3:okhttp:$okHttpVersion"
@ -110,22 +109,20 @@ dependencies {
// Butterknife
implementation 'com.jakewharton:butterknife:8.0.1'
annotationProcessor 'com.jakewharton:butterknife-compiler:8.0.1'
kapt 'com.jakewharton:butterknife-compiler:8.0.1'
// RxJava
implementation "io.reactivex.rxjava2:rxandroid:$rxAndroidVersion"
implementation "io.reactivex.rxjava2:rxjava:$rxJavaVersion"
// JUnit
testImplementation 'junit:junit:4.12'
androidTestImplementation 'junit:junit:4.12'
// Mockito
testImplementation "org.mockito:mockito-core:2.7.22"
androidTestImplementation "org.mockito:mockito-android:2.7.22"
androidTestImplementation "org.mockito:mockito-android:2.24.5"
// Leak canary
implementation 'com.squareup.leakcanary:leakcanary-android-no-op:1.5.4'
implementation 'com.squareup.leakcanary:leakcanary-android-no-op:1.6.3'
// Only enable leak canary in debug builds
configurations.all { config ->
if (config.name.contains('debug') || config.name.contains("Debug")) {
@ -138,6 +135,20 @@ dependencies {
}
}
}
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
implementation "android.arch.lifecycle:extensions:1.1.1"
implementation "io.objectbox:objectbox-kotlin:$objectboxVersion"
implementation "io.objectbox:objectbox-rxjava:$objectboxVersion"
testImplementation "org.junit.jupiter:junit-jupiter:5.4.2"
testImplementation "io.mockk:mockk:1.9"
testImplementation "org.assertj:assertj-core:3.11.1"
//update this with androidx
testImplementation 'com.jraska.livedata:testing-ktx:0.2.1'
testImplementation 'android.arch.core:core-testing:1.1.1'
}
// Set custom app import directory
@ -148,28 +159,58 @@ if (project.hasProperty('customDir')) {
}
// Set up flavours for each custom app in the directory
if(custom.listFiles()) {
if (custom.listFiles()) {
custom.eachFile() { file ->
def fileName = file.getName()
if (fileName.startsWith(".") || fileName.contains("test") || fileName == "main" || fileName.contains("Test")) {
if (fileName.startsWith(".") ||
fileName.contains("test") ||
fileName == "main" ||
fileName.contains("Test")) {
return
}
map.put(fileName, file.getAbsolutePath())
}
}
jacoco {
toolVersion = "0.8.3"
}
tasks.withType(Test) {
jacoco.includeNoLocationClasses = true
}
def branchName = System.getenv('TRAVIS_PULL_REQUEST') ?: "false" == "false"
? System.getenv('TRAVIS_BRANCH') ?: "local"
: System.getenv('TRAVIS_PULL_REQUEST_BRANCH')
def buildNumber = System.getenv('TRAVIS_BUILD_NUMBER') ?: "dev"
ext {
versionMajor = 2
versionMinor = 5
versionPatch = 0
}
private String generateVersionName() {
"${ext.versionMajor}.${ext.versionMinor}.${ext.versionPatch}"
}
private Integer generateVersionCode() {
200000 + (ext.versionMajor * 10000) + (ext.versionMinor * 100) + (ext.versionPatch)
}
android {
compileSdkVersion 27
defaultConfig {
minSdkVersion 14
minSdkVersion 15
targetSdkVersion 27
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
// See https://github.com/linkedin/dexmaker/issues/65 for why we need the following line.
testInstrumentationRunnerArguments.notClass = 'com.android.dex.DexIndexOverflowException'
multiDexEnabled true
vectorDrawables.useSupportLibrary = true
archivesBaseName = "${branchName.replace('/', '-')}-$buildNumber"
}
aaptOptions {
@ -191,19 +232,36 @@ android {
warning 'InvalidPackage'
warning 'StringFormatInvalid'
}
testOptions {
unitTests.returnDefaultValues = true
unitTests.all {
useJUnitPlatform()
testLogging {
events "passed", "skipped", "failed", "standardOut", "standardError"
outputs.upToDateWhen { false }
showStandardStreams = true
}
}
}
flavorDimensions "default"
signingConfigs {
release {
storeFile file("../kiwix-android.keystore")
storePassword System.getenv("KEY_STORE_PASSWORD") ?: "000000"
keyAlias System.getenv("KEY_ALIAS") ?: "keystore"
keyPassword System.getenv("KEY_PASSWORD") ?: "000000"
}
}
buildTypes {
// Main build type for debugging
debug {
buildConfigField "String", "KIWIX_DOWNLOAD_URL", "\"http://download.kiwix.org/\""
buildConfigField "boolean", "KIWIX_ERROR_ACTIVITY", "false"
// True breaks local variables being shown in breakpoints
testCoverageEnabled false
// Needed for instrumentation tests on Pre 5.0
multiDexKeepProguard file('multidex-instrumentation-config.pro')
testCoverageEnabled true
}
mock_network {
@ -218,19 +276,12 @@ android {
matchingFallbacks = ['debug', 'release']
}
// Used to assess code coverage
coverage {
initWith debug
testCoverageEnabled true
matchingFallbacks = ['debug', 'release']
}
// Release Type
release {
signingConfig signingConfigs.release
buildConfigField "String", "KIWIX_DOWNLOAD_URL", "\"http://download.kiwix.org/\""
buildConfigField "boolean", "KIWIX_ERROR_ACTIVITY", "false"
}
}
productFlavors {
@ -250,18 +301,22 @@ android {
def version_code = project.property('version_code')
versionCode version_code.toInteger()
} else {
versionCode 55
versionCode generateVersionCode()
}
if (project.hasProperty('version_name')) {
versionName project.property('version_name')
} else {
versionName "2.4"
versionName generateVersionName()
}
}
// Custom apps built from a json file, zim file and icon set
map.each { name, directory ->
"$name" {
println "Configuring $name"
if (name == "kiwix") {
return
}
if (file(directory + "/build.gradle").exists()) {
apply from: directory + "/build.gradle"
}
@ -279,15 +334,15 @@ android {
sourceFile = file(directory + "/" + parsedJson.zim_file)
}
if (parsedJson.embed_zim) {
// Place content in each lib directory for embeded zims
// Place content in each lib directory for embeded zims
for (String archName : archs) {
copy {
from sourceFile
into file(directory + "/jniLibs/" + archName)
rename { String filename -> "libcontent.so" }
}
}
parsedJson.zim_file = "libcontent.so"
copy {
from sourceFile
into file(directory + "/jniLibs/" + archName)
rename { String filename -> "libcontent.so" }
}
}
parsedJson.zim_file = "libcontent.so"
}
// Set custom config from json
applicationId "$parsedJson.package"
@ -323,7 +378,7 @@ android {
buildConfigField "int", "CONTENT_VERSION_CODE", "$content_version_code"
} else if (parsedJson.content_version_code != null) {
buildConfigField "int", "CONTENT_VERSION_CODE", "$parsedJson.content_version_code"
} else if (project.hasProperty('version_code')) {
} else if (project.hasProperty('version_code')) {
def version_code = project.property('version_code')
buildConfigField "int", "CONTENT_VERSION_CODE", "$version_code"
} else if (parsedJson.version_code != null) {
@ -366,10 +421,35 @@ android {
}
}
*/
androidExtensions {
experimental = true
}
def abiCodes = ['arm64-v8a': 1, 'x86': 2, 'x86_64': 3, 'armeabi-v7a': 4]
splits {
abi {
enable true
reset()
include "x86", "x86_64", 'armeabi-v7a', "arm64-v8a"
universalApk true
}
}
applicationVariants.all { variant ->
variant.outputs.each { output ->
def baseAbiVersionCode = abiCodes.get(output.getFilter(OutputFile.ABI))
if (baseAbiVersionCode != null) {
output.versionCodeOverride = baseAbiVersionCode * 1000000 + variant.versionCode
}
}
}
}
// Testdroid deployment configuration
def buildNumber = System.getenv('TRAVIS_BUILD_NUMBER')
play {
enabled = branchName == "master" || branchName == "release"
serviceAccountCredentials = file("../google.json")
track = "alpha"
resolutionStrategy = "fail"
}
def findJar(prefix) {
configurations.runtime.filter { it.name.startsWith(prefix) }
@ -385,4 +465,4 @@ testdroid {
fullRunConfig {
instrumentationRunner = "android.support.test.runner.AndroidJUnitRunner"
}
}
}

View File

@ -0,0 +1,203 @@
{
"_note1": "KEEP THIS FILE! Check it into a version control system (VCS) like git.",
"_note2": "ObjectBox manages crucial IDs for your object model. See docs for details.",
"_note3": "If you have VCS merge conflicts, you must resolve them according to ObjectBox docs.",
"entities": [
{
"id": "1:7257718270326155947",
"lastPropertyId": "17:8085320504542486236",
"name": "DownloadEntity",
"properties": [
{
"id": "1:2266566996008201697",
"name": "id"
},
{
"id": "2:1953917250527765737",
"name": "downloadId"
},
{
"id": "5:6575412958851693470",
"name": "bookId"
},
{
"id": "6:1075612111256674117",
"name": "title"
},
{
"id": "7:2831524841121029990",
"name": "description"
},
{
"id": "8:2334902404590133038",
"name": "language"
},
{
"id": "9:5087250349738158996",
"name": "creator"
},
{
"id": "10:6128960350043895299",
"name": "publisher"
},
{
"id": "11:3850323036475883785",
"name": "date"
},
{
"id": "12:5288623325038033644",
"name": "url"
},
{
"id": "13:2501711400901908648",
"name": "articleCount"
},
{
"id": "14:3550975911715416030",
"name": "mediaCount"
},
{
"id": "15:8949996430663588693",
"name": "size"
},
{
"id": "16:7554483297276446029",
"name": "name"
},
{
"id": "17:8085320504542486236",
"name": "favIcon"
}
],
"relations": []
},
{
"id": "3:5536749840871435068",
"lastPropertyId": "16:6142333908132117423",
"name": "BookOnDiskEntity",
"properties": [
{
"id": "1:4248832782795400383",
"name": "id"
},
{
"id": "2:2644395282642821815",
"name": "file"
},
{
"id": "4:3145196313443812205",
"name": "bookId"
},
{
"id": "5:597997298666253723",
"name": "title"
},
{
"id": "6:8028706022307902131",
"name": "description"
},
{
"id": "7:4257578632233656657",
"name": "language"
},
{
"id": "8:7771231471515752814",
"name": "creator"
},
{
"id": "9:892859866782486178",
"name": "publisher"
},
{
"id": "10:1925365063061602631",
"name": "date"
},
{
"id": "11:1111395522977944209",
"name": "url"
},
{
"id": "12:3765116904492031525",
"name": "articleCount"
},
{
"id": "13:5901922417972273396",
"name": "mediaCount"
},
{
"id": "14:1229023184984372602",
"name": "size"
},
{
"id": "15:6851856791814492874",
"name": "name"
},
{
"id": "16:6142333908132117423",
"name": "favIcon"
}
],
"relations": []
},
{
"id": "4:6278838675135543734",
"lastPropertyId": "4:8812214350305159407",
"name": "LanguageEntity",
"properties": [
{
"id": "1:7795244654012809404",
"name": "id"
},
{
"id": "2:9116495537035444904",
"name": "locale"
},
{
"id": "3:452531964346972307",
"name": "active"
},
{
"id": "4:8812214350305159407",
"name": "occurencesOfLanguage"
}
],
"relations": []
}
],
"lastEntityId": "4:6278838675135543734",
"lastIndexId": "4:4868787482832538530",
"lastRelationId": "0:0",
"lastSequenceId": "0:0",
"modelVersion": 4,
"modelVersionParserMinimum": 4,
"retiredEntityUids": [
349148274283701276
],
"retiredIndexUids": [
1293695782925933448,
3655049272366703856,
7576716732364166705,
4868787482832538530
],
"retiredPropertyUids": [
4712434661554562781,
1521665545502891268,
1831899651198481824,
8913656606098213241,
4745760836781949968,
9177466730609383913,
6985467229796102081,
4417830652027770707,
3485079785941052658,
2875347328622347138,
96906195091428769,
305997162787053035,
8804682238892773896,
3464301918251637220,
5620508895870653354,
7273406943564025911,
428251106490095982
],
"retiredRelationUids": [],
"version": 1
}

View File

@ -0,0 +1,203 @@
{
"_note1": "KEEP THIS FILE! Check it into a version control system (VCS) like git.",
"_note2": "ObjectBox manages crucial IDs for your object model. See docs for details.",
"_note3": "If you have VCS merge conflicts, you must resolve them according to ObjectBox docs.",
"entities": [
{
"id": "1:7257718270326155947",
"lastPropertyId": "17:8085320504542486236",
"name": "DownloadEntity",
"properties": [
{
"id": "1:2266566996008201697",
"name": "id"
},
{
"id": "2:1953917250527765737",
"name": "downloadId"
},
{
"id": "5:6575412958851693470",
"name": "bookId"
},
{
"id": "6:1075612111256674117",
"name": "title"
},
{
"id": "7:2831524841121029990",
"name": "description"
},
{
"id": "8:2334902404590133038",
"name": "language"
},
{
"id": "9:5087250349738158996",
"name": "creator"
},
{
"id": "10:6128960350043895299",
"name": "publisher"
},
{
"id": "11:3850323036475883785",
"name": "date"
},
{
"id": "12:5288623325038033644",
"name": "url"
},
{
"id": "13:2501711400901908648",
"name": "articleCount"
},
{
"id": "14:3550975911715416030",
"name": "mediaCount"
},
{
"id": "15:8949996430663588693",
"name": "size"
},
{
"id": "16:7554483297276446029",
"name": "name"
},
{
"id": "17:8085320504542486236",
"name": "favIcon"
}
],
"relations": []
},
{
"id": "3:5536749840871435068",
"lastPropertyId": "16:6142333908132117423",
"name": "BookOnDiskEntity",
"properties": [
{
"id": "1:4248832782795400383",
"name": "id"
},
{
"id": "2:2644395282642821815",
"name": "file"
},
{
"id": "4:3145196313443812205",
"name": "bookId"
},
{
"id": "5:597997298666253723",
"name": "title"
},
{
"id": "6:8028706022307902131",
"name": "description"
},
{
"id": "7:4257578632233656657",
"name": "language"
},
{
"id": "8:7771231471515752814",
"name": "creator"
},
{
"id": "9:892859866782486178",
"name": "publisher"
},
{
"id": "10:1925365063061602631",
"name": "date"
},
{
"id": "11:1111395522977944209",
"name": "url"
},
{
"id": "12:3765116904492031525",
"name": "articleCount"
},
{
"id": "13:5901922417972273396",
"name": "mediaCount"
},
{
"id": "14:1229023184984372602",
"name": "size"
},
{
"id": "15:6851856791814492874",
"name": "name"
},
{
"id": "16:6142333908132117423",
"name": "favIcon"
}
],
"relations": []
},
{
"id": "4:6278838675135543734",
"lastPropertyId": "4:8812214350305159407",
"name": "LanguageEntity",
"properties": [
{
"id": "1:7795244654012809404",
"name": "id"
},
{
"id": "2:9116495537035444904",
"name": "locale"
},
{
"id": "3:452531964346972307",
"name": "active"
},
{
"id": "4:8812214350305159407",
"name": "occurencesOfLanguage"
}
],
"relations": []
}
],
"lastEntityId": "4:6278838675135543734",
"lastIndexId": "4:4868787482832538530",
"lastRelationId": "0:0",
"lastSequenceId": "0:0",
"modelVersion": 4,
"modelVersionParserMinimum": 4,
"retiredEntityUids": [
349148274283701276
],
"retiredIndexUids": [
1293695782925933448,
3655049272366703856,
7576716732364166705,
4868787482832538530
],
"retiredPropertyUids": [
4712434661554562781,
1521665545502891268,
1831899651198481824,
8913656606098213241,
4745760836781949968,
9177466730609383913,
6985467229796102081,
4417830652027770707,
3485079785941052658,
2875347328622347138,
96906195091428769,
305997162787053035,
8804682238892773896,
3464301918251637220,
5620508895870653354,
7273406943564025911,
428251106490095982
],
"retiredRelationUids": [],
"version": 1
}

View File

@ -23,10 +23,6 @@ import android.content.Context;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
@ -34,6 +30,8 @@ import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.ArrayList;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.assertArrayEquals;
@ -50,7 +48,7 @@ public class KiwixDatabaseTest {
@Test
public void testMigrateDatabase() throws IOException {
KiwixDatabase kiwixDatabase = new KiwixDatabase(mContext);
KiwixDatabase kiwixDatabase = new KiwixDatabase(mContext,null,null);
kiwixDatabase.recreate();
String testId = "8ce5775a-10a9-bbf3-178a-9df69f23263c";
String[] testBookmarks = new String[] {"Test1","Test2","Test3"};

View File

@ -17,18 +17,17 @@
*/
package org.kiwix.kiwixmobile.di.components;
import android.content.Context;
import dagger.BindsInstance;
import dagger.Component;
import javax.inject.Singleton;
import org.kiwix.kiwixmobile.di.modules.ApplicationModule;
import org.kiwix.kiwixmobile.di.modules.JNIModule;
import org.kiwix.kiwixmobile.di.modules.TestJNIModule;
import org.kiwix.kiwixmobile.di.modules.TestNetworkModule;
import org.kiwix.kiwixmobile.tests.NetworkTest;
import org.kiwix.kiwixmobile.tests.ZimTest;
import org.kiwix.kiwixmobile.utils.TestNetworkInterceptor;
import javax.inject.Singleton;
import dagger.Component;
/**
* Created by mhutti1 on 13/04/17.
*/
@ -41,6 +40,14 @@ import dagger.Component;
})
public interface TestComponent extends ApplicationComponent {
@Component.Builder
interface Builder {
@BindsInstance TestComponent.Builder context(Context context);
TestComponent build();
}
void inject(ZimTest zimTest);
void inject(NetworkTest networkTest);

View File

@ -17,7 +17,6 @@
*/
package org.kiwix.kiwixmobile.di.modules;
import org.apache.commons.io.IOUtils;
import org.kiwix.kiwixlib.JNIKiwix;
import org.kiwix.kiwixlib.JNIKiwixString;
import org.mockito.Mockito;

View File

@ -18,49 +18,44 @@
package org.kiwix.kiwixmobile.tests;
import android.Manifest;
import android.support.test.espresso.Espresso;
import android.support.test.espresso.IdlingPolicies;
import android.support.test.espresso.ViewInteraction;
import android.support.test.rule.ActivityTestRule;
import android.support.test.rule.GrantPermissionRule;
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.LargeTest;
import android.util.Log;
import com.schibsted.spain.barista.interaction.BaristaSleepInteractions;
import java.util.concurrent.TimeUnit;
import org.junit.After;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.kiwix.kiwixmobile.R;
import org.kiwix.kiwixmobile.utils.KiwixIdlingResource;
import org.kiwix.kiwixmobile.utils.SplashActivity;
import android.Manifest;
import android.support.test.espresso.Espresso;
import android.support.test.espresso.IdlingPolicies;
import android.support.test.espresso.ViewInteraction;
import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4;
import android.support.test.rule.GrantPermissionRule;
import android.test.suitebuilder.annotation.LargeTest;
import android.util.Log;
import com.schibsted.spain.barista.interaction.BaristaSleepInteractions;
import org.junit.After;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.kiwix.kiwixmobile.R;
import org.kiwix.kiwixmobile.utils.KiwixIdlingResource;
import org.kiwix.kiwixmobile.utils.SplashActivity;
import java.util.concurrent.TimeUnit;
import static android.support.test.espresso.Espresso.onData;
import static android.support.test.espresso.Espresso.onView;
import static android.support.test.espresso.action.ViewActions.click;
import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
import static android.support.test.espresso.matcher.ViewMatchers.withId;
import static android.support.test.espresso.matcher.ViewMatchers.withParent;
import static com.schibsted.spain.barista.assertion.BaristaVisibilityAssertions.assertDisplayed;
import static com.schibsted.spain.barista.interaction.BaristaClickInteractions.clickOn;
import static com.schibsted.spain.barista.interaction.BaristaDialogInteractions.clickDialogPositiveButton;
import static com.schibsted.spain.barista.interaction.BaristaSwipeRefreshInteractions.refresh;
import static junit.framework.Assert.fail;
import static org.hamcrest.Matchers.allOf;
import static org.kiwix.kiwixmobile.testutils.TestUtils.TEST_PAUSE_MS;
import static org.kiwix.kiwixmobile.testutils.TestUtils.allowPermissionsIfNeeded;
import static org.kiwix.kiwixmobile.testutils.TestUtils.captureAndSaveScreenshot;
import static org.kiwix.kiwixmobile.testutils.TestUtils.withContent;
import static org.kiwix.kiwixmobile.utils.StandardActions.deleteZimIfExists;
import static org.kiwix.kiwixmobile.utils.StandardActions.enterHelp;
import static android.support.test.espresso.Espresso.onData;
import static android.support.test.espresso.Espresso.onView;
import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
import static android.support.test.espresso.matcher.ViewMatchers.withId;
import static android.support.test.espresso.matcher.ViewMatchers.withParent;
import static com.schibsted.spain.barista.assertion.BaristaVisibilityAssertions.assertDisplayed;
import static com.schibsted.spain.barista.interaction.BaristaClickInteractions.clickOn;
import static com.schibsted.spain.barista.interaction.BaristaSwipeRefreshInteractions.refresh;
import static junit.framework.Assert.fail;
import static org.hamcrest.Matchers.allOf;
import static org.kiwix.kiwixmobile.testutils.TestUtils.TEST_PAUSE_MS;
import static org.kiwix.kiwixmobile.testutils.TestUtils.allowPermissionsIfNeeded;
import static org.kiwix.kiwixmobile.testutils.TestUtils.captureAndSaveScreenshot;
import static org.kiwix.kiwixmobile.testutils.TestUtils.withContent;
import static org.kiwix.kiwixmobile.utils.StandardActions.deleteZimIfExists;
import static org.kiwix.kiwixmobile.utils.StandardActions.enterHelp;
@LargeTest
@RunWith(AndroidJUnit4.class)
@ -86,6 +81,7 @@ public class DownloadTest {
}
@Test
@Ignore("Broken in 2.5")//TODO: Fix in 3.0
public void downloadTest() {
enterHelp();
clickOn(R.string.menu_zim_manager);
@ -98,15 +94,9 @@ public class DownloadTest {
clickOn(R.string.remote_zims);
try {
clickOn(R.id.network_permission_button);
} catch (RuntimeException e) {
Log.d(KIWIX_DOWNLOAD_TEST, "Failed to click Network Permission Button", e);
}
captureAndSaveScreenshot("Before-checking-for-ZimManager-Main-Activity");
ViewInteraction viewPager2 = onView(
allOf(withId(R.id.container),
allOf(withId(R.id.manageViewPager),
withParent(allOf(withId(R.id.zim_manager_main_activity),
withParent(withId(android.R.id.content)))),
isDisplayed()));
@ -115,13 +105,13 @@ public class DownloadTest {
BaristaSleepInteractions.sleep(TEST_PAUSE_MS);
try {
onData(withContent("ray_charles")).inAdapterView(withId(R.id.library_list));
onData(withContent("ray_charles")).inAdapterView(withId(R.id.libraryList));
} catch (Exception e) {
fail("Couldn't find downloaded file 'ray_charles'\n\nOriginal Exception:\n" +
e.getLocalizedMessage() + "\n\n" );
}
deleteZimIfExists("ray_charles", R.id.library_list);
deleteZimIfExists("ray_charles", R.id.libraryList);
assertDisplayed(R.string.local_zims);
clickOn(R.string.local_zims);

View File

@ -24,11 +24,19 @@ import android.support.test.espresso.IdlingPolicies;
import android.support.test.rule.ActivityTestRule;
import android.support.test.rule.GrantPermissionRule;
import android.util.Log;
import org.apache.commons.io.IOUtils;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.concurrent.TimeUnit;
import javax.inject.Inject;
import okhttp3.mockwebserver.MockResponse;
import okhttp3.mockwebserver.MockWebServer;
import okio.Buffer;
import org.junit.After;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.kiwix.kiwixmobile.KiwixApplication;
@ -37,20 +45,10 @@ import org.kiwix.kiwixmobile.R;
import org.kiwix.kiwixmobile.ZimContentProvider;
import org.kiwix.kiwixmobile.di.components.DaggerTestComponent;
import org.kiwix.kiwixmobile.di.components.TestComponent;
import org.kiwix.kiwixmobile.di.modules.ApplicationModule;
import org.kiwix.kiwixmobile.testutils.TestUtils;
import org.kiwix.kiwixmobile.utils.IOUtils;
import org.kiwix.kiwixmobile.utils.KiwixIdlingResource;
import java.io.IOException;
import java.io.InputStream;
import java.util.concurrent.TimeUnit;
import javax.inject.Inject;
import okhttp3.mockwebserver.MockResponse;
import okhttp3.mockwebserver.MockWebServer;
import okio.Buffer;
import static android.support.test.InstrumentationRegistry.getInstrumentation;
import static android.support.test.espresso.Espresso.onData;
import static android.support.test.espresso.Espresso.onView;
@ -93,9 +91,8 @@ public class NetworkTest {
@Before
public void setUp() {
TestComponent component = DaggerTestComponent.builder().applicationModule
(new ApplicationModule(
(KiwixApplication) getInstrumentation().getTargetContext().getApplicationContext())).build();
TestComponent component = DaggerTestComponent.builder().context(
getInstrumentation().getTargetContext().getApplicationContext()).build();
((KiwixApplication) getInstrumentation().getTargetContext().getApplicationContext()).setApplicationComponent(component);
@ -121,6 +118,7 @@ public class NetworkTest {
@Test
@Ignore("Broken in 2.5")//TODO: Fix in 3.0
public void networkTest() {
mActivityTestRule.launchActivity(null);
@ -130,14 +128,7 @@ public class NetworkTest {
TestUtils.allowPermissionsIfNeeded();
try {
onView(withId(R.id.network_permission_button)).perform(click());
} catch (RuntimeException e) {
Log.i(NETWORK_TEST_TAG,
"Permission dialog was not shown, we probably already have required permissions");
}
onData(withContent("wikipedia_ab_all_2017-03")).inAdapterView(withId(R.id.library_list)).perform(click());
onData(withContent("wikipedia_ab_all_2017-03")).inAdapterView(withId(R.id.libraryList)).perform(click());
try {
onView(withId(android.R.id.button1)).perform(click());

View File

@ -0,0 +1,57 @@
package org.kiwix.kiwixmobile.utils;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
/*
* 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/>.
*/
public class IOUtils {
private IOUtils() {
//utility class
}
private static final int DEFAULT_BUFFER_SIZE = 1024 * 4;
private static final int EOF = -1;
public static byte[] toByteArray(final InputStream input) throws IOException {
try (final ByteArrayOutputStream output = new ByteArrayOutputStream()) {
copy(input, output);
return output.toByteArray();
}
}
private static int copy(final InputStream input, final OutputStream output) throws IOException {
final long count = copyLarge(input, output);
if (count > Integer.MAX_VALUE) {
return -1;
}
return (int) count;
}
private static long copyLarge(final InputStream input, final OutputStream output) throws IOException {
final byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
long count = 0;
int n;
while (EOF != (n = input.read(buffer))) {
output.write(buffer, 0, n);
count += n;
}
return count;
}
}

View File

@ -0,0 +1 @@
contact+android@kiwix.org

View File

@ -0,0 +1 @@
http://www.kiwix.org

View File

@ -0,0 +1 @@
en-US

View File

@ -0,0 +1,9 @@
Die ganze Wikipedia auf deinem Mobilgerät!
Kiwix ist eine Programm, daß das Lesen der Wikipedia und anderer Inhalte (Ubuntu Dokumentation, WikiLeaks, WikiVoyage, WikiSource, etc) ohne Internetverbindung erlaubt.
Sobald du die Inhaltsdateien heruntergeladen hast, die sehr groß sein können, benötigst du keine Internetverbindung um sie zu verwenden.
Siehe die Hilfe in der App und unsere Webseite für Informationen über verfügbare Inhalte.
Anmerkung: Kiwix existiert auch für PCs (Windows, Mac, Linux) verfügbar..

View File

@ -0,0 +1 @@
Die Wikipedia immer dabei; ohne Internetverbindung!

View File

@ -0,0 +1 @@
Kiwix, Wikipedia offline

View File

@ -0,0 +1,7 @@
The whole of Wikipedia on your device!
The app is a lightweight piece of software reading bigger files stored on your device or SD card: once it is installed, you can select which additional content you would like to download (Wikipedia, Wiktionary, TED talks, etc.) and be ready for when your internet connexion is bad (or need to be in airplane mode)!
Please read the instructions inside the App or on the website (www.kiwix.org) to learn about the various contents that are available for download.
Note: Kiwix is also available on regular computers (Windows, Mac, Linux).

Binary file not shown.

After

Width:  |  Height:  |  Size: 253 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 522 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 491 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 212 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 185 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 491 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 145 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 323 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 491 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 522 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

View File

@ -0,0 +1 @@
Wikipedia (and a lot more) at hand everywhere. No internet required!

View File

@ -0,0 +1 @@
Kiwix, Wikipedia offline

View File

@ -0,0 +1,11 @@
Tout Wikipédia dans votre mobile !
Kiwix est un lecteur de contenus hors-ligne qui peut être utilisé pour Wikipédia ainsi que d'autres contenus (Documentation Ubuntu, WikiLeaks, WikiVoyage, WikiSource, etc).
Une fois que vous avez téléchargé le fichier de contenu (potentiellement très gros), vous n'avez plus du tout besoin de connexion pour l'utiliser.
Kiwix est une application légère utilisant des fichiers ZIM que vous aurez préalablement téléchargé et stocké sur la mémoire externe (carte SD).
Merci de consulter les instructions d'utilisations dans l'application ainsi que sur le site web pour en savoir plus sur les contenus disponibles.
Note: Kiwix est aussi disponible sur ordinateur (Windows, Mac, Linux).

View File

@ -0,0 +1 @@
Emportez la Wikipédia partout avec vous ; sans connexion !

View File

@ -0,0 +1 @@
Kiwix, Wikipédia sans Internet

View File

@ -0,0 +1,11 @@
L'intera Wikipedia sempre con te!
Kiwix è un lettore non in linea di contenuti e siti di ogni genere, Wikipedia ma non solo (documentazione di Ubuntu, WikiLeaks, Wikisource, Wikivoyage ecc.).
Una volta scaricato l'archivio ZIM contenente il tutto (potenzialmente molto grande), non serve alcuna connessione a internet per navigarlo.
Kiwix è leggero e gli archivi ZIM si possono scaricare e conservare nel tuo apparecchio android o memoria esterna (scheda SD).
Controlla le istruzioni nell'applicazione e nel sito per sapere di più sui contenuti disponibili.
Nota, Kiwix è disponibile anche per computer normali (Windows, Mac e Linux).

View File

@ -0,0 +1 @@
Wikipedia sempre con te!

View File

@ -0,0 +1 @@
Kiwix

View File

@ -0,0 +1,5 @@
Vikipedi'nin tamamı cihazınızda!
Uygulama, cihazınızda veya SD kartta depolanan büyük dosyalar okuyan hafif bir yazılım parçasıdır: yükledikten sonra, hangi ek içerikler indirmek istediğinizi seçebilirsiniz (Vikipedi, Vikisözlük, TED Konferansları, v.b.) ve ne zaman internet bağlantınız kötü olsa, hazır olabilirsiniz (veya uçak modunda olmanız gerekiyorsa) !
Indirmek için hazır olan çeşitli içerikler hakkında bilgi edinmek için, lütfen uygulama içindeki veya web sitemizindeki talimatları okuyun (www.kiwix.org)

Binary file not shown.

After

Width:  |  Height:  |  Size: 379 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 809 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 278 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 502 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 121 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 670 KiB

View File

@ -0,0 +1 @@
Kiwix ile, Vikipedi tamamıyla telefonunuza veya tabletinize indirin

View File

@ -0,0 +1 @@
Kiwix - offline Vikipedi

View File

@ -0,0 +1,3 @@
NEW: Downloads are now using the DownloadManager
NEW: Downloads/Device/Library completely rewritten
+ Lots More

View File

@ -130,6 +130,7 @@
</activity>
<activity
android:name=".zim_manager.ZimManageActivity"
android:launchMode="singleTop"
android:label="@string/choose_file">
<!-- TODO -->
@ -175,8 +176,6 @@
</receiver>
<service android:name=".downloader.DownloadService" />
<activity
android:name=".KiwixErrorActivity"
android:process=":error_activity" />
@ -192,6 +191,11 @@
android:resource="@xml/provider_paths" />
</provider>
<receiver android:name=".zim_manager.DownloadNotificationClickedReceiver">
<intent-filter>
<action android:name="android.intent.action.DOWNLOAD_NOTIFICATION_CLICKED"/>
</intent-filter>
</receiver>
</application>
</manifest>

View File

@ -18,26 +18,22 @@
package org.kiwix.kiwixmobile;
import android.app.Activity;
import android.app.Application;
import android.content.Context;
import android.os.Environment;
import android.support.multidex.MultiDexApplication;
import android.util.Log;
import android.support.v7.app.AppCompatDelegate;
import android.util.Log;
import com.squareup.leakcanary.LeakCanary;
import org.kiwix.kiwixmobile.di.components.ApplicationComponent;
import org.kiwix.kiwixmobile.di.components.DaggerApplicationComponent;
import org.kiwix.kiwixmobile.di.modules.ApplicationModule;
import java.io.File;
import java.io.IOException;
import javax.inject.Inject;
import dagger.android.AndroidInjector;
import dagger.android.DispatchingAndroidInjector;
import dagger.android.HasActivityInjector;
import java.io.File;
import java.io.IOException;
import javax.inject.Inject;
import org.kiwix.kiwixmobile.di.components.ApplicationComponent;
import org.kiwix.kiwixmobile.di.components.DaggerApplicationComponent;
public class KiwixApplication extends MultiDexApplication implements HasActivityInjector {
public class KiwixApplication extends Application implements HasActivityInjector {
private static KiwixApplication application;
private static ApplicationComponent applicationComponent;
@ -68,13 +64,18 @@ public class KiwixApplication extends MultiDexApplication implements HasActivity
super.attachBaseContext(base);
application = this;
setApplicationComponent(DaggerApplicationComponent.builder()
.applicationModule(new ApplicationModule(this))
.context(this)
.build());
}
@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;
}
if (isExternalStorageWritable()) {
File appDirectory = new File(Environment.getExternalStorageDirectory() + "/Kiwix");
logFile = new File(appDirectory, "logcat.txt");
@ -105,13 +106,7 @@ public class KiwixApplication extends MultiDexApplication implements HasActivity
}
Log.d("KIWIX", "Started KiwixApplication");
applicationComponent.inject(this);
if (LeakCanary.isInAnalyzerProcess(this)) {
// This process is dedicated to LeakCanary for heap analysis.
// You should not init your app in this process.
return;
}
LeakCanary.install(this);
}

View File

@ -9,26 +9,23 @@ import android.os.Environment;
import android.support.v4.content.FileProvider;
import android.widget.Button;
import android.widget.CheckBox;
import org.kiwix.kiwixmobile.base.BaseActivity;
import org.kiwix.kiwixmobile.database.BookDao;
import org.kiwix.kiwixmobile.library.entity.LibraryNetworkEntity;
import org.kiwix.kiwixmobile.utils.SplashActivity;
import java.io.File;
import java.util.ArrayList;
import javax.inject.Inject;
import butterknife.BindView;
import butterknife.ButterKnife;
import java.io.File;
import java.util.List;
import javax.inject.Inject;
import org.kiwix.kiwixmobile.base.BaseActivity;
import org.kiwix.kiwixmobile.database.newdb.dao.NewBookDao;
import org.kiwix.kiwixmobile.downloader.model.BookOnDisk;
import org.kiwix.kiwixmobile.library.entity.LibraryNetworkEntity;
import org.kiwix.kiwixmobile.utils.SplashActivity;
import static org.kiwix.kiwixmobile.utils.LanguageUtils.getCurrentLocale;
public class KiwixErrorActivity extends BaseActivity {
@Inject
BookDao bookDao;
NewBookDao bookDao;
@BindView(R.id.reportButton)
Button reportButton;
@ -89,10 +86,11 @@ public class KiwixErrorActivity extends BaseActivity {
}
if(allowZimsCheckbox.isChecked()) {
ArrayList<LibraryNetworkEntity.Book> books = bookDao.getBooks();
List<BookOnDisk> books = bookDao.getBooks();
StringBuilder sb = new StringBuilder();
for(LibraryNetworkEntity.Book book: books) {
for (BookOnDisk bookOnDisk : books) {
final LibraryNetworkEntity.Book book = bookOnDisk.getBook();
String bookString = book.getTitle() +
":\nArticles: ["+ book.getArticleCount() +
"]\nCreator: [" + book.getCreator() +

View File

@ -482,7 +482,7 @@ public class KiwixMobileActivity extends BaseActivity implements WebViewCallback
}
if (i.hasExtra(EXTRA_ZIM_FILE)) {
File file = new File(getFileName(i.getStringExtra(EXTRA_ZIM_FILE)));
LibraryFragment.mService.cancelNotification(i.getIntExtra(EXTRA_NOTIFICATION_ID, 0));
//LibraryFragment.mService.cancelNotification(i.getIntExtra(EXTRA_NOTIFICATION_ID, 0));
Uri uri = Uri.fromFile(file);
finish();

View File

@ -0,0 +1,57 @@
package org.kiwix.kiwixmobile;
import android.arch.lifecycle.ViewModel;
import android.arch.lifecycle.ViewModelProvider;
import java.util.Map;
import javax.inject.Inject;
import javax.inject.Provider;
import javax.inject.Singleton;
/*
* 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/>.
*/
@Singleton
public class KiwixViewModelFactory implements ViewModelProvider.Factory {
private final Map<Class<? extends ViewModel>, Provider<ViewModel>> creators;
@Inject
public KiwixViewModelFactory(Map<Class<? extends ViewModel>, Provider<ViewModel>> creators) {
this.creators = creators;
}
@SuppressWarnings("unchecked")
@Override
public <T extends ViewModel> T create(Class<T> modelClass) {
Provider<? extends ViewModel> creator = creators.get(modelClass);
if (creator == null) {
for (Map.Entry<Class<? extends ViewModel>, Provider<ViewModel>> entry : creators.entrySet()) {
if (modelClass.isAssignableFrom(entry.getKey())) {
creator = entry.getValue();
break;
}
}
}
if (creator == null) {
throw new IllegalArgumentException("unknown model class " + modelClass);
}
try {
return (T) creator.get();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}

View File

@ -1,32 +0,0 @@
package org.kiwix.kiwixmobile.base;
import android.app.Activity;
import android.content.Context;
import android.os.Build;
import android.support.v4.app.Fragment;
import org.kiwix.kiwixmobile.KiwixApplication;
/**
* All fragments should inherit from this fragment.
*/
public abstract class BaseFragment extends Fragment {
@Override
public void onAttach(Context context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
KiwixApplication.getApplicationComponent().inject(this);
}
super.onAttach(context);
}
@SuppressWarnings("deprecation")
@Override
public void onAttach(Activity activity) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
KiwixApplication.getApplicationComponent().inject(this);
}
super.onAttach(activity);
}
}

View File

@ -0,0 +1,26 @@
package org.kiwix.kiwixmobile.base
import android.content.Context
import android.support.v4.app.Fragment
import android.support.v4.app.FragmentActivity
import org.kiwix.kiwixmobile.KiwixApplication
import org.kiwix.kiwixmobile.di.components.ActivityComponent
/**
* All fragments should inherit from this fragment.
*/
abstract class BaseFragment : Fragment() {
override fun onAttach(context: Context?) {
super.onAttach(context)
inject(
KiwixApplication.getApplicationComponent().activityComponent()
.activity(activity as FragmentActivity)
.build()
)
}
abstract fun inject(activityComponent: ActivityComponent)
}

View File

@ -17,18 +17,13 @@
*/
package org.kiwix.kiwixmobile.database;
import com.yahoo.squidb.data.SquidCursor;
import com.yahoo.squidb.sql.Query;
import org.kiwix.kiwixmobile.database.entity.BookDatabaseEntity;
import org.kiwix.kiwixmobile.library.entity.LibraryNetworkEntity.Book;
import org.kiwix.kiwixmobile.utils.files.FileUtils;
import java.io.File;
import java.util.ArrayList;
import javax.inject.Inject;
import org.kiwix.kiwixmobile.database.entity.BookDatabaseEntity;
import org.kiwix.kiwixmobile.library.entity.LibraryNetworkEntity.Book;
import static org.kiwix.kiwixmobile.downloader.ChunkUtils.hasParts;
@ -36,6 +31,7 @@ import static org.kiwix.kiwixmobile.downloader.ChunkUtils.hasParts;
* Dao class for books
*/
@Deprecated
public class BookDao {
private KiwixDatabase mDb;
@ -44,7 +40,7 @@ public class BookDao {
this.mDb = kiwixDatabase;
}
public void setBookDetails(Book book, SquidCursor<BookDatabaseEntity> bookCursor) {
book.id = bookCursor.get(BookDatabaseEntity.BOOK_ID);
book.title = bookCursor.get(BookDatabaseEntity.TITLE);
@ -60,26 +56,7 @@ public class BookDao {
book.favicon = bookCursor.get(BookDatabaseEntity.FAVICON);
book.bookName = bookCursor.get(BookDatabaseEntity.NAME);
}
public void setBookDatabaseEntity(Book book, BookDatabaseEntity bookDatabaseEntity) {
bookDatabaseEntity.setBookId(book.getId());
bookDatabaseEntity.setTitle(book.getTitle());
bookDatabaseEntity.setDescription(book.getDescription());
bookDatabaseEntity.setLanguage(book.getLanguage());
bookDatabaseEntity.setBookCreator(book.getCreator());
bookDatabaseEntity.setPublisher(book.getPublisher());
bookDatabaseEntity.setDate(book.getDate());
bookDatabaseEntity.setUrl(book.file.getPath());
bookDatabaseEntity.setArticleCount(book.getArticleCount());
bookDatabaseEntity.setMediaCount(book.getMediaCount());
bookDatabaseEntity.setSize(book.getSize());
bookDatabaseEntity.setFavicon(book.getFavicon());
bookDatabaseEntity.setName(book.getName());
String filePath = book.file.getPath();
mDb.deleteWhere(BookDatabaseEntity.class, BookDatabaseEntity.URL.eq(filePath));
mDb.persist(bookDatabaseEntity);
}
public ArrayList<Book> getBooks() {
SquidCursor<BookDatabaseEntity> bookCursor = mDb.query(
BookDatabaseEntity.class,
@ -91,48 +68,10 @@ public class BookDao {
if (!hasParts(book.file)) {
if (book.file.exists()) {
books.add(book);
} else {
mDb.deleteWhere(BookDatabaseEntity.class, BookDatabaseEntity.URL.eq(book.file));
}
}
}
bookCursor.close();
return books;
}
public ArrayList<Book> getDownloadingBooks() {
SquidCursor<BookDatabaseEntity> bookCursor = mDb.query(
BookDatabaseEntity.class,
Query.select());
ArrayList<Book> books = new ArrayList<>();
while (bookCursor.moveToNext()) {
Book book = new Book();
setBookDetails(book, bookCursor);
book.remoteUrl = bookCursor.get(BookDatabaseEntity.REMOTE_URL);
if (hasParts(book.file)) {
books.add(book);
}
}
bookCursor.close();
return books;
}
public void saveBooks(ArrayList<Book> books) {
for (Book book : books) {
if (book != null) {
BookDatabaseEntity bookDatabaseEntity = new BookDatabaseEntity();
setBookDatabaseEntity(book, bookDatabaseEntity);
}
}
}
public void saveBook(Book book) {
BookDatabaseEntity bookDatabaseEntity = new BookDatabaseEntity();
bookDatabaseEntity.setRemoteUrl(book.remoteUrl);
setBookDatabaseEntity(book, bookDatabaseEntity);
}
public void deleteBook(String id) {
mDb.deleteWhere(BookDatabaseEntity.class, BookDatabaseEntity.BOOK_ID.eq(id));
}
}
}

View File

@ -21,40 +21,42 @@ package org.kiwix.kiwixmobile.database;
import android.content.Context;
import android.util.Log;
import com.yahoo.squidb.data.SquidDatabase;
import com.yahoo.squidb.data.adapter.SQLiteDatabaseWrapper;
import com.yahoo.squidb.sql.Table;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import javax.inject.Inject;
import javax.inject.Singleton;
import org.kiwix.kiwixmobile.ZimContentProvider;
import org.kiwix.kiwixmobile.database.entity.BookDatabaseEntity;
import org.kiwix.kiwixmobile.database.entity.Bookmarks;
import org.kiwix.kiwixmobile.database.entity.LibraryDatabaseEntity;
import org.kiwix.kiwixmobile.database.entity.NetworkLanguageDatabaseEntity;
import org.kiwix.kiwixmobile.database.entity.RecentSearch;
import org.kiwix.kiwixmobile.database.newdb.dao.NewBookDao;
import org.kiwix.kiwixmobile.database.newdb.dao.NewLanguagesDao;
import org.kiwix.kiwixmobile.utils.UpdateUtils;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import javax.inject.Inject;
import javax.inject.Singleton;
import static org.kiwix.kiwixmobile.utils.Constants.TAG_KIWIX;
@Singleton
public class KiwixDatabase extends SquidDatabase {
private static final int VERSION = 15;
private static final int VERSION = 16;
private Context context;
private final NewBookDao bookDao;
private final NewLanguagesDao languagesDao;
@Inject
public KiwixDatabase(Context context) {
public KiwixDatabase(Context context, NewBookDao bookDao, NewLanguagesDao languagesDao) {
super(context);
this.context = context;
this.bookDao = bookDao;
this.languagesDao = languagesDao;
}
@Override
@ -64,17 +66,26 @@ public class KiwixDatabase extends SquidDatabase {
@Override
protected Table[] getTables() {
return new Table[]{
BookDatabaseEntity.TABLE,
LibraryDatabaseEntity.TABLE,
return new Table[] {
RecentSearch.TABLE,
Bookmarks.TABLE,
NetworkLanguageDatabaseEntity.TABLE
};
}
@Override
protected boolean onUpgrade(SQLiteDatabaseWrapper db, int oldVersion, int newVersion) {
if (newVersion >= 16) { //2.5 attempt reading values from old db before they get dropped
try {
bookDao.migrationInsert(new BookDao(this).getBooks());
} catch (Exception e) {
e.printStackTrace();
}
try {
languagesDao.insert(new NetworkLanguageDao(this).getFilteredLanguages());
} catch (Exception e) {
e.printStackTrace();
}
}
if (newVersion >= 3 && oldVersion < 3) {
db.execSQL("DROP TABLE IF EXISTS recents");
tryCreateTable(RecentSearch.TABLE);
@ -133,6 +144,11 @@ public class KiwixDatabase extends SquidDatabase {
if (newVersion >= 15 && oldVersion < 15) {
reformatBookmarks();
}
if (newVersion >= 16) { //2.5 drop tables
tryDropTable(BookDatabaseEntity.TABLE);
tryDropTable(NetworkLanguageDatabaseEntity.TABLE);
tryDropTable(LibraryDatabaseEntity.TABLE);
}
return true;
}
@ -177,4 +193,3 @@ public class KiwixDatabase extends SquidDatabase {
}
}

View File

@ -21,16 +21,12 @@ package org.kiwix.kiwixmobile.database;
import com.yahoo.squidb.data.SquidCursor;
import com.yahoo.squidb.sql.Query;
import java.util.ArrayList;
import javax.inject.Inject;
import org.kiwix.kiwixmobile.database.entity.NetworkLanguageDatabaseEntity;
import org.kiwix.kiwixmobile.library.LibraryAdapter;
import org.kiwix.kiwixmobile.library.LibraryAdapter.Language;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.kiwix.kiwixmobile.zim_manager.library_view.adapter.Language;
@Deprecated
public class NetworkLanguageDao {
private KiwixDatabase mDb;
@ -39,31 +35,17 @@ public class NetworkLanguageDao {
this.mDb = kiwikDatabase;
}
public ArrayList<LibraryAdapter.Language> getFilteredLanguages() {
SquidCursor<NetworkLanguageDatabaseEntity> languageCursor = mDb.query(
public ArrayList<Language> getFilteredLanguages() {
ArrayList<Language> result = new ArrayList<>();
try (SquidCursor<NetworkLanguageDatabaseEntity> languageCursor = mDb.query(
NetworkLanguageDatabaseEntity.class,
Query.select());
ArrayList<LibraryAdapter.Language> result = new ArrayList<>();
try {
Query.select())) {
while (languageCursor.moveToNext()) {
String languageCode = languageCursor.get(NetworkLanguageDatabaseEntity.LANGUAGE_I_S_O_3);
boolean enabled = languageCursor.get(NetworkLanguageDatabaseEntity.ENABLED);
result.add(new LibraryAdapter.Language(languageCode, enabled));
result.add(new Language(languageCode, enabled, 0));
}
} finally {
languageCursor.close();
}
return result;
}
public void saveFilteredLanguages(List<Language> languages){
mDb.deleteAll(NetworkLanguageDatabaseEntity.class);
Collections.sort(languages, (language, t1) -> language.language.compareTo(t1.language));
for (LibraryAdapter.Language language : languages){
NetworkLanguageDatabaseEntity networkLanguageDatabaseEntity = new NetworkLanguageDatabaseEntity();
networkLanguageDatabaseEntity.setLanguageISO3(language.languageCode);
networkLanguageDatabaseEntity.setIsEnabled(language.active);
mDb.persist(networkLanguageDatabaseEntity);
}
}
}
}

View File

@ -54,4 +54,4 @@ public class BookDataSource {
public boolean downloaded;
}
}

View File

@ -25,4 +25,5 @@ import com.yahoo.squidb.annotations.TableModelSpec;
public class NetworkLanguageSpec {
public String languageISO3;
public boolean enabled;
public int numberOfOccurences;
}

View File

@ -0,0 +1,56 @@
/*
* 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.database.newdb.dao
import io.objectbox.Box
import io.objectbox.kotlin.inValues
import io.objectbox.kotlin.query
import org.kiwix.kiwixmobile.database.newdb.entities.BookOnDiskEntity
import org.kiwix.kiwixmobile.database.newdb.entities.BookOnDiskEntity_
import org.kiwix.kiwixmobile.downloader.model.BookOnDisk
import org.kiwix.kiwixmobile.library.entity.LibraryNetworkEntity.Book
import java.util.ArrayList
import javax.inject.Inject
class NewBookDao @Inject constructor(private val box: Box<BookOnDiskEntity>) {
fun books() = box.asFlowable()
.map { it.map(::BookOnDisk) }
fun getBooks() = box.all.map(::BookOnDisk)
fun insert(booksOnDisk: List<BookOnDisk>) {
box.store.callInTx {
box
.query {
inValues(BookOnDiskEntity_.bookId, booksOnDisk.map { it.book.id }.toTypedArray())
}
.remove()
box.put(booksOnDisk.map(::BookOnDiskEntity))
}
}
fun delete(databaseId: Long) {
box.remove(databaseId)
}
fun migrationInsert(books: ArrayList<Book>) {
insert(books.map { BookOnDisk(book = it, file = it.file) })
}
}

View File

@ -0,0 +1,45 @@
package org.kiwix.kiwixmobile.database.newdb.dao
import android.util.Log
import io.objectbox.Box
import io.objectbox.kotlin.inValues
import io.objectbox.kotlin.query
import io.objectbox.rx.RxQuery
import io.reactivex.BackpressureStrategy.LATEST
import org.kiwix.kiwixmobile.database.newdb.entities.DownloadEntity
import org.kiwix.kiwixmobile.database.newdb.entities.DownloadEntity_
import org.kiwix.kiwixmobile.downloader.model.DownloadModel
import org.kiwix.kiwixmobile.library.entity.LibraryNetworkEntity.Book
import javax.inject.Inject
class NewDownloadDao @Inject constructor(private val box: Box<DownloadEntity>) {
fun downloads() = box.asFlowable()
.map { it.map(DownloadEntity::toDownloadModel) }
fun delete(vararg downloadIds: Long) {
box
.query {
inValues(DownloadEntity_.downloadId, downloadIds)
}
.remove()
}
fun containsAny(vararg downloadIds: Long) =
box
.query {
inValues(DownloadEntity_.downloadId, downloadIds)
}
.count() > 0
fun doesNotAlreadyExist(book: Book) =
box
.query {
equal(DownloadEntity_.bookId, book.id)
}
.count() == 0L
fun insert(downloadModel: DownloadModel) {
box.put(DownloadEntity(downloadModel))
}
}

View File

@ -0,0 +1,42 @@
/*
* 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.database.newdb.dao
import io.objectbox.Box
import io.objectbox.kotlin.query
import io.objectbox.rx.RxQuery
import io.reactivex.BackpressureStrategy
import io.reactivex.BackpressureStrategy.LATEST
import org.kiwix.kiwixmobile.database.newdb.entities.LanguageEntity
import org.kiwix.kiwixmobile.zim_manager.library_view.adapter.Language
import javax.inject.Inject
class NewLanguagesDao @Inject constructor(private val box: Box<LanguageEntity>) {
fun languages() = box.asFlowable()
.map { it.map(LanguageEntity::toLanguageModel) }
fun insert(languages: List<Language>) {
box.store.callInTx {
box.removeAll()
box.put(languages.map(::LanguageEntity))
}
}
}
internal fun <T> Box<T>.asFlowable(backpressureStrategy: BackpressureStrategy = LATEST) =
RxQuery.observable(query {}).toFlowable(backpressureStrategy)

View File

@ -0,0 +1,71 @@
package org.kiwix.kiwixmobile.database.newdb.entities
import io.objectbox.annotation.Convert
import io.objectbox.annotation.Entity
import io.objectbox.annotation.Id
import io.objectbox.converter.PropertyConverter
import org.kiwix.kiwixmobile.downloader.model.BookOnDisk
import org.kiwix.kiwixmobile.library.entity.LibraryNetworkEntity.Book
import java.io.File
@Entity
data class BookOnDiskEntity(
@Id var id: Long = 0,
@Convert(converter = StringToFileConverter::class, dbType = String::class)
val file: File = File(""),
val bookId: String,
val title: String,
val description: String,
val language: String,
val creator: String,
val publisher: String,
val date: String,
val url: String?,
val articleCount: String?,
val mediaCount: String?,
val size: String,
val name: String?,
val favIcon: String
) {
constructor(bookOnDisk: BookOnDisk) : this(
0,
bookOnDisk.file,
bookOnDisk.book.getId(),
bookOnDisk.book.getTitle(),
bookOnDisk.book.getDescription(),
bookOnDisk.book.getLanguage(),
bookOnDisk.book.getCreator(),
bookOnDisk.book.getPublisher(),
bookOnDisk.book.getDate(),
bookOnDisk.book.getUrl(),
bookOnDisk.book.getArticleCount(),
bookOnDisk.book.getMediaCount(),
bookOnDisk.book.getSize(),
bookOnDisk.book.getName(),
bookOnDisk.book.getFavicon()
)
fun toBook() = Book().apply {
id = this@BookOnDiskEntity.bookId
title = this@BookOnDiskEntity.title
description = this@BookOnDiskEntity.description
language = this@BookOnDiskEntity.language
creator = this@BookOnDiskEntity.creator
publisher = this@BookOnDiskEntity.publisher
date = this@BookOnDiskEntity.date
url = this@BookOnDiskEntity.url
articleCount = this@BookOnDiskEntity.articleCount
mediaCount = this@BookOnDiskEntity.mediaCount
size = this@BookOnDiskEntity.size
bookName = this@BookOnDiskEntity.name
favicon = this@BookOnDiskEntity.favIcon
}
}
class StringToFileConverter : PropertyConverter<File, String> {
override fun convertToDatabaseValue(entityProperty: File?) = entityProperty?.path ?: ""
override fun convertToEntityProperty(databaseValue: String?) = File(databaseValue ?: "")
}

View File

@ -0,0 +1,78 @@
/*
* 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.database.newdb.entities
import io.objectbox.annotation.Entity
import io.objectbox.annotation.Id
import org.kiwix.kiwixmobile.downloader.model.DownloadModel
import org.kiwix.kiwixmobile.library.entity.LibraryNetworkEntity.Book
@Entity
data class DownloadEntity(
@Id var id: Long = 0,
val downloadId: Long,
val bookId: String,
val title: String,
val description: String,
val language: String,
val creator: String,
val publisher: String,
val date: String,
val url: String?,
val articleCount: String?,
val mediaCount: String?,
val size: String,
val name: String?,
val favIcon: String
) {
constructor(downloadModel: DownloadModel) : this(
0,
downloadModel.downloadId,
downloadModel.book.getId(),
downloadModel.book.getTitle(),
downloadModel.book.getDescription(),
downloadModel.book.getLanguage(),
downloadModel.book.getCreator(),
downloadModel.book.getPublisher(),
downloadModel.book.getDate(),
downloadModel.book.getUrl(),
downloadModel.book.getArticleCount(),
downloadModel.book.getMediaCount(),
downloadModel.book.getSize(),
downloadModel.book.getName(),
downloadModel.book.getFavicon()
)
fun toDownloadModel() = DownloadModel(id, downloadId, toBook())
private fun toBook() = Book().apply {
id = this@DownloadEntity.bookId
title = this@DownloadEntity.title
description = this@DownloadEntity.description
language = this@DownloadEntity.language
creator = this@DownloadEntity.creator
publisher = this@DownloadEntity.publisher
date = this@DownloadEntity.date
url = this@DownloadEntity.url
articleCount = this@DownloadEntity.articleCount
mediaCount = this@DownloadEntity.mediaCount
size = this@DownloadEntity.size
bookName = this@DownloadEntity.name
favicon = this@DownloadEntity.favIcon
}
}

View File

@ -0,0 +1,36 @@
package org.kiwix.kiwixmobile.database.newdb.entities
import io.objectbox.annotation.Convert
import io.objectbox.annotation.Entity
import io.objectbox.annotation.Id
import io.objectbox.converter.PropertyConverter
import org.kiwix.kiwixmobile.zim_manager.library_view.adapter.Language
import java.util.Locale
@Entity
data class LanguageEntity(
@Id var id: Long = 0,
@Convert(converter = StringToLocaleConverter::class, dbType = String::class)
val locale: Locale = Locale.ENGLISH,
val active: Boolean = false,
val occurencesOfLanguage: Int = 0
) {
constructor(language: Language) : this(
0,
Locale(language.languageCode),
language.active,
language.occurencesOfLanguage
)
fun toLanguageModel() = Language(locale, active, occurencesOfLanguage)
}
class StringToLocaleConverter : PropertyConverter<Locale, String> {
override fun convertToDatabaseValue(entityProperty: Locale?) =
entityProperty?.isO3Language ?: Locale.ENGLISH.isO3Language
override fun convertToEntityProperty(databaseValue: String?) =
databaseValue?.let(::Locale) ?: Locale.ENGLISH
}

View File

@ -0,0 +1,34 @@
package org.kiwix.kiwixmobile.di;
import android.arch.lifecycle.ViewModel;
import dagger.MapKey;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/*
* 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/>.
*/
@Documented
@Target({ ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@MapKey
public @interface ViewModelKey {
Class<? extends ViewModel> value();
}

View File

@ -0,0 +1,43 @@
/*
* 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.di.components
import android.app.Activity
import dagger.BindsInstance
import dagger.Subcomponent
import org.kiwix.kiwixmobile.di.modules.ActivityModule
import org.kiwix.kiwixmobile.downloader.DownloadFragment
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.ZimFileSelectFragment
import org.kiwix.kiwixmobile.zim_manager.library_view.LibraryFragment
@Subcomponent(modules = [ActivityModule::class])
interface ActivityComponent {
fun inject(downloadFragment: DownloadFragment)
fun inject(libraryFragment: LibraryFragment)
fun inject(zimFileSelectFragment: ZimFileSelectFragment)
@Subcomponent.Builder
interface Builder {
@BindsInstance fun activity(activity: Activity): Builder
fun build(): ActivityComponent
}
}

View File

@ -17,23 +17,20 @@
*/
package org.kiwix.kiwixmobile.di.components;
import android.content.Context;
import dagger.BindsInstance;
import dagger.Component;
import javax.inject.Singleton;
import org.kiwix.kiwixmobile.KiwixApplication;
import org.kiwix.kiwixmobile.ZimContentProvider;
import org.kiwix.kiwixmobile.base.BaseFragment;
import org.kiwix.kiwixmobile.di.modules.ApplicationModule;
import org.kiwix.kiwixmobile.di.modules.JNIModule;
import org.kiwix.kiwixmobile.di.modules.NetworkModule;
import org.kiwix.kiwixmobile.downloader.DownloadService;
import org.kiwix.kiwixmobile.library.LibraryAdapter;
import org.kiwix.kiwixmobile.settings.KiwixSettingsActivity;
import org.kiwix.kiwixmobile.views.AutoCompleteAdapter;
import org.kiwix.kiwixmobile.views.web.KiwixWebView;
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.ZimFileSelectFragment;
import org.kiwix.kiwixmobile.zim_manager.library_view.LibraryFragment;
import javax.inject.Singleton;
import dagger.Component;
import org.kiwix.kiwixmobile.zim_manager.DownloadNotificationClickedReceiver;
@Singleton
@Component(modules = {
@ -42,23 +39,28 @@ import dagger.Component;
JNIModule.class,
})
public interface ApplicationComponent {
@Component.Builder
interface Builder {
@BindsInstance Builder context(Context context);
ApplicationComponent build();
}
ActivityComponent.Builder activityComponent();
void inject(KiwixApplication application);
void inject(DownloadService service);
void inject(LibraryFragment libraryFragment);
void inject(BaseFragment baseFragment);
void inject(ZimFileSelectFragment zimFileSelectFragment);
void inject(ZimContentProvider zimContentProvider);
void inject(LibraryAdapter libraryAdapter);
void inject(KiwixWebView kiwixWebView);
void inject(KiwixSettingsActivity.PrefsFragment prefsFragment);
void inject(AutoCompleteAdapter autoCompleteAdapter);
void inject(DownloadNotificationClickedReceiver downloadNotificationClickedReceiver);
}

View File

@ -0,0 +1,29 @@
/*
* 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.di.modules
import dagger.Binds
import dagger.Module
import org.kiwix.kiwixmobile.utils.AlertDialogShower
import org.kiwix.kiwixmobile.utils.DialogShower
@Module
abstract class ActivityModule {
@Binds
abstract fun bindDialogShower(alertDialogShower: AlertDialogShower): DialogShower
}

View File

@ -17,36 +17,45 @@
*/
package org.kiwix.kiwixmobile.di.modules;
import android.app.Application;
import android.app.DownloadManager;
import android.app.NotificationManager;
import android.content.Context;
import org.kiwix.kiwixmobile.KiwixApplication;
import org.kiwix.kiwixmobile.utils.BookUtils;
import javax.inject.Singleton;
import dagger.Module;
import dagger.Provides;
import dagger.android.AndroidInjectionModule;
import javax.inject.Singleton;
import org.kiwix.kiwixmobile.downloader.model.UriToFileConverter;
import org.kiwix.kiwixmobile.utils.BookUtils;
@Module(includes = {ActivityBindingModule.class, AndroidInjectionModule.class})
@Module(includes = {
ActivityBindingModule.class,
AndroidInjectionModule.class,
DownloaderModule.class,
ViewModelModule.class,
DatabaseModule.class
})
public class ApplicationModule {
private final KiwixApplication application;
public ApplicationModule(KiwixApplication application) {
this.application = application;
}
@Provides @Singleton Context provideApplicationContext() {
return this.application;
@Provides @Singleton Application provideApplication(Context context) {
return (Application) context;
}
@Provides @Singleton NotificationManager provideNotificationManager(Context context) {
return (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
}
@Provides @Singleton DownloadManager provideDownloadManager(Context context) {
return (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
}
@Provides @Singleton
BookUtils provideBookUtils() {
return new BookUtils();
}
@Provides @Singleton
UriToFileConverter provideUriToFIleCOnverter() {
return new UriToFileConverter.Impl();
}
}

View File

@ -0,0 +1,44 @@
/*
* 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.di.modules
import android.content.Context
import dagger.Module
import dagger.Provides
import io.objectbox.BoxStore
import io.objectbox.kotlin.boxFor
import org.kiwix.kiwixmobile.database.newdb.dao.NewBookDao
import org.kiwix.kiwixmobile.database.newdb.dao.NewDownloadDao
import org.kiwix.kiwixmobile.database.newdb.dao.NewLanguagesDao
import org.kiwix.kiwixmobile.database.newdb.entities.MyObjectBox
import javax.inject.Singleton
@Module
class DatabaseModule {
@Provides @Singleton fun providesBoxStore(context: Context): BoxStore =
MyObjectBox.builder().androidContext(context.applicationContext).build()
@Provides @Singleton fun providesNewDownloadDao(boxStore: BoxStore): NewDownloadDao =
NewDownloadDao(boxStore.boxFor())
@Provides @Singleton fun providesNewBookDao(boxStore: BoxStore): NewBookDao =
NewBookDao(boxStore.boxFor())
@Provides @Singleton fun providesNewLanguagesDao(boxStore: BoxStore): NewLanguagesDao =
NewLanguagesDao(boxStore.boxFor())
}

View File

@ -0,0 +1,34 @@
/*
* Kiwix Android
* Copyright (C) 2018 Kiwix <android.kiwix.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.kiwix.kiwixmobile.di.modules
import dagger.Binds
import dagger.Module
import org.kiwix.kiwixmobile.downloader.DownloadManagerRequester
import org.kiwix.kiwixmobile.downloader.DownloadRequester
import org.kiwix.kiwixmobile.downloader.Downloader
import org.kiwix.kiwixmobile.downloader.DownloaderImpl
@Module
abstract class DownloaderModule {
@Binds
abstract fun bindDownloader(downloaderImpl: DownloaderImpl): Downloader
@Binds
abstract fun bindDownloaderRequester(downloaderImpl: DownloadManagerRequester): DownloadRequester
}

View File

@ -19,18 +19,16 @@ package org.kiwix.kiwixmobile.di.modules;
import android.content.Context;
import android.net.ConnectivityManager;
import dagger.Module;
import dagger.Provides;
import java.util.concurrent.TimeUnit;
import javax.inject.Singleton;
import okhttp3.OkHttpClient;
import okhttp3.logging.HttpLoggingInterceptor;
import org.kiwix.kiwixmobile.BuildConfig;
import org.kiwix.kiwixmobile.network.KiwixService;
import org.kiwix.kiwixmobile.network.UserAgentInterceptor;
import javax.inject.Singleton;
import dagger.Module;
import dagger.Provides;
import okhttp3.OkHttpClient;
import okhttp3.logging.HttpLoggingInterceptor;
@Module public class NetworkModule {
public static String KIWIX_DOWNLOAD_URL = BuildConfig.KIWIX_DOWNLOAD_URL; //"http://download.kiwix.org/";
@ -41,6 +39,8 @@ import okhttp3.logging.HttpLoggingInterceptor;
logging.setLevel(HttpLoggingInterceptor.Level.BASIC);
return new OkHttpClient().newBuilder().followRedirects(true).followSslRedirects(true)
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(10, TimeUnit.SECONDS)
.addNetworkInterceptor(logging)
.addNetworkInterceptor(new UserAgentInterceptor(useragent)).build();
}

View File

@ -0,0 +1,38 @@
package org.kiwix.kiwixmobile.di.modules
import android.arch.lifecycle.ViewModel
import android.arch.lifecycle.ViewModelProvider
import dagger.Binds
import dagger.Module
import dagger.multibindings.IntoMap
import org.kiwix.kiwixmobile.KiwixViewModelFactory
import org.kiwix.kiwixmobile.di.ViewModelKey
import org.kiwix.kiwixmobile.zim_manager.ZimManageViewModel
/*
* 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/>.
*/
@Module
abstract class ViewModelModule {
@Binds
@IntoMap
@ViewModelKey(ZimManageViewModel::class)
internal abstract fun bindUserViewModel(userViewModel: ZimManageViewModel): ViewModel
@Binds
internal abstract fun bindViewModelFactory(factory: KiwixViewModelFactory): ViewModelProvider.Factory
}

View File

@ -17,13 +17,10 @@
*/
package org.kiwix.kiwixmobile.downloader;
import org.kiwix.kiwixmobile.library.entity.LibraryNetworkEntity;
import org.kiwix.kiwixmobile.utils.StorageUtils;
import java.io.File;
import java.io.FilenameFilter;
import java.util.ArrayList;
import java.util.List;
import org.kiwix.kiwixmobile.utils.StorageUtils;
public class ChunkUtils {
@ -80,14 +77,14 @@ public class ChunkUtils {
}
}
public static long getCurrentSize(LibraryNetworkEntity.Book book) {
long size = 0;
File[] files = getAllZimParts(book.file);
for (File file : files) {
size += file.length();
}
return size;
}
//public static long getCurrentSize(LibraryNetworkEntity.Book book) {
// long size = 0;
// File[] files = getAllZimParts(book.file);
// for (File file : files) {
// size += file.length();
// }
// return size;
//}
private static File[] getAllZimParts(File file) {
final String baseName = baseNameFromParts(file);

View File

@ -0,0 +1,54 @@
/*
* 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.downloader
import android.support.v7.widget.RecyclerView
import android.view.ViewGroup
import org.kiwix.kiwixmobile.R
import org.kiwix.kiwixmobile.downloader.model.DownloadItem
import org.kiwix.kiwixmobile.extensions.inflate
class DownloadAdapter(val itemClickListener: (DownloadItem) -> Unit) : RecyclerView.Adapter<DownloadViewHolder>() {
init {
setHasStableIds(true)
}
var itemList: List<DownloadItem> = mutableListOf()
set(value) {
field = value
notifyDataSetChanged()
}
override fun getItemId(position: Int) = itemList[position].downloadId
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
) = DownloadViewHolder(parent.inflate(R.layout.download_item, false))
override fun getItemCount() = itemList.size
override fun onBindViewHolder(
holder: DownloadViewHolder,
position: Int
) {
holder.bind(itemList[position], itemClickListener)
}
}

View File

@ -1,357 +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.downloader;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.database.DataSetObserver;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.design.widget.CoordinatorLayout;
import android.support.design.widget.Snackbar;
import android.support.v4.content.ContextCompat;
import android.util.Base64;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.RelativeLayout;
import android.widget.TextView;
import org.kiwix.kiwixmobile.KiwixApplication;
import org.kiwix.kiwixmobile.KiwixMobileActivity;
import org.kiwix.kiwixmobile.R;
import org.kiwix.kiwixmobile.base.BaseFragment;
import org.kiwix.kiwixmobile.library.entity.LibraryNetworkEntity;
import org.kiwix.kiwixmobile.utils.NetworkUtils;
import org.kiwix.kiwixmobile.utils.SharedPreferenceUtil;
import org.kiwix.kiwixmobile.utils.files.FileUtils;
import org.kiwix.kiwixmobile.zim_manager.ZimManageActivity;
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.ZimFileSelectFragment;
import org.kiwix.kiwixmobile.zim_manager.library_view.LibraryFragment;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.Locale;
import javax.inject.Inject;
import static org.kiwix.kiwixmobile.downloader.ChunkUtils.getFileName;
import static org.kiwix.kiwixmobile.utils.StyleUtils.dialogStyle;
public class DownloadFragment extends BaseFragment {
public static LinkedHashMap<Integer, LibraryNetworkEntity.Book> mDownloads = new LinkedHashMap<>();
public static LinkedHashMap<Integer, String> mDownloadFiles = new LinkedHashMap<>();
public RelativeLayout relLayout;
public ListView listView;
public static DownloadAdapter downloadAdapter;
private ZimManageActivity zimManageActivity;
CoordinatorLayout mainLayout;
private Activity faActivity;
private boolean hasArtificiallyPaused;
@Inject
SharedPreferenceUtil sharedPreferenceUtil;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
faActivity = super.getActivity();
relLayout = (RelativeLayout) inflater.inflate(R.layout.download_management, container, false);
zimManageActivity = (ZimManageActivity) super.getActivity();
listView = relLayout.findViewById(R.id.zim_downloader_list);
downloadAdapter = new DownloadAdapter(mDownloads);
downloadAdapter.registerDataSetObserver(this);
listView.setAdapter(downloadAdapter);
mainLayout = faActivity.findViewById(R.id.zim_manager_main_activity);
return relLayout;
}
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
updateNoDownloads();
}
private void updateNoDownloads() {
if (faActivity == null) {
return;
}
TextView noDownloadsText = faActivity.findViewById(R.id.download_management_no_downloads);
if (noDownloadsText == null) return;
if (listView.getCount() == 0) {
noDownloadsText.setVisibility(View.VISIBLE);
} else if (listView.getCount() > 0) {
noDownloadsText.setVisibility(View.GONE);
}
}
@Override
public void onDestroy() {
super.onDestroy();
downloadAdapter.unRegisterDataSetObserver();
}
public void showNoWiFiWarning(Context context, Runnable yesAction) {
new AlertDialog.Builder(context)
.setTitle(R.string.wifi_only_title)
.setMessage(R.string.wifi_only_msg)
.setPositiveButton(R.string.yes, (dialog, i) -> {
sharedPreferenceUtil.putPrefWifiOnly(false);
KiwixMobileActivity.wifiOnly = false;
yesAction.run();
})
.setNegativeButton(R.string.no, (dialog, i) -> {})
.show();
}
public static String toHumanReadableTime(int seconds) {
final double MINUTES = 60;
final double HOURS = 60 * MINUTES;
final double DAYS = 24 * HOURS;
if (Math.round(seconds / DAYS) > 0)
return String.format(Locale.getDefault(), "%d %s %s", Math.round(seconds / DAYS),
KiwixApplication.getInstance().getResources().getString(R.string.time_day),
KiwixApplication.getInstance().getResources().getString(R.string.time_left));
if (Math.round(seconds / HOURS) > 0)
return String.format(Locale.getDefault(), "%d %s %s", Math.round(seconds / HOURS),
KiwixApplication.getInstance().getResources().getString(R.string.time_hour),
KiwixApplication.getInstance().getResources().getString(R.string.time_left));
if (Math.round(seconds / MINUTES) > 0)
return String.format(Locale.getDefault(), "%d %s %s", Math.round(seconds / MINUTES),
KiwixApplication.getInstance().getResources().getString(R.string.time_minute),
KiwixApplication.getInstance().getResources().getString(R.string.time_left));
return String.format(Locale.getDefault(), "%d %s %s", seconds,
KiwixApplication.getInstance().getResources().getString(R.string.time_second),
KiwixApplication.getInstance().getResources().getString(R.string.time_left));
}
public class DownloadAdapter extends BaseAdapter {
private LinkedHashMap<Integer, LibraryNetworkEntity.Book> mData = new LinkedHashMap<>();
private Integer[] mKeys;
private DataSetObserver dataSetObserver;
public DownloadAdapter(LinkedHashMap<Integer, LibraryNetworkEntity.Book> data) {
mData = data;
mKeys = mData.keySet().toArray(new Integer[data.size()]);
}
@Override
public int getCount() {
return mData.size();
}
@Override
public LibraryNetworkEntity.Book getItem(int position) {
return mData.get(mKeys[position]);
}
@Override
public long getItemId(int arg0) {
return arg0;
}
public void complete(int notificationID) {
if (!isAdded()) {
return;
}
int position = Arrays.asList(mKeys).indexOf(notificationID);
ViewGroup viewGroup = (ViewGroup) listView.getChildAt(position - listView.getFirstVisiblePosition());
if (viewGroup == null) {
mDownloads.remove(mKeys[position]);
mDownloadFiles.remove(mKeys[position]);
downloadAdapter.notifyDataSetChanged();
updateNoDownloads();
}
ImageView pause = viewGroup.findViewById(R.id.pause);
pause.setEnabled(false);
String fileName = getFileName(mDownloadFiles.get(mKeys[position]));
{
Snackbar completeSnack = Snackbar.make(mainLayout, getResources().getString(R.string.download_complete_snackbar), Snackbar.LENGTH_LONG);
completeSnack.setAction(getResources().getString(R.string.open), v -> zimManageActivity.finishResult(fileName)).setActionTextColor(getResources().getColor(R.color.white)).show();
}
ZimFileSelectFragment zimFileSelectFragment = (ZimFileSelectFragment) zimManageActivity.mSectionsPagerAdapter.getItem(0);
zimFileSelectFragment.addBook(fileName);
mDownloads.remove(mKeys[position]);
mDownloadFiles.remove(mKeys[position]);
downloadAdapter.notifyDataSetChanged();
updateNoDownloads();
}
public void updateProgress(int progress, int notificationID) {
if (isAdded()) {
int position = Arrays.asList(mKeys).indexOf(notificationID);
ViewGroup viewGroup = (ViewGroup) listView.getChildAt(position - listView.getFirstVisiblePosition());
if (viewGroup == null) {
return;
}
ProgressBar downloadProgress = viewGroup.findViewById(R.id.downloadProgress);
downloadProgress.setProgress(progress);
TextView timeRemaining = viewGroup.findViewById(R.id.time_remaining);
int secLeft = LibraryFragment.mService.timeRemaining.get(mKeys[position], -1);
if (secLeft != -1)
timeRemaining.setText(toHumanReadableTime(secLeft));
}
}
private void setPlayState(ImageView pauseButton, int position, int newPlayState) {
if (newPlayState == DownloadService.PLAY) { //Playing
if (LibraryFragment.mService.playDownload(mKeys[position]))
pauseButton.setImageDrawable(ContextCompat.getDrawable(getActivity(), R.drawable.ic_pause_black_24dp));
} else { //Pausing
LibraryFragment.mService.pauseDownload(mKeys[position]);
pauseButton.setImageDrawable(ContextCompat.getDrawable(getActivity(), R.drawable.ic_play_arrow_black_24dp));
}
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// Get the data item for this position
// Check if an existing view is being reused, otherwise inflate the view
if (convertView == null) {
convertView = LayoutInflater.from(faActivity).inflate(R.layout.download_item, parent, false);
}
mKeys = mData.keySet().toArray(new Integer[mData.size()]);
// Lookup view for data population
//downloadProgress.setProgress(download.progress);
// Populate the data into the template view using the data object
TextView title = convertView.findViewById(R.id.title);
TextView description = convertView.findViewById(R.id.description);
TextView timeRemaining = convertView.findViewById(R.id.time_remaining);
ImageView imageView = convertView.findViewById(R.id.favicon);
title.setText(getItem(position).getTitle());
description.setText(getItem(position).getDescription());
imageView.setImageBitmap(StringToBitMap(getItem(position).getFavicon()));
ProgressBar downloadProgress = convertView.findViewById(R.id.downloadProgress);
ImageView pause = convertView.findViewById(R.id.pause);
if (LibraryFragment.mService.downloadStatus.get(mKeys[position]) == 0) {
downloadProgress.setProgress(0);
pause.setImageDrawable(ContextCompat.getDrawable(getActivity(), R.drawable.ic_pause_black_24dp));
} else {
downloadProgress.setProgress(LibraryFragment.mService.downloadProgress.get(mKeys[position]));
if (LibraryFragment.mService.downloadStatus.get(mKeys[position]) == DownloadService.PAUSE) {
pause.setImageDrawable(ContextCompat.getDrawable(getActivity(), R.drawable.ic_play_arrow_black_24dp));
}
if (LibraryFragment.mService.downloadStatus.get(mKeys[position]) == DownloadService.PLAY) {
pause.setImageDrawable(ContextCompat.getDrawable(getActivity(), R.drawable.ic_pause_black_24dp));
}
}
pause.setOnClickListener(v -> {
int newPlayPauseState = LibraryFragment.mService.downloadStatus.get(mKeys[position]) == DownloadService.PLAY ? DownloadService.PAUSE : DownloadService.PLAY;
if (newPlayPauseState == DownloadService.PLAY && KiwixMobileActivity.wifiOnly && !NetworkUtils.isWiFi(getContext())) {
showNoWiFiWarning(getContext(), () -> {
setPlayState(pause, position, newPlayPauseState);
});
return;
}
timeRemaining.setText("");
setPlayState(pause, position, newPlayPauseState);
});
ImageView stop = convertView.findViewById(R.id.stop);
stop.setOnClickListener(v -> {
hasArtificiallyPaused = LibraryFragment.mService.downloadStatus.get(mKeys[position]) == DownloadService.PLAY;
setPlayState(pause, position, DownloadService.PAUSE);
new AlertDialog.Builder(faActivity, dialogStyle())
.setTitle(R.string.confirm_stop_download_title)
.setMessage(R.string.confirm_stop_download_msg)
.setPositiveButton(R.string.yes, (dialog, i) -> {
LibraryFragment.mService.stopDownload(mKeys[position]);
mDownloads.remove(mKeys[position]);
mDownloadFiles.remove(mKeys[position]);
downloadAdapter.notifyDataSetChanged();
updateNoDownloads();
if (zimManageActivity.mSectionsPagerAdapter.libraryFragment.libraryAdapter != null) {
zimManageActivity.mSectionsPagerAdapter.libraryFragment.libraryAdapter.getFilter().filter(((ZimManageActivity) getActivity()).searchView.getQuery());
}
})
.setNegativeButton(R.string.no, (dialog, i) -> {
if (hasArtificiallyPaused) {
hasArtificiallyPaused = false;
setPlayState(pause, position, DownloadService.PLAY);
}
})
.show();
});
// Return the completed view to render on screen
return convertView;
}
public void registerDataSetObserver(DownloadFragment downloadFragment) {
if (dataSetObserver == null) {
dataSetObserver = new DataSetObserver() {
@Override
public void onChanged() {
super.onChanged();
downloadFragment.updateNoDownloads();
}
@Override
public void onInvalidated() {
super.onInvalidated();
downloadFragment.updateNoDownloads();
}
};
registerDataSetObserver(dataSetObserver);
}
}
public void unRegisterDataSetObserver() {
if (dataSetObserver != null) {
unregisterDataSetObserver(dataSetObserver);
}
}
}
public void addDownload(int position, LibraryNetworkEntity.Book book, String fileName) {
mDownloads.put(position, book);
mDownloadFiles.put(position, fileName);
downloadAdapter.notifyDataSetChanged();
updateNoDownloads();
}
public Bitmap StringToBitMap(String encodedString) {
try {
byte[] encodeByte = Base64.decode(encodedString, Base64.DEFAULT);
return BitmapFactory.decodeByteArray(encodeByte, 0, encodeByte.length);
} catch (Exception e) {
e.getMessage();
return null;
}
}
}

View File

@ -0,0 +1,90 @@
/*
* 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.downloader
import android.arch.lifecycle.Observer
import android.arch.lifecycle.ViewModelProvider
import android.arch.lifecycle.ViewModelProviders
import android.os.Bundle
import android.support.v7.widget.LinearLayoutManager
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import kotlinx.android.synthetic.main.layout_download_management.download_management_no_downloads
import kotlinx.android.synthetic.main.layout_download_management.zim_downloader_list
import org.kiwix.kiwixmobile.base.BaseFragment
import org.kiwix.kiwixmobile.di.components.ActivityComponent
import org.kiwix.kiwixmobile.downloader.model.DownloadItem
import org.kiwix.kiwixmobile.utils.DialogShower
import org.kiwix.kiwixmobile.utils.KiwixDialog.YesNoDialog.StopDownload
import org.kiwix.kiwixmobile.utils.SharedPreferenceUtil
import org.kiwix.kiwixmobile.zim_manager.ZimManageViewModel
import javax.inject.Inject
class DownloadFragment : BaseFragment() {
@Inject lateinit var viewModelFactory: ViewModelProvider.Factory
@Inject lateinit var dialogShower: DialogShower
@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 downloadAdapter = DownloadAdapter {
dialogShower.show(StopDownload, { downloader.cancelDownload(it) })
}
override fun inject(activityComponent: ActivityComponent) {
activityComponent.inject(this)
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View =
inflater.inflate(org.kiwix.kiwixmobile.R.layout.layout_download_management, container, false)
override fun onViewCreated(
view: View,
savedInstanceState: Bundle?
) {
super.onViewCreated(view, savedInstanceState)
zim_downloader_list.run {
adapter = downloadAdapter
layoutManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)
setHasFixedSize(true)
}
zimManageViewModel.downloadItems.observe(this, Observer {
onDownloadItemsUpdate(it!!)
})
}
private fun onDownloadItemsUpdate(items: List<DownloadItem>) {
downloadAdapter.itemList = items
updateNoDownloads(items)
}
private fun updateNoDownloads(downloadItems: List<DownloadItem>) {
download_management_no_downloads.visibility =
if (downloadItems.isEmpty()) View.VISIBLE
else View.GONE
}
}

View File

@ -0,0 +1,92 @@
/*
* 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.downloader
import android.app.DownloadManager
import android.app.DownloadManager.Request
import android.net.Uri
import android.os.Build
import org.kiwix.kiwixmobile.downloader.model.DownloadItem
import org.kiwix.kiwixmobile.downloader.model.DownloadModel
import org.kiwix.kiwixmobile.downloader.model.DownloadRequest
import org.kiwix.kiwixmobile.downloader.model.DownloadStatus
import org.kiwix.kiwixmobile.extensions.forEachRow
import org.kiwix.kiwixmobile.utils.SharedPreferenceUtil
import org.kiwix.kiwixmobile.utils.StorageUtils
import java.io.File
import javax.inject.Inject
class DownloadManagerRequester @Inject constructor(
private val downloadManager: DownloadManager,
private val sharedPreferenceUtil: SharedPreferenceUtil
) : DownloadRequester {
override fun enqueue(downloadRequest: DownloadRequest) =
downloadManager.enqueue(downloadRequest.toDownloadManagerRequest(sharedPreferenceUtil))
override fun query(downloadModels: List<DownloadModel>): List<DownloadStatus> {
val downloadStatuses = mutableListOf<DownloadStatus>()
if (downloadModels.isNotEmpty()) {
downloadModels.forEach { model ->
downloadManager.query(model.toQuery()).forEachRow {
downloadStatuses.add(DownloadStatus(it, model))
}
}
}
return downloadStatuses
}
override fun cancel(downloadItem: DownloadItem) {
downloadManager.remove(downloadItem.downloadId)
}
private fun DownloadRequest.toDownloadManagerRequest(sharedPreferenceUtil: SharedPreferenceUtil) =
Request(uri).apply {
setAllowedNetworkTypes(
if (sharedPreferenceUtil.prefWifiOnly) {
Request.NETWORK_WIFI
} else {
Request.NETWORK_MOBILE or Request.NETWORK_WIFI
}
)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
setAllowedOverMetered(true)
}
setAllowedOverRoaming(true)
setTitle(title)
setDescription(description)
setDestinationUri(toDestinationUri(sharedPreferenceUtil))
setNotificationVisibility(Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
setVisibleInDownloadsUi(true)
}
private fun DownloadRequest.toDestinationUri(sharedPreferenceUtil: SharedPreferenceUtil) =
Uri.fromFile(
File(
"${sharedPreferenceUtil.prefStorage}/Kiwix/${
StorageUtils.getFileNameFromUrl(urlString)
}"
)
)
private fun DownloadModel.toQuery() =
DownloadManager.Query().setFilterById(downloadId)
}

View File

@ -0,0 +1,29 @@
/*
* 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.downloader
import org.kiwix.kiwixmobile.downloader.model.DownloadItem
import org.kiwix.kiwixmobile.downloader.model.DownloadModel
import org.kiwix.kiwixmobile.downloader.model.DownloadRequest
import org.kiwix.kiwixmobile.downloader.model.DownloadStatus
interface DownloadRequester {
fun enqueue(downloadRequest: DownloadRequest): Long
fun query(downloadModels: List<DownloadModel>): List<DownloadStatus>
fun cancel(downloadItem: DownloadItem)
}

View File

@ -35,22 +35,8 @@ import android.util.Pair;
import android.util.SparseArray;
import android.util.SparseIntArray;
import android.widget.Toast;
import org.kiwix.kiwixmobile.KiwixApplication;
import org.kiwix.kiwixmobile.KiwixMobileActivity;
import org.kiwix.kiwixmobile.R;
import org.kiwix.kiwixmobile.database.BookDao;
import org.kiwix.kiwixmobile.library.entity.LibraryNetworkEntity;
import org.kiwix.kiwixmobile.network.KiwixService;
import org.kiwix.kiwixmobile.utils.Constants;
import org.kiwix.kiwixmobile.utils.NetworkUtils;
import org.kiwix.kiwixmobile.utils.SharedPreferenceUtil;
import org.kiwix.kiwixmobile.utils.StorageUtils;
import org.kiwix.kiwixmobile.utils.TestingUtils;
import org.kiwix.kiwixmobile.utils.files.FileUtils;
import org.kiwix.kiwixmobile.zim_manager.ZimManageActivity;
import org.kiwix.kiwixmobile.zim_manager.library_view.LibraryFragment;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
@ -58,32 +44,34 @@ import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.concurrent.TimeUnit;
import javax.inject.Inject;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.functions.Action;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okio.BufferedSource;
import org.kiwix.kiwixmobile.KiwixApplication;
import org.kiwix.kiwixmobile.KiwixMobileActivity;
import org.kiwix.kiwixmobile.R;
import org.kiwix.kiwixmobile.database.newdb.dao.NewBookDao;
import org.kiwix.kiwixmobile.library.entity.LibraryNetworkEntity;
import org.kiwix.kiwixmobile.network.KiwixService;
import org.kiwix.kiwixmobile.utils.Constants;
import org.kiwix.kiwixmobile.utils.NetworkUtils;
import org.kiwix.kiwixmobile.utils.SharedPreferenceUtil;
import org.kiwix.kiwixmobile.utils.StorageUtils;
import org.kiwix.kiwixmobile.utils.TestingUtils;
import org.kiwix.kiwixmobile.zim_manager.ZimManageActivity;
import static org.kiwix.kiwixmobile.downloader.ChunkUtils.ALPHABET;
import static org.kiwix.kiwixmobile.downloader.ChunkUtils.ZIM_EXTENSION;
import static org.kiwix.kiwixmobile.downloader.ChunkUtils.completeChunk;
import static org.kiwix.kiwixmobile.downloader.ChunkUtils.completeDownload;
import static org.kiwix.kiwixmobile.downloader.ChunkUtils.completedChunk;
import static org.kiwix.kiwixmobile.downloader.ChunkUtils.deleteAllParts;
import static org.kiwix.kiwixmobile.downloader.ChunkUtils.getCurrentSize;
import static org.kiwix.kiwixmobile.downloader.ChunkUtils.initialChunk;
import static org.kiwix.kiwixmobile.downloader.ChunkUtils.isPresent;
import static org.kiwix.kiwixmobile.utils.Constants.EXTRA_BOOK;
import static org.kiwix.kiwixmobile.utils.Constants.EXTRA_LIBRARY;
import static org.kiwix.kiwixmobile.utils.Constants.EXTRA_NOTIFICATION_ID;
import static org.kiwix.kiwixmobile.utils.Constants.EXTRA_ZIM_FILE;
import static org.kiwix.kiwixmobile.utils.Constants.ONGOING_DOWNLOAD_CHANNEL_ID;
@Deprecated
public class DownloadService extends Service {
@Inject KiwixService kiwixService;
@ -118,7 +106,7 @@ public class DownloadService extends Service {
SharedPreferenceUtil sharedPreferenceUtil;
@Inject
BookDao bookDao;
NewBookDao bookDao;
public static void setDownloadFragment(DownloadFragment dFragment) {
downloadFragment = dFragment;
@ -163,7 +151,6 @@ public class DownloadService extends Service {
return START_NOT_STICKY;
}
SD_CARD = sharedPreferenceUtil.getPrefStorage();
KIWIX_ROOT = SD_CARD + "/Kiwix/";
@ -185,7 +172,7 @@ public class DownloadService extends Service {
PendingIntent pendingIntent = PendingIntent.getActivity
(getBaseContext(), notificationID,
target, PendingIntent.FLAG_CANCEL_CURRENT);
target, PendingIntent.FLAG_CANCEL_CURRENT);
Intent pauseIntent = new Intent(this, this.getClass()).setAction(ACTION_PAUSE).putExtra(NOTIFICATION_ID, notificationID);
Intent stopIntent = new Intent(this, this.getClass()).setAction(ACTION_STOP).putExtra(NOTIFICATION_ID, notificationID);
@ -195,7 +182,7 @@ public class DownloadService extends Service {
NotificationCompat.Action pause = new NotificationCompat.Action(R.drawable.ic_pause_black_24dp, getString(R.string.download_pause), pausePending);
NotificationCompat.Action stop = new NotificationCompat.Action(R.drawable.ic_stop_black_24dp, getString(R.string.download_stop), stopPending);
if(flags == START_FLAG_REDELIVERY && book.file == null) {
if (flags == START_FLAG_REDELIVERY /*&& book.file == null*/) {
return START_NOT_STICKY;
} else {
notification.put(notificationID , new NotificationCompat.Builder(this, ONGOING_DOWNLOAD_CHANNEL_ID)
@ -210,7 +197,7 @@ public class DownloadService extends Service {
notificationManager.notify(notificationID, notification.get(notificationID).build());
downloadStatus.put(notificationID, PLAY);
LibraryFragment.downloadingBooks.remove(book);
//LibraryFragment.downloadingBooks.remove(book);
String url = intent.getExtras().getString(DownloadIntent.DOWNLOAD_URL_PARAMETER);
downloadBook(url, notificationID, book);
}
@ -223,11 +210,11 @@ public class DownloadService extends Service {
synchronized (pauseLock) {
pauseLock.notify();
}
if (!DownloadFragment.mDownloads.isEmpty()) {
DownloadFragment.mDownloads.remove(notificationID);
DownloadFragment.mDownloadFiles.remove(notificationID);
DownloadFragment.downloadAdapter.notifyDataSetChanged();
}
// if (!DownloadFragment.mDownloads.isEmpty()) {
// DownloadFragment.mDownloads.remove(notificationID);
// DownloadFragment.mDownloadFiles.remove(notificationID);
// DownloadFragment.downloadAdapter.notifyDataSetChanged();
// }
updateForeground();
notificationManager.cancel(notificationID);
}
@ -263,14 +250,14 @@ public class DownloadService extends Service {
public void pauseDownload(int notificationID) {
Log.i(KIWIX_TAG, "Pausing ZIM Download for notificationID: " + notificationID);
downloadStatus.put(notificationID, PAUSE);
notification.get(notificationID).mActions.get(0).title = getString(R.string.download_play);
notification.get(notificationID).mActions.get(0).icon = R.drawable.ic_play_arrow_black_24dp;
// notification.get(notificationID).mActions.get(0).title = getString(R.string.download_play);
// notification.get(notificationID).mActions.get(0).icon = R.drawable.ic_play_arrow_black_24dp;
notification.get(notificationID).setContentText(getString(R.string.download_paused));
notificationManager.notify(notificationID, notification.get(notificationID).build());
if (DownloadFragment.downloadAdapter != null) {
DownloadFragment.downloadAdapter.notifyDataSetChanged();
downloadFragment.listView.invalidateViews();
}
// if (DownloadFragment.downloadAdapter != null) {
// DownloadFragment.downloadAdapter.notifyDataSetChanged();
// downloadFragment.listView.invalidateViews();
// }
}
public boolean playDownload(int notificationID) {
@ -279,31 +266,31 @@ public class DownloadService extends Service {
synchronized (pauseLock) {
pauseLock.notify();
}
notification.get(notificationID).mActions.get(0).title = getString(R.string.download_pause);
notification.get(notificationID).mActions.get(0).icon = R.drawable.ic_pause_black_24dp;
// notification.get(notificationID).mActions.get(0).title = getString(R.string.download_pause);
// notification.get(notificationID).mActions.get(0).icon = R.drawable.ic_pause_black_24dp;
notification.get(notificationID).setContentText("");
notificationManager.notify(notificationID, notification.get(notificationID).build());
if (DownloadFragment.downloadAdapter != null) {
DownloadFragment.downloadAdapter.notifyDataSetChanged();
downloadFragment.listView.invalidateViews();
}
// if (DownloadFragment.downloadAdapter != null) {
// DownloadFragment.downloadAdapter.notifyDataSetChanged();
// downloadFragment.listView.invalidateViews();
// }
return true;
}
private void downloadBook(String url, int notificationID, LibraryNetworkEntity.Book book) {
if (downloadFragment != null) {
downloadFragment.addDownload(notificationID, book,
KIWIX_ROOT + StorageUtils.getFileNameFromUrl(book.getUrl()));
}
//if (downloadFragment != null) {
// downloadFragment.addDownload(notificationID, book,
// KIWIX_ROOT + StorageUtils.getFileNameFromUrl(book.getUrl()));
//}
TestingUtils.bindResource(DownloadService.class);
if (book.file != null && isPresent(book.file.getPath())) {
// Calculate initial download progress
int initial = (int) (getCurrentSize(book) / (Long.valueOf(book.getSize()) * BOOK_SIZE_OFFSET));
notification.get(notificationID).setProgress(100, initial, false);
updateDownloadFragmentProgress(initial, notificationID, book);
notificationManager.notify(notificationID, notification.get(notificationID).build());
}
//if (book.file != null && isPresent(book.file.getPath())) {
// // Calculate initial download progress
// int initial = (int) (getCurrentSize(book) / (Long.valueOf(book.getSize()) * BOOK_SIZE_OFFSET));
// notification.get(notificationID).setProgress(100, initial, false);
// updateDownloadFragmentProgress(initial, notificationID, book);
// notificationManager.notify(notificationID, notification.get(notificationID).build());
//}
kiwixService.getMetaLinks(url)
.retryWhen(errors -> errors.flatMap(error -> Observable.timer(5, TimeUnit.SECONDS)))
.subscribeOn(AndroidSchedulers.mainThread())
@ -311,66 +298,67 @@ public class DownloadService extends Service {
.flatMap(pair -> Observable.fromIterable(ChunkUtils.getChunks(pair.first, pair.second, notificationID)))
.concatMap(this::downloadChunk)
.distinctUntilChanged().doOnComplete(() -> updateDownloadFragmentComplete(notificationID, book)).doOnComplete(() -> {
notification.get(notificationID).setOngoing(false);
notification.get(notificationID).setContentTitle(notificationTitle + " " + getResources().getString(R.string.zim_file_downloaded));
notification.get(notificationID).setContentText(getString(R.string.zim_file_downloaded));
final Intent target = new Intent(this, KiwixMobileActivity.class);
target.putExtra(EXTRA_ZIM_FILE, KIWIX_ROOT + StorageUtils.getFileNameFromUrl(book.getUrl()));
File filec = book.file;
completeDownload(filec);
target.putExtra(EXTRA_NOTIFICATION_ID, notificationID);
PendingIntent pendingIntent = PendingIntent.getActivity
(getBaseContext(), 0,
target, PendingIntent.FLAG_CANCEL_CURRENT);
book.downloaded = true;
bookDao.deleteBook(book.id);
notification.get(notificationID).setContentIntent(pendingIntent);
notification.get(notificationID).mActions.clear();
TestingUtils.unbindResource(DownloadService.class);
notification.get(notificationID).setProgress(100, 100, false);
notificationManager.notify(notificationID, notification.get(notificationID).build());
updateForeground();
updateDownloadFragmentProgress(100, notificationID, book);
stopSelf();
}).subscribe(progress -> {
notification.get(notificationID).setProgress(100, progress, false);
if (progress != 100 && timeRemaining.get(notificationID) != -1)
notification.get(notificationID).setContentText(DownloadFragment.toHumanReadableTime(timeRemaining.get(notificationID)));
notificationManager.notify(notificationID, notification.get(notificationID).build());
if (progress == 0 || progress == 100) {
// Tells android to not kill the service
updateForeground();
}
updateDownloadFragmentProgress(progress, notificationID, book);
}, Throwable::printStackTrace);
notification.get(notificationID).setOngoing(false);
notification.get(notificationID).setContentTitle(notificationTitle + " " + getResources().getString(R.string.zim_file_downloaded));
notification.get(notificationID).setContentText(getString(R.string.zim_file_downloaded));
final Intent target = new Intent(this, KiwixMobileActivity.class);
target.putExtra(EXTRA_ZIM_FILE, KIWIX_ROOT + StorageUtils.getFileNameFromUrl(book.getUrl()));
//File filec = book.file;
//completeDownload(filec);
target.putExtra(EXTRA_NOTIFICATION_ID, notificationID);
PendingIntent pendingIntent = PendingIntent.getActivity
(getBaseContext(), 0,
target, PendingIntent.FLAG_CANCEL_CURRENT);
//book.downloaded = true;
//bookDao.deleteBook(book.id);
notification.get(notificationID).setContentIntent(pendingIntent);
// notification.get(notificationID).mActions.clear();
TestingUtils.unbindResource(DownloadService.class);
notification.get(notificationID).setProgress(100, 100, false);
notificationManager.notify(notificationID, notification.get(notificationID).build());
updateForeground();
updateDownloadFragmentProgress(100, notificationID, book);
stopSelf();
}).subscribe(progress -> {
notification.get(notificationID).setProgress(100, progress, false);
if (progress != 100 && timeRemaining.get(notificationID) != -1) {
// notification.get(notificationID).setContentText(DownloadFragment.toHumanReadableTime(timeRemaining.get(notificationID)));
}
notificationManager.notify(notificationID, notification.get(notificationID).build());
if (progress == 0 || progress == 100) {
// Tells android to not kill the service
updateForeground();
}
updateDownloadFragmentProgress(progress, notificationID, book);
}, Throwable::printStackTrace);
}
private void updateDownloadFragmentProgress(int progress, int notificationID, LibraryNetworkEntity.Book book) {
if (DownloadFragment.mDownloads != null) {
if (DownloadFragment.mDownloads.get(notificationID) != null) {
handler.post(() -> {
if (DownloadFragment.mDownloads.get(notificationID) != null) {
DownloadFragment.downloadAdapter.updateProgress(progress, notificationID);
}
});
} else {
DownloadFragment.mDownloads.put(notificationID, book);
}
}
// if (DownloadFragment.mDownloads != null) {
// if (DownloadFragment.mDownloads.get(notificationID) != null) {
// handler.post(() -> {
// if (DownloadFragment.mDownloads.get(notificationID) != null) {
// DownloadFragment.downloadAdapter.updateProgress(progress, notificationID);
// }
// });
// } else {
// DownloadFragment.mDownloads.put(notificationID, book);
// }
// }
}
private void updateDownloadFragmentComplete(int notificationID, LibraryNetworkEntity.Book book) {
if (DownloadFragment.mDownloads != null) {
if (DownloadFragment.mDownloads.get(notificationID) != null) {
handler.post(() -> {
if (DownloadFragment.mDownloads.get(notificationID) != null) {
DownloadFragment.downloadAdapter.complete(notificationID);
}
});
} else {
DownloadFragment.mDownloads.put(notificationID, book);
}
}
// if (DownloadFragment.mDownloads != null) {
// if (DownloadFragment.mDownloads.get(notificationID) != null) {
// handler.post(() -> {
// if (DownloadFragment.mDownloads.get(notificationID) != null) {
// DownloadFragment.downloadAdapter.complete(notificationID);
// }
// });
// } else {
// DownloadFragment.mDownloads.put(notificationID, book);
// }
// }
}
private void updateForeground() {
@ -449,13 +437,13 @@ public class DownloadService extends Service {
downloaded += output.length();
if (chunk.getStartByte() == 0) {
if (!DownloadFragment.mDownloads.isEmpty()) {
LibraryNetworkEntity.Book book = DownloadFragment.mDownloads
.get(chunk.getNotificationID());
book.remoteUrl = book.getUrl();
book.file = fullFile;
bookDao.saveBook(book);
}
// if (!DownloadFragment.mDownloads.isEmpty()) {
// LibraryNetworkEntity.Book book = DownloadFragment.mDownloads
// .get(chunk.getNotificationID());
// book.remoteUrl = book.getUrl();
// book.file = fullFile;
// bookDao.saveBook(book);
// }
downloadStatus.put(chunk.getNotificationID(), PLAY);
downloadProgress.put(chunk.getNotificationID(), 0);
}
@ -499,7 +487,7 @@ public class DownloadService extends Service {
}
if (KiwixMobileActivity.wifiOnly && !NetworkUtils.isWiFi(getApplicationContext()) ||
!NetworkUtils.isNetworkAvailable(getApplicationContext())) {
!NetworkUtils.isNetworkAvailable(getApplicationContext())) {
pauseDownload(chunk.getNotificationID());
}
@ -513,7 +501,6 @@ public class DownloadService extends Service {
lastTime = System.currentTimeMillis();
lastSize = downloaded;
} catch (InterruptedException e) {
// Happens if someone interrupts your thread.
}
@ -607,5 +594,4 @@ public class DownloadService extends Service {
public IBinder onBind(Intent intent) {
return mBinder;
}
}

View File

@ -0,0 +1,85 @@
/*
* 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.downloader
import android.content.Context
import android.support.v7.widget.RecyclerView
import android.view.View
import kotlinx.android.extensions.LayoutContainer
import kotlinx.android.synthetic.main.download_item.description
import kotlinx.android.synthetic.main.download_item.downloadProgress
import kotlinx.android.synthetic.main.download_item.downloadState
import kotlinx.android.synthetic.main.download_item.favicon
import kotlinx.android.synthetic.main.download_item.stop
import kotlinx.android.synthetic.main.download_item.title
import org.kiwix.kiwixmobile.downloader.model.DownloadItem
import org.kiwix.kiwixmobile.downloader.model.DownloadState
import org.kiwix.kiwixmobile.downloader.model.DownloadState.Failed
import org.kiwix.kiwixmobile.downloader.model.DownloadState.Paused
import org.kiwix.kiwixmobile.downloader.model.DownloadState.Pending
import org.kiwix.kiwixmobile.downloader.model.DownloadState.Running
import org.kiwix.kiwixmobile.downloader.model.DownloadState.Successful
import org.kiwix.kiwixmobile.downloader.model.FailureReason.Rfc2616HttpCode
import org.kiwix.kiwixmobile.extensions.setBitmap
class DownloadViewHolder(override val containerView: View) : RecyclerView.ViewHolder(containerView),
LayoutContainer {
fun bind(
downloadItem: DownloadItem,
itemClickListener: (DownloadItem) -> Unit
) {
favicon.setBitmap(downloadItem.favIcon)
title.text = downloadItem.title
description.text = downloadItem.description
downloadProgress.progress = downloadItem.progress
stop.setOnClickListener {
itemClickListener.invoke(downloadItem)
}
downloadState.text = toReadableState(
downloadItem.downloadState, containerView.context
)
}
private fun toReadableState(
downloadState: DownloadState,
context: Context
) = when (downloadState) {
is Paused -> context.getString(
downloadState.stringId,
context.getString(downloadState.reason.stringId)
)
is Failed -> context.getString(
downloadState.stringId,
getTemplateString(downloadState, context)
)
Pending,
Running,
Successful -> context.getString(downloadState.stringId)
}
private fun getTemplateString(
downloadState: Failed,
context: Context
) = when (downloadState.reason) {
is Rfc2616HttpCode -> context.getString(
downloadState.reason.stringId,
downloadState.reason.code
)
else -> context.getString(downloadState.reason.stringId)
}
}

View File

@ -0,0 +1,29 @@
/*
* 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.downloader
import org.kiwix.kiwixmobile.downloader.model.DownloadItem
import org.kiwix.kiwixmobile.downloader.model.DownloadModel
import org.kiwix.kiwixmobile.downloader.model.DownloadStatus
import org.kiwix.kiwixmobile.library.entity.LibraryNetworkEntity
interface Downloader {
fun download(book: LibraryNetworkEntity.Book)
fun queryStatus(downloadModels: List<DownloadModel>): List<DownloadStatus>
fun cancelDownload(downloadItem: DownloadItem)
}

View File

@ -0,0 +1,61 @@
/*
* 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.downloader
import org.kiwix.kiwixmobile.database.newdb.dao.NewDownloadDao
import org.kiwix.kiwixmobile.downloader.model.DownloadItem
import org.kiwix.kiwixmobile.downloader.model.DownloadModel
import org.kiwix.kiwixmobile.downloader.model.DownloadRequest
import org.kiwix.kiwixmobile.library.entity.LibraryNetworkEntity
import org.kiwix.kiwixmobile.network.KiwixService
import javax.inject.Inject
class DownloaderImpl @Inject constructor(
private val downloadRequester: DownloadRequester,
private val downloadDao: NewDownloadDao,
private val kiwixService: KiwixService
) : Downloader {
override fun download(book: LibraryNetworkEntity.Book) {
kiwixService.getMetaLinks(book.url)
.take(1)
.subscribe(
{
if(downloadDao.doesNotAlreadyExist(book)){
val downloadId = downloadRequester.enqueue(
DownloadRequest(it, book)
)
downloadDao.insert(
DownloadModel(downloadId = downloadId, book = book)
)
}
},
Throwable::printStackTrace
)
}
override fun queryStatus(downloadModels: List<DownloadModel>) =
downloadRequester.query(downloadModels)
.sortedBy { it.downloadId }
override fun cancelDownload(downloadItem: DownloadItem) {
downloadRequester.cancel(downloadItem)
downloadDao.delete(downloadItem.downloadId)
}
}

View File

@ -0,0 +1,19 @@
package org.kiwix.kiwixmobile.downloader.model
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.util.Base64
inline class Base64String(private val encodedString: String?) {
fun toBitmap(): Bitmap? = try {
encodedString?.let { nonNullString ->
Base64.decode(nonNullString, Base64.DEFAULT)
.let {
BitmapFactory.decodeByteArray(it, 0, it.size)
}
}
} catch (illegalArgumentException: IllegalArgumentException) {
null
}
}

View File

@ -0,0 +1,17 @@
package org.kiwix.kiwixmobile.downloader.model
import org.kiwix.kiwixmobile.database.newdb.entities.BookOnDiskEntity
import org.kiwix.kiwixmobile.library.entity.LibraryNetworkEntity.Book
import java.io.File
data class BookOnDisk(
val databaseId: Long? = null,
val book: Book = Book().apply { id = "" },
val file: File = File("")
) {
constructor(bookOnDiskEntity: BookOnDiskEntity) : this(
bookOnDiskEntity.id,
bookOnDiskEntity.toBook(),
bookOnDiskEntity.file
)
}

View File

@ -0,0 +1,40 @@
/*
* 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.downloader.model
data class DownloadItem(
val downloadId: Long,
val favIcon: Base64String,
val title: String,
val description: String,
val bytesDownloaded: Long,
val totalSizeBytes: Long,
val downloadState: DownloadState
) {
val progress get() = ((bytesDownloaded.toFloat() / totalSizeBytes) * 100).toInt()
constructor(downloadStatus: DownloadStatus) : this(
downloadStatus.downloadId,
Base64String(downloadStatus.book.favicon),
downloadStatus.title,
downloadStatus.description,
downloadStatus.bytesDownloadedSoFar,
downloadStatus.totalSizeBytes,
downloadStatus.state
)
}

View File

@ -15,16 +15,13 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.kiwix.kiwixmobile.zim_manager.fileselect_view;
package org.kiwix.kiwixmobile.downloader.model
import org.kiwix.kiwixmobile.base.ViewCallback;
import org.kiwix.kiwixmobile.library.entity.LibraryNetworkEntity;
import org.kiwix.kiwixmobile.library.entity.LibraryNetworkEntity
import org.kiwix.kiwixmobile.library.entity.LibraryNetworkEntity.Book
import java.util.ArrayList;
/**
* Created by EladKeyshawn on 06/04/2017.
*/
public interface ZimFileSelectViewCallback extends ViewCallback {
void showFiles(ArrayList<LibraryNetworkEntity.Book> books);
}
data class DownloadModel(
val databaseId: Long? = null,
val downloadId: Long = 0,
val book: LibraryNetworkEntity.Book = Book().apply { id = "" }
)

View File

@ -15,36 +15,26 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.kiwix.kiwixmobile.zim_manager.fileselect_view;
package org.kiwix.kiwixmobile.downloader.model
import org.kiwix.kiwixmobile.base.BasePresenter;
import org.kiwix.kiwixmobile.database.BookDao;
import org.kiwix.kiwixmobile.library.entity.LibraryNetworkEntity;
import android.net.Uri
import org.kiwix.kiwixmobile.library.entity.LibraryNetworkEntity
import org.kiwix.kiwixmobile.library.entity.MetaLinkNetworkEntity
import java.util.ArrayList;
data class DownloadRequest(
val urlString: String,
val title: String,
val description: String
) {
import javax.inject.Inject;
/**
* Created by EladKeyshawn on 06/04/2017.
*/
public class ZimFileSelectPresenter extends BasePresenter<ZimFileSelectViewCallback> {
@Inject
BookDao bookDao;
@Inject
public ZimFileSelectPresenter() {
}
@Override
public void attachView(ZimFileSelectViewCallback mvpView) {
super.attachView(mvpView);
}
public void loadLocalZimFileFromDb() {
ArrayList<LibraryNetworkEntity.Book> books = bookDao.getBooks();
getMvpView().showFiles(books);
}
val uri get() = Uri.parse(urlString)
constructor(
metaLinkNetworkEntity: MetaLinkNetworkEntity,
book: LibraryNetworkEntity.Book
) : this(
metaLinkNetworkEntity.relevantUrl.value,
book.title,
book.description
)
}

View File

@ -0,0 +1,169 @@
/*
* 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.downloader.model
import android.app.DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR
import android.app.DownloadManager.COLUMN_DESCRIPTION
import android.app.DownloadManager.COLUMN_ID
import android.app.DownloadManager.COLUMN_LAST_MODIFIED_TIMESTAMP
import android.app.DownloadManager.COLUMN_LOCAL_URI
import android.app.DownloadManager.COLUMN_MEDIAPROVIDER_URI
import android.app.DownloadManager.COLUMN_MEDIA_TYPE
import android.app.DownloadManager.COLUMN_REASON
import android.app.DownloadManager.COLUMN_STATUS
import android.app.DownloadManager.COLUMN_TITLE
import android.app.DownloadManager.COLUMN_TOTAL_SIZE_BYTES
import android.app.DownloadManager.COLUMN_URI
import android.app.DownloadManager.ERROR_CANNOT_RESUME
import android.app.DownloadManager.ERROR_DEVICE_NOT_FOUND
import android.app.DownloadManager.ERROR_FILE_ALREADY_EXISTS
import android.app.DownloadManager.ERROR_FILE_ERROR
import android.app.DownloadManager.ERROR_HTTP_DATA_ERROR
import android.app.DownloadManager.ERROR_INSUFFICIENT_SPACE
import android.app.DownloadManager.ERROR_TOO_MANY_REDIRECTS
import android.app.DownloadManager.ERROR_UNHANDLED_HTTP_CODE
import android.app.DownloadManager.ERROR_UNKNOWN
import android.app.DownloadManager.PAUSED_QUEUED_FOR_WIFI
import android.app.DownloadManager.PAUSED_UNKNOWN
import android.app.DownloadManager.PAUSED_WAITING_FOR_NETWORK
import android.app.DownloadManager.PAUSED_WAITING_TO_RETRY
import android.app.DownloadManager.STATUS_FAILED
import android.app.DownloadManager.STATUS_PAUSED
import android.app.DownloadManager.STATUS_PENDING
import android.app.DownloadManager.STATUS_RUNNING
import android.app.DownloadManager.STATUS_SUCCESSFUL
import android.database.Cursor
import android.net.Uri
import org.kiwix.kiwixmobile.R
import org.kiwix.kiwixmobile.extensions.get
import org.kiwix.kiwixmobile.library.entity.LibraryNetworkEntity.Book
import java.io.File
class DownloadStatus(
val downloadId: Long = 0L,
val title: String = "",
val description: String = "",
val state: DownloadState = DownloadState.Pending,
val bytesDownloadedSoFar: Long = 0,
val totalSizeBytes: Long = 0,
val lastModified: String = "",
val localUri: String? = null,
val mediaProviderUri: String? = null,
val mediaType: String? = null,
val uri: String? = null,
val book: Book = Book().apply { id = "" }
) {
fun toBookOnDisk(uriToFileConverter:UriToFileConverter) =
BookOnDisk(book = book, file = uriToFileConverter.convert(localUri))
constructor(
cursor: Cursor,
downloadModel: DownloadModel
) : this(
cursor[COLUMN_ID],
cursor[COLUMN_TITLE],
cursor[COLUMN_DESCRIPTION],
DownloadState.from(cursor[COLUMN_STATUS], cursor[COLUMN_REASON]),
cursor[COLUMN_BYTES_DOWNLOADED_SO_FAR],
cursor[COLUMN_TOTAL_SIZE_BYTES],
cursor[COLUMN_LAST_MODIFIED_TIMESTAMP],
cursor[COLUMN_LOCAL_URI],
cursor[COLUMN_MEDIAPROVIDER_URI],
cursor[COLUMN_MEDIA_TYPE],
cursor[COLUMN_URI],
downloadModel.book
)
}
interface UriToFileConverter {
fun convert(uriString: String?) = File(Uri.parse(uriString).path)
class Impl:UriToFileConverter{}
}
sealed class DownloadState(val stringId: Int) {
companion object {
fun from(
status: Int,
reason: Int
) = when (status) {
STATUS_PAUSED -> Paused(PausedReason.from(reason))
STATUS_FAILED -> Failed(FailureReason.from(reason))
STATUS_PENDING -> Pending
STATUS_RUNNING -> Running
STATUS_SUCCESSFUL -> Successful
else -> throw RuntimeException("invalid status $status")
}
}
data class Paused(val reason: PausedReason) : DownloadState(R.string.paused_state)
data class Failed(val reason: FailureReason) : DownloadState(R.string.failed_state)
object Pending : DownloadState(R.string.pending_state)
object Running : DownloadState(R.string.running_state)
object Successful : DownloadState(R.string.successful_state)
override fun toString(): String {
return javaClass.simpleName
}
}
sealed class FailureReason(val stringId: Int) {
companion object {
fun from(reason: Int) = when (reason) {
in 100..505 -> Rfc2616HttpCode(reason)
ERROR_CANNOT_RESUME -> CannotResume
ERROR_DEVICE_NOT_FOUND -> StorageNotFound
ERROR_FILE_ALREADY_EXISTS -> FileAlreadyExists
ERROR_FILE_ERROR -> UnknownFileError
ERROR_HTTP_DATA_ERROR -> HttpError
ERROR_INSUFFICIENT_SPACE -> InsufficientSpace
ERROR_TOO_MANY_REDIRECTS -> TooManyRedirects
ERROR_UNHANDLED_HTTP_CODE -> UnhandledHttpCode
ERROR_UNKNOWN -> Unknown
else -> Unknown
}
}
object CannotResume : FailureReason(R.string.failed_cannot_resume)
object StorageNotFound : FailureReason(R.string.failed_storage_not_found)
object FileAlreadyExists : FailureReason(R.string.failed_file_already_exists)
object UnknownFileError : FailureReason(R.string.failed_unknown_file_error)
object HttpError : FailureReason(R.string.failed_http_error)
object InsufficientSpace : FailureReason(R.string.failed_insufficient_space)
object TooManyRedirects : FailureReason(R.string.failed_too_many_redirects)
object UnhandledHttpCode : FailureReason(R.string.failed_unhandled_http_code)
object Unknown : FailureReason(R.string.failed_unknown)
data class Rfc2616HttpCode(val code: Int) : FailureReason(R.string.failed_http_code)
}
sealed class PausedReason(val stringId: Int) {
companion object {
fun from(reason: Int) = when (reason) {
PAUSED_QUEUED_FOR_WIFI -> WaitingForWifi
PAUSED_WAITING_FOR_NETWORK -> WaitingForConnectivity
PAUSED_WAITING_TO_RETRY -> WaitingForRetry
PAUSED_UNKNOWN -> Unknown
else -> Unknown
}
}
object WaitingForWifi : PausedReason(R.string.paused_wifi)
object WaitingForConnectivity : PausedReason(R.string.paused_connectivity)
object WaitingForRetry : PausedReason(R.string.paused_retry)
object Unknown : PausedReason(R.string.paused_unknown)
}

View File

@ -0,0 +1,36 @@
package org.kiwix.kiwixmobile.downloader.model
import org.kiwix.kiwixmobile.KiwixApplication
import java.util.Locale
inline class Seconds(private val seconds: Int) {
fun toHumanReadableTime(): String {
val MINUTES = 60.0
val HOURS = 60 * MINUTES
val DAYS = 24 * HOURS
val context = KiwixApplication.getInstance()
return when {
Math.round(seconds / DAYS) > 0 -> String.format(
Locale.getDefault(), "%d %s %s", Math.round(seconds / DAYS),
context.getString(org.kiwix.kiwixmobile.R.string.time_day),
context.getString(org.kiwix.kiwixmobile.R.string.time_left)
)
Math.round(seconds / HOURS) > 0 -> String.format(
Locale.getDefault(), "%d %s %s", Math.round(seconds / HOURS),
context.getString(org.kiwix.kiwixmobile.R.string.time_hour),
context.getString(org.kiwix.kiwixmobile.R.string.time_left)
)
(Math.round(seconds / MINUTES) > 0) -> String.format(
Locale.getDefault(), "%d %s %s", Math.round(seconds / MINUTES),
context.getString(org.kiwix.kiwixmobile.R.string.time_minute),
context.getString(org.kiwix.kiwixmobile.R.string.time_left)
)
else -> String.format(
Locale.getDefault(), "%d %s %s", seconds,
context.getString(org.kiwix.kiwixmobile.R.string.time_second),
context.getString(org.kiwix.kiwixmobile.R.string.time_left)
)
}
}
}

Some files were not shown because too many files have changed in this diff Show More