Merged kiwix-android code into main repo.
59
AndroidManifest.xml
Normal file
@ -0,0 +1,59 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:versionCode="1"
|
||||
android:versionName="1.0" package="org.kiwix.kiwixmobile">
|
||||
<supports-screens
|
||||
android:largeScreens="true"
|
||||
android:normalScreens="true"
|
||||
android:smallScreens="true"
|
||||
android:resizeable="true"
|
||||
android:anyDensity="true"
|
||||
/>
|
||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" />
|
||||
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.GET_ACCOUNTS" />
|
||||
<uses-sdk android:minSdkVersion="11" android:targetSdkVersion="17"/>
|
||||
<application android:icon="@drawable/kiwix_icon" android:label="@string/app_name" android:allowBackup="true">
|
||||
<activity android:name=".KiwixMobileActivity"
|
||||
android:label="@string/app_name" android:configChanges="orientation|keyboardHidden">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<data android:scheme="file" />
|
||||
<data android:host="*" />
|
||||
<data android:pathPattern=".*\\.zim.*" />
|
||||
<data android:pathPattern=".*\\..*\\.zim.*"/>
|
||||
<data android:pathPattern=".*\\..*\\..*\\.zim.*"/>
|
||||
<data android:pathPattern=".*\\..*\\..*\\..*\\.zim.*"/>
|
||||
<data android:mimeType="*/*" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<provider
|
||||
android:name=".ZimContentProvider"
|
||||
android:authorities="org.kiwix.zim"
|
||||
android:exported="false"/>
|
||||
|
||||
<activity
|
||||
android:name=".ZimFileSelectActivity"
|
||||
android:icon="@drawable/kiwix_icon"
|
||||
android:label="@string/choose_file" >
|
||||
<!-- TODO -->
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.GET_CONTENT" />
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.OPENABLE" />
|
||||
|
||||
<data android:mimeType="*/*" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
</manifest>
|
BIN
Kiwix_icon_transparent_512x512.png
Normal file
After Width: | Height: | Size: 85 KiB |
BIN
Kiwix_icon_transparent_600x600.png
Normal file
After Width: | Height: | Size: 42 KiB |
@ -18,7 +18,7 @@ COMPILE_LIBLZMA = True
|
||||
COMPILE_LIBZIM = True
|
||||
COMPILE_LIBKIWIX = True
|
||||
STRIP_LIBKIWIX = True
|
||||
COMPILE_APK = False
|
||||
COMPILE_APK = True
|
||||
|
||||
# store the OS's environment PATH as we'll mess with it
|
||||
# ORIGINAL_ENVIRON_PATH = os.environ.get('PATH')
|
||||
@ -45,6 +45,7 @@ ARCHS_SHORT_NAMES = {
|
||||
# store host machine name
|
||||
UNAME = check_output(['uname', '-s']).strip()
|
||||
UARCH = check_output(['uname', '-m']).strip()
|
||||
SYSTEMS = {'Linux': 'linux', 'Darwin': 'mac'}
|
||||
|
||||
# compiler version to use
|
||||
# list of available toolchains in <NDK_PATH>/toolchains
|
||||
@ -56,6 +57,10 @@ NDK_PATH = os.environ.get('NDK_PATH',
|
||||
os.path.join(os.path.dirname(CURRENT_PATH),
|
||||
'src', 'dependencies',
|
||||
'android-ndk-r8e'))
|
||||
SDK_PATH = os.environ.get('ANDROID_HOME',
|
||||
os.path.join(os.path.dirname(CURRENT_PATH),
|
||||
'src', 'dependencies',
|
||||
'android-sdk', 'sdk'))
|
||||
|
||||
# Target Android EABI/version to compile for.
|
||||
# list of available platforms in <NDK_PATH>/platforms
|
||||
@ -184,8 +189,8 @@ for arch in ARCHS:
|
||||
'orig': ORIGINAL_ENVIRON['PATH'],
|
||||
'arch_full': arch_full,
|
||||
'gccver': COMPILER_VERSION}),
|
||||
'CFLAGS': ' -fPIC '
|
||||
}
|
||||
'CFLAGS': ' -fPIC ',
|
||||
'ANDROID_HOME': SDK_PATH}
|
||||
change_env(new_environ)
|
||||
change_env(OPTIMIZATION_ENV)
|
||||
|
||||
@ -264,9 +269,9 @@ for arch in ARCHS:
|
||||
os.remove(src.replace('.cpp', '.o'))
|
||||
|
||||
# compile JNI header
|
||||
os.chdir(os.path.join(curdir, 'org', 'kiwix', 'kiwixmobile'))
|
||||
os.chdir(os.path.join(curdir, 'src', 'org', 'kiwix', 'kiwixmobile'))
|
||||
syscall('javac JNIKiwix.java')
|
||||
os.chdir(curdir)
|
||||
os.chdir(os.path.join(curdir, 'src'))
|
||||
syscall('javah -jni org.kiwix.kiwixmobile.JNIKiwix')
|
||||
|
||||
# create libkiwix.so
|
||||
@ -284,7 +289,9 @@ for arch in ARCHS:
|
||||
+ platform_includes
|
||||
+ [LIBKIWIX_SRC,
|
||||
os.path.join(LIBZIM_SRC,
|
||||
'include')])
|
||||
'include'),
|
||||
os.path.join(curdir,
|
||||
'src')])
|
||||
})
|
||||
|
||||
link_cmd = ('g++ -fPIC -shared -B%(platform)s/sysroot '
|
||||
@ -297,10 +304,12 @@ for arch in ARCHS:
|
||||
'/libs/%(arch_short)s/libgnustl_static.a '
|
||||
'-llog -landroid -lstdc++ -lc '
|
||||
'%(platform)s/lib/gcc/%(arch_full)s/%(gccver)s/libgcc.a '
|
||||
'-o %(platform)s/lib/libkiwix.so'
|
||||
'-o %(curdir)s/libs/%(arch_short)s/libkiwix.so'
|
||||
% {'kwsrc': LIBKIWIX_SRC,
|
||||
'platform': platform,
|
||||
'arch_full': arch_full,
|
||||
'arch_short': arch_short,
|
||||
'curdir': curdir,
|
||||
'gccver': COMPILER_VERSION,
|
||||
'NDK_PATH': NDK_PATH,
|
||||
'arch_short': arch_short})
|
||||
@ -309,14 +318,17 @@ for arch in ARCHS:
|
||||
syscall(compile_cmd)
|
||||
syscall(link_cmd)
|
||||
|
||||
for obj in ('kiwix.o', 'reader.o', 'stringTools.o'):
|
||||
for obj in ('kiwix.o', 'reader.o', 'stringTools.o',
|
||||
'src/org_kiwix_kiwixmobile_JNIKiwix.h'):
|
||||
os.remove(obj)
|
||||
|
||||
if STRIP_LIBKIWIX:
|
||||
syscall('%(platform)s/%(arch_full)s/bin/strip '
|
||||
'%(platform)s/lib/libkiwix.so'
|
||||
'%(curdir)s/libs/%(arch_short)s/libkiwix.so'
|
||||
% {'platform': platform,
|
||||
'arch_full': arch_full})
|
||||
'arch_full': arch_full,
|
||||
'arch_short': arch_short,
|
||||
'curdir': curdir})
|
||||
|
||||
os.chdir(curdir)
|
||||
change_env(ORIGINAL_ENVIRON)
|
92
build.xml
Normal file
@ -0,0 +1,92 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project name="Kiwix" default="help">
|
||||
|
||||
<!-- The local.properties file is created and updated by the 'android' tool.
|
||||
It contains the path to the SDK. It should *NOT* be checked into
|
||||
Version Control Systems. -->
|
||||
<property file="local.properties" />
|
||||
|
||||
<!-- The ant.properties file can be created by you. It is only edited by the
|
||||
'android' tool to add properties to it.
|
||||
This is the place to change some Ant specific build properties.
|
||||
Here are some properties you may want to change/update:
|
||||
|
||||
source.dir
|
||||
The name of the source directory. Default is 'src'.
|
||||
out.dir
|
||||
The name of the output directory. Default is 'bin'.
|
||||
|
||||
For other overridable properties, look at the beginning of the rules
|
||||
files in the SDK, at tools/ant/build.xml
|
||||
|
||||
Properties related to the SDK location or the project target should
|
||||
be updated using the 'android' tool with the 'update' action.
|
||||
|
||||
This file is an integral part of the build system for your
|
||||
application and should be checked into Version Control Systems.
|
||||
|
||||
-->
|
||||
<property file="ant.properties" />
|
||||
|
||||
<!-- if sdk.dir was not set from one of the property file, then
|
||||
get it from the ANDROID_HOME env var.
|
||||
This must be done before we load project.properties since
|
||||
the proguard config can use sdk.dir -->
|
||||
<property environment="env" />
|
||||
<condition property="sdk.dir" value="${env.ANDROID_HOME}">
|
||||
<isset property="env.ANDROID_HOME" />
|
||||
</condition>
|
||||
|
||||
<!-- The project.properties file is created and updated by the 'android'
|
||||
tool, as well as ADT.
|
||||
|
||||
This contains project specific properties such as project target, and library
|
||||
dependencies. Lower level build properties are stored in ant.properties
|
||||
(or in .classpath for Eclipse projects).
|
||||
|
||||
This file is an integral part of the build system for your
|
||||
application and should be checked into Version Control Systems. -->
|
||||
<loadproperties srcFile="project.properties" />
|
||||
|
||||
<!-- quick check on sdk.dir -->
|
||||
<fail
|
||||
message="sdk.dir is missing. Make sure to generate local.properties using 'android update project' or to inject it through the ANDROID_HOME environment variable."
|
||||
unless="sdk.dir"
|
||||
/>
|
||||
|
||||
<!--
|
||||
Import per project custom build rules if present at the root of the project.
|
||||
This is the place to put custom intermediary targets such as:
|
||||
-pre-build
|
||||
-pre-compile
|
||||
-post-compile (This is typically used for code obfuscation.
|
||||
Compiled code location: ${out.classes.absolute.dir}
|
||||
If this is not done in place, override ${out.dex.input.absolute.dir})
|
||||
-post-package
|
||||
-post-build
|
||||
-pre-clean
|
||||
-->
|
||||
<import file="custom_rules.xml" optional="true" />
|
||||
|
||||
<!-- Import the actual build file.
|
||||
|
||||
To customize existing targets, there are two options:
|
||||
- Customize only one target:
|
||||
- copy/paste the target into this file, *before* the
|
||||
<import> task.
|
||||
- customize it to your needs.
|
||||
- Customize the whole content of build.xml
|
||||
- copy/paste the content of the rules files (minus the top node)
|
||||
into this file, replacing the <import> task.
|
||||
- customize to your needs.
|
||||
|
||||
***********************
|
||||
****** IMPORTANT ******
|
||||
***********************
|
||||
In all cases you must update the value of version-tag below to read 'custom' instead of an integer,
|
||||
in order to avoid having your file be overridden by tools such as "android update project"
|
||||
-->
|
||||
<!-- version-tag: 1 -->
|
||||
<import file="${sdk.dir}/tools/ant/build.xml" />
|
||||
|
||||
</project>
|
26
create-signed-android-release.sh
Executable file
@ -0,0 +1,26 @@
|
||||
#!/bin/bash
|
||||
|
||||
if [ -f "$1" ];
|
||||
then
|
||||
CERTIFICATE=$1
|
||||
else
|
||||
echo "Usage: $0 Kiwix-android.keystore"
|
||||
echo "You must specify the path of the certificate keystore."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
function die {
|
||||
echo -n "[ERROR] "
|
||||
echo -n $1
|
||||
echo -n " Aborting.
|
||||
"
|
||||
exit 1
|
||||
}
|
||||
|
||||
ant release || die "ant release error."
|
||||
jarsigner -verbose -sigalg MD5withRSA -digestalg SHA1 -keystore $CERTIFICATE bin/Kiwix-release-unsigned.apk kiwix || die "Error signing the package."
|
||||
jarsigner -verify bin/Kiwix-release-unsigned.apk || die "The package is not properly signed."
|
||||
zipalign -f -v 4 bin/Kiwix-release-unsigned.apk bin/kiwix-android.apk || die "Could not zipalign the signed package. Please check."
|
||||
|
||||
echo "[SUCCESS] Your signed release package is ready:"
|
||||
ls -lh bin/kiwix-android.apk
|
36
proguard.cfg
Normal file
@ -0,0 +1,36 @@
|
||||
-optimizationpasses 5
|
||||
-dontusemixedcaseclassnames
|
||||
-dontskipnonpubliclibraryclasses
|
||||
-dontpreverify
|
||||
-verbose
|
||||
-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*
|
||||
|
||||
-keep public class * extends android.app.Activity
|
||||
-keep public class * extends android.app.Application
|
||||
-keep public class * extends android.app.Service
|
||||
-keep public class * extends android.content.BroadcastReceiver
|
||||
-keep public class * extends android.content.ContentProvider
|
||||
-keep public class * extends android.app.backup.BackupAgentHelper
|
||||
-keep public class * extends android.preference.Preference
|
||||
-keep public class com.android.vending.licensing.ILicensingService
|
||||
|
||||
-keepclasseswithmembernames class * {
|
||||
native <methods>;
|
||||
}
|
||||
|
||||
-keepclasseswithmembernames class * {
|
||||
public <init>(android.content.Context, android.util.AttributeSet);
|
||||
}
|
||||
|
||||
-keepclasseswithmembernames class * {
|
||||
public <init>(android.content.Context, android.util.AttributeSet, int);
|
||||
}
|
||||
|
||||
-keepclassmembers enum * {
|
||||
public static **[] values();
|
||||
public static ** valueOf(java.lang.String);
|
||||
}
|
||||
|
||||
-keep class * implements android.os.Parcelable {
|
||||
public static final android.os.Parcelable$Creator *;
|
||||
}
|
14
project.properties
Normal file
@ -0,0 +1,14 @@
|
||||
# This file is automatically generated by Android Tools.
|
||||
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
|
||||
#
|
||||
# This file must be checked in Version Control Systems.
|
||||
#
|
||||
# To customize properties used by the Ant build system edit
|
||||
# "ant.properties", and override values to adapt the script to your
|
||||
# project structure.
|
||||
#
|
||||
# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
|
||||
#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
|
||||
|
||||
# Project target.
|
||||
target=android-14
|
BIN
res/drawable-hdpi/action_help.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
BIN
res/drawable-hdpi/action_search.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
res/drawable-hdpi/device_access_sd_storage.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
res/drawable-hdpi/icon.png
Normal file
After Width: | Height: | Size: 4.0 KiB |
BIN
res/drawable-hdpi/kiwix_icon.png
Normal file
After Width: | Height: | Size: 4.5 KiB |
BIN
res/drawable-hdpi/navigation_back.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
res/drawable-hdpi/navigation_forward.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
res/drawable-ldpi/icon.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
res/drawable-ldpi/kiwix_icon.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
res/drawable-mdpi/action_help.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
res/drawable-mdpi/action_search.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
res/drawable-mdpi/device_access_sd_storage.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
res/drawable-mdpi/icon.png
Normal file
After Width: | Height: | Size: 2.5 KiB |
BIN
res/drawable-mdpi/kiwix_icon.png
Normal file
After Width: | Height: | Size: 2.5 KiB |
BIN
res/drawable-mdpi/navigation_back.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
res/drawable-mdpi/navigation_forward.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
res/drawable-xhdpi/action_help.png
Normal file
After Width: | Height: | Size: 1.8 KiB |
BIN
res/drawable-xhdpi/action_search.png
Normal file
After Width: | Height: | Size: 2.1 KiB |
BIN
res/drawable-xhdpi/device_access_sd_storage.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
res/drawable-xhdpi/kiwix_icon.png
Normal file
After Width: | Height: | Size: 6.8 KiB |
BIN
res/drawable-xhdpi/navigation_back.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
res/drawable-xhdpi/navigation_forward.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
29
res/layout/main.xml
Normal file
@ -0,0 +1,29 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/MainLayout"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:orientation="vertical" >
|
||||
|
||||
<AutoCompleteTextView
|
||||
android:id="@+id/articleSearchTextView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:ems="10"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:inputType="text|textCapWords"
|
||||
android:imeOptions="actionGo"
|
||||
android:text=""
|
||||
android:hint="@string/articlesearch_hint"
|
||||
android:completionThreshold="1" >
|
||||
</AutoCompleteTextView>
|
||||
|
||||
<WebView
|
||||
android:id="@+id/webview"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent">
|
||||
<requestFocus />
|
||||
</WebView>
|
||||
|
||||
</LinearLayout>
|
8
res/layout/zimfilelist.xml
Normal file
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ListView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/zimfilelist"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" >
|
||||
|
||||
|
||||
</ListView>
|
17
res/layout/zimfilelistentry.xml
Normal file
@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content" >
|
||||
<TextView
|
||||
android:id="@+id/zim_file_list_entry_path"
|
||||
style="@android:style/TextAppearance.Large"
|
||||
android:paddingBottom="15dp"
|
||||
android:paddingTop="15dp"
|
||||
android:paddingLeft="10dp"
|
||||
android:paddingRight="10dp"
|
||||
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
/>
|
||||
</LinearLayout>
|
47
res/menu/main.xml
Normal file
@ -0,0 +1,47 @@
|
||||
<!--
|
||||
Copyright 2011 The Android Open Source Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:id="@+id/menu_help"
|
||||
android:title="@string/menu_help"
|
||||
android:icon="@drawable/action_help"
|
||||
android:orderInCategory="0"
|
||||
android:showAsAction="always" />
|
||||
<item android:id="@+id/menu_openfile"
|
||||
android:title="@string/menu_openfile"
|
||||
android:icon="@drawable/device_access_sd_storage"
|
||||
android:orderInCategory="0"
|
||||
android:showAsAction="always|withText" />
|
||||
<item android:id="@+id/menu_search"
|
||||
android:icon="@drawable/action_search"
|
||||
android:title="@string/menu_search"
|
||||
android:orderInCategory="0"
|
||||
android:showAsAction="ifRoom" />
|
||||
<item android:id="@+id/menu_back"
|
||||
android:title="@string/menu_back"
|
||||
android:icon="@drawable/navigation_back"
|
||||
android:orderInCategory="0"
|
||||
android:showAsAction="always" />
|
||||
|
||||
<item android:id="@+id/menu_forward"
|
||||
android:title="@string/menu_forward"
|
||||
android:icon="@drawable/navigation_forward"
|
||||
android:orderInCategory="0"
|
||||
android:showAsAction="always" />
|
||||
|
||||
|
||||
|
||||
</menu>
|
7
res/raw-de/welcome.html
Normal file
@ -0,0 +1,7 @@
|
||||
<html><body>
|
||||
<h1>Willkommen zu Kiwix</h1>
|
||||
|
||||
Visit <a href="http://www.kiwix.org">Kiwix<img src="kiwix_icon.png"/></a> to find out how
|
||||
to download zim files, such as the Wikipedia.
|
||||
|
||||
</body></html>
|
BIN
res/raw/kiwix_icon.png
Normal file
After Width: | Height: | Size: 6.8 KiB |
7
res/raw/welcome.html
Normal file
@ -0,0 +1,7 @@
|
||||
<html><body>
|
||||
<h1>Welcome to Kiwix</h1>
|
||||
|
||||
Visit <a href="http://www.kiwix.org">Kiwix<img src="kiwix_icon.png"/></a> to find out how
|
||||
to download zim files, such as the Wikipedia.
|
||||
|
||||
</body></html>
|
13
res/values-de/strings.xml
Normal file
@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_name">Kiwix</string>
|
||||
<string-array name="articleSearchSuggestionsTrial">
|
||||
<item>Paris</item>
|
||||
<item>Wikipedia</item>
|
||||
<item>Seine</item>
|
||||
</string-array>
|
||||
<string name="menu_openfile">Datei öffnen</string>
|
||||
<string name="menu_forward">Vorwärts</string>
|
||||
<string name="menu_back">Zurück</string>
|
||||
<string name="choose_file">Datei auswählen</string>
|
||||
</resources>
|
19
res/values/strings.xml
Normal file
@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_name">Kiwix</string>
|
||||
<string-array name="articleSearchSuggestionsTrial">
|
||||
<item>Paris</item>
|
||||
<item>Wikipedia</item>
|
||||
<item>Seine</item>
|
||||
</string-array>
|
||||
<string name="menu_openfile">Open File</string>
|
||||
<string name="menu_help">Help</string>
|
||||
<string name="menu_forward">Forward</string>
|
||||
<string name="menu_back">Back</string>
|
||||
<string name="menu_search">Find in text</string>
|
||||
<string name="articlesearch_hint">Type to search article</string>
|
||||
<string name="choose_file">Choose (*.zim) File</string>
|
||||
<string name="error_filenotfound">Error: The selected zim file could not be found.</string>
|
||||
<string name="error_fileinvalid">Error: The selected file is not a valid zim file.</string>
|
||||
<string name="error_articlenotfound">Error: Loading article (Url: %1$s) failed.</string>
|
||||
</resources>
|
25
src/org/kiwix/kiwixmobile/JNIKiwix.java
Normal file
@ -0,0 +1,25 @@
|
||||
package org.kiwix.kiwixmobile;
|
||||
public class JNIKiwix {
|
||||
public native String getMainPage();
|
||||
public native boolean loadZIM(String path);
|
||||
public native byte[] getContent(String url, JNIKiwixString mimeType, JNIKiwixInt size);
|
||||
public native boolean searchSuggestions(String prefix, int count);
|
||||
public native boolean getNextSuggestion(JNIKiwixString title);
|
||||
public native boolean getPageUrlFromTitle(String title, JNIKiwixString url);
|
||||
|
||||
static {
|
||||
System.loadLibrary("kiwix");
|
||||
}
|
||||
}
|
||||
|
||||
class JNIKiwixString {
|
||||
String value;
|
||||
}
|
||||
|
||||
class JNIKiwixInt {
|
||||
int value;
|
||||
}
|
||||
|
||||
class JNIKiwixBool {
|
||||
boolean value;
|
||||
}
|
385
src/org/kiwix/kiwixmobile/KiwixMobileActivity.java
Normal file
@ -0,0 +1,385 @@
|
||||
package org.kiwix.kiwixmobile;
|
||||
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
import android.util.Log;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.Window;
|
||||
import android.webkit.WebBackForwardList;
|
||||
import android.webkit.WebChromeClient;
|
||||
import android.webkit.WebSettings;
|
||||
import android.webkit.WebView;
|
||||
import android.webkit.WebViewClient;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.AutoCompleteTextView;
|
||||
import android.widget.TextView;
|
||||
import android.widget.TextView.OnEditorActionListener;
|
||||
import android.widget.Toast;
|
||||
|
||||
|
||||
public class KiwixMobileActivity extends Activity {
|
||||
/** Called when the activity is first created. */
|
||||
|
||||
private WebView webView;
|
||||
private ArrayAdapter<String> adapter;
|
||||
protected boolean requestClearHistoryAfterLoad;
|
||||
private static final int ZIMFILESELECT_REQUEST_CODE = 1234;
|
||||
private static final String PREFS_KIWIX_MOBILE = "kiwix-mobile";
|
||||
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
requestClearHistoryAfterLoad=false;
|
||||
|
||||
|
||||
this.requestWindowFeature(Window.FEATURE_PROGRESS);
|
||||
this.setProgressBarVisibility(true);
|
||||
|
||||
setContentView(R.layout.main);
|
||||
webView = (WebView) findViewById(R.id.webview);
|
||||
|
||||
// Get a reference to the AutoCompleteTextView in the layout
|
||||
AutoCompleteTextView articleSearchtextView = (AutoCompleteTextView) findViewById(R.id.articleSearchTextView);
|
||||
// Get the string array
|
||||
//TODO Implement db backend
|
||||
ArrayList<String> countries = new ArrayList<String>(Arrays.asList(getResources().getStringArray(R.array.articleSearchSuggestionsTrial)));
|
||||
// Create the adapter and set it to the AutoCompleteTextView
|
||||
adapter =
|
||||
new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, countries);
|
||||
articleSearchtextView.setAdapter(adapter);
|
||||
articleSearchtextView.setOnEditorActionListener(new OnEditorActionListener() {
|
||||
|
||||
@Override
|
||||
public boolean onEditorAction(TextView v, int actionId,
|
||||
KeyEvent event) {
|
||||
//Do Stuff
|
||||
Log.d("zimgap", v+" onEditorAction. "+v.getText());
|
||||
// To close softkeyboard
|
||||
String articleUrl = ZimContentProvider.getPageUrlFromTitle(v.getText().toString());
|
||||
Log.d("zimgap", v+" onEditorAction. TextView: "+v.getText()+ " articleUrl: "+articleUrl);
|
||||
|
||||
if (articleUrl!=null) {
|
||||
webView.requestFocus();
|
||||
webView.loadUrl(Uri.parse(ZimContentProvider.CONTENT_URI
|
||||
+articleUrl).toString());
|
||||
return true;
|
||||
} else {
|
||||
//FIXME Toast.makeText(this, "Article not found.", Toast.LENGTH_SHORT).show(); //FIXME resource string
|
||||
|
||||
return true;
|
||||
}
|
||||
}});
|
||||
articleSearchtextView.addTextChangedListener(new TextWatcher()
|
||||
{
|
||||
public void afterTextChanged(Editable s)
|
||||
{
|
||||
// Abstract Method of TextWatcher Interface.
|
||||
}
|
||||
public void beforeTextChanged(CharSequence s,
|
||||
int start, int count, int after)
|
||||
{
|
||||
// Abstract Method of TextWatcher Interface.
|
||||
}
|
||||
public void onTextChanged(CharSequence s,
|
||||
int start, int before, int count)
|
||||
{
|
||||
AutoCompleteTextView articleSearchtextView = (AutoCompleteTextView) findViewById(R.id.articleSearchTextView);
|
||||
Log.d("zimgap", "Adapter:"+adapter.getCount());
|
||||
adapter.clear();
|
||||
ZimContentProvider.searchSuggestions(s.toString(), 20);
|
||||
String suggestion;
|
||||
while ((suggestion = ZimContentProvider.getNextSuggestion())!=null) {
|
||||
adapter.add(suggestion);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// js includes will not happen unless we enable JS
|
||||
webView.getSettings().setJavaScriptEnabled(true);
|
||||
//Does not seem to have impact. (Idea was that
|
||||
// web page is rendered before loading all pictures)
|
||||
//webView.getSettings().setRenderPriority(RenderPriority.HIGH);
|
||||
final Activity activity = this;
|
||||
|
||||
webView.setWebChromeClient(new WebChromeClient(){
|
||||
|
||||
public void onProgressChanged(WebView view, int progress) {
|
||||
activity.setProgress(progress * 100);
|
||||
if (progress==100) {
|
||||
|
||||
Log.d("zimgap", "Loading article finished.");
|
||||
if (requestClearHistoryAfterLoad) {
|
||||
Log.d("zimgap", "Loading article finished and requestClearHistoryAfterLoad -> clearHistory");
|
||||
webView.clearHistory();
|
||||
requestClearHistoryAfterLoad=false;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Should basically resemble the behavior when setWebClient not done
|
||||
// (i.p. internal urls load in webview, external urls in browser)
|
||||
// as currently no custom setWebViewClient required it is commented
|
||||
webView.setWebViewClient(new WebViewClient() {
|
||||
|
||||
@Override
|
||||
public boolean shouldOverrideUrlLoading(WebView view, String url) {
|
||||
if (url.startsWith(ZimContentProvider.CONTENT_URI.toString())) {
|
||||
// This is my web site, so do not override; let my WebView load the page
|
||||
return false;
|
||||
}
|
||||
// Otherwise, the link is not for a page on my site, so launch another Activity that handles URLs
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
|
||||
startActivity(intent);
|
||||
return true;
|
||||
}
|
||||
|
||||
public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
|
||||
String errorString = String.format(getResources().getString(R.string.error_articlenotfound), failingUrl);
|
||||
//TODO apparently screws up back/forward
|
||||
webView.loadDataWithBaseURL("file://error","<html><body>"+errorString+"</body></html>", "text/html", "utf-8", failingUrl);
|
||||
}
|
||||
});
|
||||
|
||||
//Pinch to zoom
|
||||
webView.getSettings().setBuiltInZoomControls(true);
|
||||
//webView.getSettings().setLoadsImagesAutomatically(false);
|
||||
//Does not make much sense to cache data from zim files.(Not clear whether
|
||||
// this actually has any effect)
|
||||
webView.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE);
|
||||
//Workaround to avoid that default zoom is very small. TODO check cause
|
||||
// and find better solution (e.g. may only be issue on tablets, etc...)
|
||||
webView.getSettings().setDefaultZoom(WebSettings.ZoomDensity.CLOSE);
|
||||
if (getIntent().getData()!=null) {
|
||||
String filePath = getIntent().getData().getEncodedPath();
|
||||
Log.d("zimgap", " Kiwix started from a filemanager. Intent filePath: "+filePath+" -> open this zimfile and load main page");
|
||||
openZimFile(new File(filePath), false);
|
||||
|
||||
} else if (savedInstanceState!=null) {
|
||||
Log.d("zimgap", " Kiwix started with a savedInstanceState (That is was closed by OS) -> restore webview state and zimfile (if set)");
|
||||
if (savedInstanceState.getString("currentzimfile")!=null) {
|
||||
openZimFile(new File(savedInstanceState.getString("currentzimfile")), false);
|
||||
|
||||
}
|
||||
// Restore the state of the WebView
|
||||
|
||||
webView.restoreState(savedInstanceState);
|
||||
} else {
|
||||
SharedPreferences settings = getSharedPreferences(PREFS_KIWIX_MOBILE, 0);
|
||||
String zimfile = settings.getString("currentzimfile", null);
|
||||
if (zimfile != null) {
|
||||
Log.d("zimgap", " Kiwix normal start, zimfile loaded last time -> Open last used zimfile "+zimfile);
|
||||
openZimFile(new File(zimfile), false);
|
||||
// Alternative would be to restore webView state. But more effort to implement, and actually
|
||||
// fits better normal android behavior if after closing app ("back" button) state is not maintained.
|
||||
} else {
|
||||
Log.d("zimgap", " Kiwix normal start, no zimfile loaded last time -> display welcome page");
|
||||
showHelp();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
SharedPreferences settings = getSharedPreferences(PREFS_KIWIX_MOBILE, 0);
|
||||
SharedPreferences.Editor editor = settings.edit();
|
||||
editor.putString("currentzimfile", ZimContentProvider.getZimFile());
|
||||
// Commit the edits!
|
||||
editor.commit();
|
||||
|
||||
Log.d("zimgap", "onPause Save currentzimfile to preferences:"+ZimContentProvider.getZimFile());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSaveInstanceState(Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
// Save the state of the WebView
|
||||
|
||||
webView.saveState(outState);
|
||||
outState.putString("currentzimfile", ZimContentProvider.getZimFile());
|
||||
Log.v("zimgap", "onSaveInstanceState Save currentzimfile to bundle:"+ZimContentProvider.getZimFile()+" and webView state");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onRestoreInstanceState(Bundle savedInstanceState) {
|
||||
super.onRestoreInstanceState(savedInstanceState);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
MenuInflater inflater = getMenuInflater();
|
||||
inflater.inflate(R.menu.main, menu);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case android.R.id.home:
|
||||
Toast.makeText(this, "Tapped home", Toast.LENGTH_SHORT).show();
|
||||
break;
|
||||
case R.id.menu_search:
|
||||
webView.showFindDialog("", true);
|
||||
break;
|
||||
case R.id.menu_forward:
|
||||
if(webView.canGoForward() == true){
|
||||
webView.goForward();
|
||||
}
|
||||
break;
|
||||
case R.id.menu_back:
|
||||
if(webView.canGoBack() == true){
|
||||
webView.goBack();
|
||||
}
|
||||
break;
|
||||
case R.id.menu_help:
|
||||
showHelp();
|
||||
break;
|
||||
case R.id.menu_openfile:
|
||||
final Intent target = new Intent(Intent.ACTION_GET_CONTENT);
|
||||
// The MIME data type filter
|
||||
target.setType("*/*");
|
||||
// Only return URIs that can be opened with ContentResolver
|
||||
target.addCategory(Intent.CATEGORY_OPENABLE);
|
||||
//Force use of our file selection component.
|
||||
// (Note may make sense to just define a custom intent instead)
|
||||
target.setComponent(new ComponentName(getPackageName(), getPackageName()+".ZimFileSelectActivity"));
|
||||
try {
|
||||
startActivityForResult(target, ZIMFILESELECT_REQUEST_CODE);
|
||||
} catch (ActivityNotFoundException e) {
|
||||
|
||||
}break;
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
private String readTextFromResource(int resourceID)
|
||||
{
|
||||
InputStream raw = getResources().openRawResource(resourceID);
|
||||
ByteArrayOutputStream stream = new ByteArrayOutputStream();
|
||||
int i;
|
||||
try
|
||||
{
|
||||
i = raw.read();
|
||||
while (i != -1)
|
||||
{
|
||||
stream.write(i);
|
||||
i = raw.read();
|
||||
}
|
||||
raw.close();
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
return stream.toString();
|
||||
}
|
||||
|
||||
private void showHelp() {
|
||||
//Load from resource. Use with base url as else no images can be embedded.
|
||||
webView.loadDataWithBaseURL("file:///android_res/raw/", readTextFromResource(R.raw.welcome), "text/html", "utf-8", null);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
switch (requestCode) {
|
||||
case ZIMFILESELECT_REQUEST_CODE:
|
||||
if (resultCode == RESULT_OK) {
|
||||
// The URI of the selected file
|
||||
final Uri uri = data.getData();
|
||||
File file = null;
|
||||
if (uri != null) {
|
||||
String path = uri.getPath();
|
||||
if (path != null)
|
||||
file = new File(path);
|
||||
}
|
||||
if (file==null)
|
||||
return;
|
||||
// Create a File from this Uri
|
||||
openZimFile(file, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private boolean openZimFile(File file, boolean clearHistory) {
|
||||
if (file.exists()) {
|
||||
if (ZimContentProvider.setZimFile(file.getAbsolutePath())!=null) {
|
||||
//Apparently with webView.clearHistory() only
|
||||
// history before currently (fully) loaded page is cleared
|
||||
// -> request clear, actual clear done after load.
|
||||
// Probably not working in all corners (e.g. zim file openend
|
||||
// while load in progress, mainpage of new zim file invalid, ...
|
||||
// but should be good enough.
|
||||
// Actually probably redundant if no zim file openend before in session,
|
||||
// but to be on save side don't clear history in such cases.
|
||||
if (clearHistory)
|
||||
requestClearHistoryAfterLoad=true;
|
||||
loadMainPage();
|
||||
return true;
|
||||
} else {
|
||||
Toast.makeText(this, getResources().getString(R.string.error_fileinvalid), Toast.LENGTH_LONG).show();
|
||||
}
|
||||
|
||||
} else {
|
||||
Toast.makeText(this, getResources().getString(R.string.error_filenotfound), Toast.LENGTH_LONG).show();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void loadMainPage() {
|
||||
String article = ZimContentProvider.getMainPage();
|
||||
webView.loadUrl(Uri.parse(ZimContentProvider.CONTENT_URI
|
||||
+ article).toString());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean onKeyDown(int keyCode, KeyEvent event) {
|
||||
if(event.getAction() == KeyEvent.ACTION_DOWN){
|
||||
switch(keyCode)
|
||||
{
|
||||
case KeyEvent.KEYCODE_BACK:
|
||||
if(webView.canGoBack() == true){
|
||||
/*WebBackForwardList history = webView.copyBackForwardList();
|
||||
|
||||
if (history.getCurrentIndex() )*/
|
||||
|
||||
webView.goBack();
|
||||
}else{
|
||||
finish();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
return super.onKeyDown(keyCode, event);
|
||||
}
|
||||
}
|
229
src/org/kiwix/kiwixmobile/ZimContentProvider.java
Normal file
@ -0,0 +1,229 @@
|
||||
package org.kiwix.kiwixmobile;
|
||||
|
||||
/***
|
||||
Copyright (c) 2012 CommonsWare, LLC
|
||||
Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
use this file except in compliance with the License. You may obtain a copy
|
||||
of the License at http://www.apache.org/licenses/LICENSE-2.0. Unless required
|
||||
by applicable law or agreed to in writing, software distributed under the
|
||||
License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
|
||||
OF ANY KIND, either express or implied. See the License for the specific
|
||||
language governing permissions and limitations under the License.
|
||||
|
||||
From _The Busy Coder's Guide to Android Development_
|
||||
http://commonsware.com/Android
|
||||
*/
|
||||
|
||||
import android.content.ContentProvider;
|
||||
import android.content.ContentValues;
|
||||
import android.content.res.AssetManager;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.os.ParcelFileDescriptor.AutoCloseOutputStream;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.HashMap;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
public class ZimContentProvider extends ContentProvider {
|
||||
public static final Uri CONTENT_URI = Uri.parse("content://org.kiwix.zim/");
|
||||
private static final HashMap<String, String> MIME_TYPES = new HashMap<String, String>();
|
||||
|
||||
static {
|
||||
MIME_TYPES.put(".html", "text/html");
|
||||
}
|
||||
private static String zimFileName;
|
||||
private static JNIKiwix jniKiwix;
|
||||
|
||||
public synchronized static String setZimFile(String fileName) {
|
||||
if (!jniKiwix.loadZIM(fileName)) {
|
||||
Log.e("zimgap", "Unable to open the file " + fileName);
|
||||
zimFileName = null;
|
||||
} else {
|
||||
zimFileName = fileName;
|
||||
}
|
||||
return zimFileName;
|
||||
}
|
||||
|
||||
public static String getZimFile() {
|
||||
return zimFileName;
|
||||
}
|
||||
|
||||
public static String getMainPage() {
|
||||
if (jniKiwix==null)
|
||||
return null;
|
||||
else {
|
||||
return jniKiwix.getMainPage();
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean searchSuggestions(String prefix, int count) {
|
||||
if (jniKiwix==null)
|
||||
return false;
|
||||
else {
|
||||
return jniKiwix.searchSuggestions(prefix, count);
|
||||
}
|
||||
}
|
||||
|
||||
public static String getNextSuggestion() {
|
||||
if (jniKiwix==null)
|
||||
return null;
|
||||
else {
|
||||
JNIKiwixString title=new JNIKiwixString();
|
||||
if (jniKiwix.getNextSuggestion(title)) {
|
||||
return title.value;
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static String getPageUrlFromTitle(String title) {
|
||||
if (jniKiwix==null)
|
||||
return null;
|
||||
else {
|
||||
JNIKiwixString url=new JNIKiwixString();
|
||||
if (jniKiwix.getPageUrlFromTitle(title, url)) {
|
||||
return url.value;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreate() {
|
||||
jniKiwix = new JNIKiwix();
|
||||
|
||||
return (true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getType(Uri uri) {
|
||||
String path = uri.toString();
|
||||
|
||||
for (String extension : MIME_TYPES.keySet()) {
|
||||
if (path.endsWith(extension)) {
|
||||
return (MIME_TYPES.get(extension));
|
||||
}
|
||||
}
|
||||
|
||||
return (null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ParcelFileDescriptor openFile(Uri uri, String mode)
|
||||
throws FileNotFoundException {
|
||||
ParcelFileDescriptor[] pipe = null;
|
||||
|
||||
try {
|
||||
pipe = ParcelFileDescriptor.createPipe();
|
||||
new TransferThread(jniKiwix, uri, new AutoCloseOutputStream(
|
||||
pipe[1])).start();
|
||||
} catch (IOException e) {
|
||||
Log.e(getClass().getSimpleName(), "Exception opening pipe", e);
|
||||
throw new FileNotFoundException("Could not open pipe for: "
|
||||
+ uri.toString());
|
||||
}
|
||||
|
||||
return (pipe[0]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cursor query(Uri url, String[] projection, String selection,
|
||||
String[] selectionArgs, String sort) {
|
||||
throw new RuntimeException("Operation not supported");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Uri insert(Uri uri, ContentValues initialValues) {
|
||||
throw new RuntimeException("Operation not supported");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int update(Uri uri, ContentValues values, String where,
|
||||
String[] whereArgs) {
|
||||
throw new RuntimeException("Operation not supported");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int delete(Uri uri, String where, String[] whereArgs) {
|
||||
throw new RuntimeException("Operation not supported");
|
||||
}
|
||||
|
||||
static class TransferThread extends Thread {
|
||||
|
||||
Uri articleUri;
|
||||
String articleZimUrl;
|
||||
OutputStream out;
|
||||
JNIKiwix jniKiwix;
|
||||
|
||||
TransferThread(JNIKiwix jniKiwix, Uri articleUri, OutputStream out) throws IOException {
|
||||
this.articleUri = articleUri;
|
||||
this.jniKiwix = jniKiwix;
|
||||
Log.d("zimgap",
|
||||
"Retrieving :"
|
||||
+ articleUri.toString());
|
||||
String t = articleUri.toString();
|
||||
int pos = articleUri.toString().indexOf(CONTENT_URI.toString());
|
||||
if (pos != -1)
|
||||
t = articleUri.toString().substring(
|
||||
CONTENT_URI.toString().length());
|
||||
this.out = out;
|
||||
this.articleZimUrl = t;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
byte[] buf = new byte[1024];
|
||||
int len;
|
||||
|
||||
try {
|
||||
JNIKiwixString mime = new JNIKiwixString();
|
||||
JNIKiwixInt size = new JNIKiwixInt();
|
||||
byte[] data = jniKiwix.getContent(articleZimUrl, mime, size);
|
||||
// Log.d("zimgap","articleDataByteArray:"+articleDataByteArray.toString());
|
||||
// ByteArrayInputStream articleDataInputStream = new
|
||||
// ByteArrayInputStream(articleDataByteArray.toByteArray());
|
||||
// Log.d("zimgap","article data loaded from zime file");
|
||||
|
||||
//ByteArrayInputStream articleDataInputStream = new ByteArrayInputStream(
|
||||
// articleDataByteArray.toByteArray());
|
||||
ByteArrayInputStream articleDataInputStream = new ByteArrayInputStream(data);
|
||||
while ((len = articleDataInputStream.read(buf)) > 0) {
|
||||
out.write(buf, 0, len);
|
||||
}
|
||||
|
||||
articleDataInputStream.close();
|
||||
out.flush();
|
||||
|
||||
Log.d("zimgap", "reading " + articleZimUrl
|
||||
+ " finished.");
|
||||
} catch (IOException e) {
|
||||
Log.e(getClass().getSimpleName(), "Exception reading article "
|
||||
+ articleZimUrl + " from zim file", e);
|
||||
} catch (NullPointerException e) {
|
||||
Log.e(getClass().getSimpleName(), "Exception reading article "
|
||||
+ articleZimUrl + " from zim file", e);
|
||||
|
||||
} finally {
|
||||
try {
|
||||
out.close();
|
||||
} catch (IOException e) {
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
141
src/org/kiwix/kiwixmobile/ZimFileSelectActivity.java
Normal file
@ -0,0 +1,141 @@
|
||||
package org.kiwix.kiwixmobile;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.content.Loader;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.provider.MediaStore;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.Adapter;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.ListView;
|
||||
import android.widget.AdapterView.OnItemClickListener;
|
||||
import android.widget.SimpleCursorAdapter;
|
||||
//TODO API level 11 (honeycomb). use compatiblity packages instead
|
||||
import android.content.CursorLoader;
|
||||
import android.app.LoaderManager;
|
||||
|
||||
public class ZimFileSelectActivity extends Activity implements
|
||||
LoaderManager.LoaderCallbacks<Cursor> {
|
||||
|
||||
private static final int LOADER_ID = 0x02;
|
||||
private SimpleCursorAdapter mCursorAdapter;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.zimfilelist);
|
||||
selectZimFile();
|
||||
|
||||
|
||||
}
|
||||
|
||||
private void finishResult(String path) {
|
||||
if (path != null) {
|
||||
File file = new File(path);
|
||||
Uri uri = Uri.fromFile(file);
|
||||
setResult(RESULT_OK, new Intent().setData(uri));
|
||||
finish();
|
||||
} else {
|
||||
setResult(RESULT_CANCELED);
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
protected void selectZimFile() {
|
||||
// Defines a list of columns to retrieve from the Cursor and load into an output row
|
||||
String[] mZimListColumns =
|
||||
{
|
||||
MediaStore.Images.Media.DATA
|
||||
};
|
||||
|
||||
// Defines a list of View IDs that will receive the Cursor columns for each row
|
||||
int[] mZimListItems = { R.id.zim_file_list_entry_path};
|
||||
|
||||
mCursorAdapter = new SimpleCursorAdapter(
|
||||
getApplicationContext(), // The application's Context object
|
||||
R.layout.zimfilelistentry, // A layout in XML for one row in the ListView
|
||||
null, // The cursor, swapped later by cursorloader
|
||||
mZimListColumns, // A string array of column names in the cursor
|
||||
mZimListItems, // An integer array of view IDs in the row layout
|
||||
Adapter.NO_SELECTION);
|
||||
|
||||
// Sets the adapter for the ListView
|
||||
setContentView(R.layout.zimfilelist);
|
||||
|
||||
|
||||
ListView zimFileList = (ListView) findViewById(R.id.zimfilelist);
|
||||
getLoaderManager().initLoader(LOADER_ID, null, this);
|
||||
|
||||
zimFileList.setAdapter(mCursorAdapter);
|
||||
zimFileList.setOnItemClickListener(new OnItemClickListener() {
|
||||
public void onItemClick(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
|
||||
// TODO Auto-generated method stub
|
||||
onListItemClick((ListView) arg0, arg0, arg2, arg3);
|
||||
}
|
||||
});
|
||||
//TODO close cursor when done
|
||||
//allNonMediaFiles.close();
|
||||
}
|
||||
|
||||
|
||||
private void onListItemClick(AdapterView<?> adapter, View view, int position, long arg) {
|
||||
// TODO Auto-generated method stub
|
||||
Log.d("zimgap", " zimFileList.onItemClick");
|
||||
|
||||
ListView zimFileList = (ListView) findViewById(R.id.zimfilelist);
|
||||
Cursor mycursor = (Cursor) zimFileList.getItemAtPosition(position);
|
||||
//TODO not very clean
|
||||
finishResult(mycursor.getString(1));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Loader<Cursor> onCreateLoader(int i, Bundle bundle) {
|
||||
//TODO leads to API min 11
|
||||
Uri uri = MediaStore.Files.getContentUri("external");
|
||||
|
||||
String[] projection = {
|
||||
MediaStore.Images.Media._ID,
|
||||
MediaStore.Images.Media.DATA, //Path
|
||||
};
|
||||
|
||||
// exclude media files, they would be here also (perhaps
|
||||
// somewhat better performance), and filter for zim files
|
||||
// (normal and first split)
|
||||
String selection = MediaStore.Files.FileColumns.MEDIA_TYPE + "="
|
||||
+ MediaStore.Files.FileColumns.MEDIA_TYPE_NONE + " AND "
|
||||
+ " ( LOWER(" +
|
||||
MediaStore.Images.Media.DATA + ") LIKE '%.zim'"
|
||||
+ " OR LOWER(" +
|
||||
MediaStore.Images.Media.DATA + ") LIKE '%.zimaa'"
|
||||
+" ) ";
|
||||
|
||||
|
||||
String[] selectionArgs = null; // there is no ? in selection so null here
|
||||
|
||||
|
||||
String sortOrder = MediaStore.Images.Media.DATA; // unordered
|
||||
Log.d("zimgap", " Performing query for zim files...");
|
||||
|
||||
|
||||
return new CursorLoader(this, uri, projection, selection, selectionArgs, sortOrder);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadFinished(Loader<Cursor> cursorLoader, Cursor cursor) {
|
||||
Log.d("zimgap", " DONE query zim files");
|
||||
mCursorAdapter.swapCursor(cursor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoaderReset(Loader<Cursor> cursorLoader) {
|
||||
mCursorAdapter.swapCursor(null);
|
||||
}
|
||||
|
||||
}
|