├── settings.gradle
├── .idea
├── copyright
│ └── profiles_settings.xml
├── vcs.xml
├── modules.xml
├── runConfigurations.xml
├── compiler.xml
├── gradle.xml
└── misc.xml
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── comicViewer
├── lint.xml
├── src
│ └── main
│ │ ├── res
│ │ ├── drawable
│ │ │ ├── icon.png
│ │ │ ├── ic_menu_dice.png
│ │ │ ├── ic_menu_star.png
│ │ │ ├── icon_512x512.png
│ │ │ ├── ic_menu_heart.png
│ │ │ ├── ic_menu_archive.png
│ │ │ └── ic_menu_refresh.png
│ │ ├── values
│ │ │ ├── archivemenu_strings.xml
│ │ │ └── mainmenu_strings.xml
│ │ ├── menu
│ │ │ ├── archive.xml
│ │ │ └── mainmenu.xml
│ │ ├── layout
│ │ │ ├── about.xml
│ │ │ ├── search_dlg.xml
│ │ │ ├── archive_item_layout.xml
│ │ │ └── main.xml
│ │ └── xml
│ │ │ └── prefs.xml
│ │ ├── AndroidManifest.xml
│ │ └── java
│ │ └── net
│ │ └── bytten
│ │ └── comicviewer
│ │ ├── IComicInfo.java
│ │ ├── IComicDefinition.java
│ │ ├── IComicProvider.java
│ │ ├── SettingsActivity.java
│ │ ├── VersionHacks.java
│ │ ├── ArchiveData.java
│ │ ├── BookmarksHelper.java
│ │ ├── HackedWebView.java
│ │ ├── Utility.java
│ │ ├── ArchiveActivity.java
│ │ └── ComicViewerActivity.java
└── build.gradle
├── xkcdViewer
├── src
│ └── main
│ │ ├── res
│ │ ├── drawable
│ │ │ ├── icon.png
│ │ │ └── icon_512x512.png
│ │ └── values
│ │ │ └── strings.xml
│ │ ├── java
│ │ └── net
│ │ │ └── bytten
│ │ │ └── xkcdviewer
│ │ │ ├── XkcdArchiveActivity.java
│ │ │ ├── XkcdComicInfo.java
│ │ │ ├── XkcdViewerActivity.java
│ │ │ ├── XkcdComicDefinition.java
│ │ │ └── XkcdComicProvider.java
│ │ └── AndroidManifest.xml
├── build.gradle
└── lint.xml
├── .gitignore
├── gradlew.bat
└── gradlew
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':comicViewer'
2 | include ':xkcdViewer'
3 |
--------------------------------------------------------------------------------
/.idea/copyright/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tcoxon/XkcdViewer/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/comicViewer/lint.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/comicViewer/src/main/res/drawable/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tcoxon/XkcdViewer/HEAD/comicViewer/src/main/res/drawable/icon.png
--------------------------------------------------------------------------------
/xkcdViewer/src/main/res/drawable/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tcoxon/XkcdViewer/HEAD/xkcdViewer/src/main/res/drawable/icon.png
--------------------------------------------------------------------------------
/comicViewer/src/main/res/drawable/ic_menu_dice.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tcoxon/XkcdViewer/HEAD/comicViewer/src/main/res/drawable/ic_menu_dice.png
--------------------------------------------------------------------------------
/comicViewer/src/main/res/drawable/ic_menu_star.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tcoxon/XkcdViewer/HEAD/comicViewer/src/main/res/drawable/ic_menu_star.png
--------------------------------------------------------------------------------
/comicViewer/src/main/res/drawable/icon_512x512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tcoxon/XkcdViewer/HEAD/comicViewer/src/main/res/drawable/icon_512x512.png
--------------------------------------------------------------------------------
/xkcdViewer/src/main/res/drawable/icon_512x512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tcoxon/XkcdViewer/HEAD/xkcdViewer/src/main/res/drawable/icon_512x512.png
--------------------------------------------------------------------------------
/comicViewer/src/main/res/drawable/ic_menu_heart.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tcoxon/XkcdViewer/HEAD/comicViewer/src/main/res/drawable/ic_menu_heart.png
--------------------------------------------------------------------------------
/comicViewer/src/main/res/drawable/ic_menu_archive.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tcoxon/XkcdViewer/HEAD/comicViewer/src/main/res/drawable/ic_menu_archive.png
--------------------------------------------------------------------------------
/comicViewer/src/main/res/drawable/ic_menu_refresh.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tcoxon/XkcdViewer/HEAD/comicViewer/src/main/res/drawable/ic_menu_refresh.png
--------------------------------------------------------------------------------
/comicViewer/src/main/res/values/archivemenu_strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Refresh
4 |
5 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.apk
2 | *.ap_
3 | *.dex
4 | *.class
5 | bin/
6 | gen/
7 | local.properties
8 | Thumbs.db
9 | .DS_Store
10 | *.sw*
11 | .classpath
12 | .project
13 | *.iml
14 | .idea/workspace.xml
15 | .idea/tasks.xml
16 | .gradle
17 | build/
18 | obj/
19 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Mon Dec 28 10:00:20 PST 2015
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip
7 |
--------------------------------------------------------------------------------
/comicViewer/src/main/res/menu/archive.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
--------------------------------------------------------------------------------
/comicViewer/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/comicViewer/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | android {
4 | compileSdkVersion 12
5 | buildToolsVersion "25.0.2"
6 |
7 | defaultConfig {
8 | minSdkVersion 3
9 | targetSdkVersion 11
10 | }
11 |
12 | buildTypes {
13 | release {
14 | minifyEnabled false
15 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/comicViewer/src/main/res/layout/about.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
11 |
12 |
--------------------------------------------------------------------------------
/comicViewer/src/main/java/net/bytten/comicviewer/IComicInfo.java:
--------------------------------------------------------------------------------
1 | package net.bytten.comicviewer;
2 |
3 | import android.net.Uri;
4 |
5 | public interface IComicInfo {
6 | public Uri getImage();
7 | public String getTitle();
8 | public String getAlt();
9 | public Uri getLink();
10 | public String getId();
11 | public boolean isBookmarked();
12 | public void setBookmarked(boolean b);
13 | public String getNextId();
14 | public String getPrevId();
15 | public String getUrl();
16 | }
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/.idea/runConfigurations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
11 |
12 |
--------------------------------------------------------------------------------
/comicViewer/src/main/res/layout/search_dlg.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
13 |
14 |
--------------------------------------------------------------------------------
/xkcdViewer/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 10
5 | buildToolsVersion "25.0.2"
6 |
7 | defaultConfig {
8 | applicationId "net.bytten.xkcdviewer"
9 | minSdkVersion 3
10 | targetSdkVersion 11
11 | }
12 |
13 | buildTypes {
14 | release {
15 | minifyEnabled false
16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
17 | }
18 | }
19 | compileOptions {
20 | targetCompatibility JavaVersion.VERSION_1_6
21 | }
22 | }
23 |
24 | dependencies {
25 | compile project(':comicViewer')
26 | }
27 |
--------------------------------------------------------------------------------
/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/comicViewer/src/main/res/layout/archive_item_layout.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
13 |
19 |
20 |
--------------------------------------------------------------------------------
/comicViewer/src/main/java/net/bytten/comicviewer/IComicDefinition.java:
--------------------------------------------------------------------------------
1 | package net.bytten.comicviewer;
2 |
3 | import android.net.Uri;
4 |
5 | public interface IComicDefinition {
6 |
7 | public String getComicTitle();
8 | public String getAuthorName();
9 | public Uri getArchiveUrl();
10 | public String getPackageName();
11 | public boolean hasAltText();
12 | public boolean isComicUrl(Uri url);
13 | public boolean isArchiveUrl(Uri url);
14 | public boolean isHomeUrl(Uri url);
15 | public String getAuthorLinkText();
16 | public Uri getAuthorLinkUrl();
17 | public String getComicTitleAbbrev();
18 | public boolean idsAreNumbers();
19 | public IComicProvider getProvider();
20 | public Uri getDonateUrl();
21 | public Uri getDeveloperUrl();
22 |
23 | }
24 |
--------------------------------------------------------------------------------
/comicViewer/src/main/java/net/bytten/comicviewer/IComicProvider.java:
--------------------------------------------------------------------------------
1 | package net.bytten.comicviewer;
2 |
3 | import java.util.List;
4 |
5 | import net.bytten.comicviewer.ArchiveData.ArchiveItem;
6 | import android.net.Uri;
7 |
8 | public interface IComicProvider {
9 |
10 | public Uri comicDataUrlForUrl(Uri url);
11 | public Uri createComicUrl(String comicId);
12 | public String getFirstId();
13 | public Uri getFinalComicUrl();
14 | public Uri fetchRandomComicUrl() throws Exception;
15 | public IComicInfo fetchComicInfo(Uri url) throws Exception;
16 | public IComicInfo createEmptyComicInfo();
17 | public List fetchArchive() throws Exception;
18 |
19 | // Return null if the comic has no explanation
20 | public Uri getExplainUrl(IComicInfo comic);
21 |
22 | }
23 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/xkcdViewer/src/main/java/net/bytten/xkcdviewer/XkcdArchiveActivity.java:
--------------------------------------------------------------------------------
1 | package net.bytten.xkcdviewer;
2 |
3 | import net.bytten.comicviewer.ArchiveActivity;
4 | import net.bytten.comicviewer.IComicDefinition;
5 |
6 | public class XkcdArchiveActivity extends ArchiveActivity {
7 |
8 | @Override
9 | protected IComicDefinition makeComicDef() {
10 | return new XkcdComicDefinition();
11 | }
12 |
13 | @Override
14 | protected String getStringArchive() {
15 | return getResources().getString(R.string.app_archive_label);
16 | }
17 |
18 | @Override
19 | protected String getStringBookmarks() {
20 | return getResources().getString(R.string.app_bookmarks_label);
21 | }
22 |
23 | @Override
24 | protected String getStringSearchByTitle() {
25 | return getResources().getString(R.string.app_search_title_label);
26 | }
27 |
28 | }
29 |
--------------------------------------------------------------------------------
/comicViewer/src/main/java/net/bytten/comicviewer/SettingsActivity.java:
--------------------------------------------------------------------------------
1 | package net.bytten.comicviewer;
2 |
3 | import android.content.SharedPreferences;
4 | import android.os.Bundle;
5 | import android.preference.CheckBoxPreference;
6 | import android.preference.PreferenceActivity;
7 | import android.preference.PreferenceManager;
8 |
9 | public class SettingsActivity extends PreferenceActivity {
10 |
11 | @Override
12 | public void onCreate(Bundle savedInstanceState) {
13 | super.onCreate(savedInstanceState);
14 | addPreferencesFromResource(R.xml.prefs);
15 |
16 | final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
17 | final boolean value = prefs.getBoolean("useZoomControls",
18 | !VersionHacks.isIncredible() && VersionHacks.getSdkInt() >= 5);
19 | ((CheckBoxPreference)findPreference("useZoomControls"))
20 | .setChecked(value);
21 | }
22 |
23 | }
24 |
--------------------------------------------------------------------------------
/comicViewer/src/main/res/xml/prefs.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
11 |
17 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/comicViewer/src/main/java/net/bytten/comicviewer/VersionHacks.java:
--------------------------------------------------------------------------------
1 | package net.bytten.comicviewer;
2 |
3 | import android.os.Build;
4 |
5 | public class VersionHacks {
6 | //
7 | // Because Android 1.5 does not have android.os.Build.SDK_INT, we have to simulate
8 | // for 1.5 devices, but string parsing is annoying, so we will use SDK_INT if it is
9 | // available.
10 | // CREDIT: These two methods are from
11 | // http://sagistech.blogspot.com/2011/01/buildversionsdkint-on-android-15.html
12 | //
13 | public static int getSdkInt() {
14 | if (Build.VERSION.RELEASE.startsWith("1.5"))
15 | return 3;
16 |
17 | return HelperInternal.getSdkIntInternal();
18 | }
19 |
20 | private static class HelperInternal {
21 | private static int getSdkIntInternal() {
22 | return Build.VERSION.SDK_INT;
23 | }
24 | }
25 | //
26 | // !CREDIT:
27 | //
28 |
29 | public static boolean isIncredible() {
30 | return Build.MODEL.toLowerCase().contains("incredible") ||
31 | Build.MODEL.toLowerCase().contains("adr6300");
32 | }
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/comicViewer/src/main/res/values/mainmenu_strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Go To...
3 | Final
4 | Next
5 | First
6 | Previous
7 | Archive
8 | Random
9 | Website
10 | Alt Text
11 | Open Link...
12 | Archive
13 | Share...
14 | Link...
15 | Image...
16 | Favorites
17 | Search by Title...
18 | Refresh
19 | Preferences
20 | Support Comic Author
21 | Donate
22 | About
23 | Debug
24 | Explain
25 |
26 |
--------------------------------------------------------------------------------
/xkcdViewer/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | xkcdViewer
4 | xkcd Archive - xkcdViewer
5 | Favorites - xkcdViewer
6 | Search by Title - xkcdViewer
7 | Preferences - xkcdViewer
8 |
9 | xkcdViewer v%s. Application for viewing xkcd comics with hover text.\n\n
10 | xkcdViewer Copyright (C) 2009-2017, Tom Coxon, Tyler Breisacher, David
11 | McCullough, Kristian Lundkvist.\n\n
12 | xkcd is Copyright (C) 2006 - 2017, Randall Munroe (http://xkcd.com)\n\n
13 | Contains some menu icons from www.androidicons.com, licensed under a
14 | Creative Commons Attribution - Share Alike license.\n\n
15 | Application icon kindly donated by Johan Larsson.\n\n
16 | You can fork this project on github:\n
17 | \thttp://github.com/tcoxon/XkcdViewer/\n\n
18 | This program is distributed in the hope that it will be useful, but WITHOUT
19 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
20 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
21 | more details.\n\n
22 | Developed by Bytten Studio\n
23 | \thttp://bytten-studio.com\n\n
24 | If you like this app, please consider donating, or buying one of my games. Thanks.
25 |
26 |
27 |
--------------------------------------------------------------------------------
/xkcdViewer/src/main/java/net/bytten/xkcdviewer/XkcdComicInfo.java:
--------------------------------------------------------------------------------
1 | package net.bytten.xkcdviewer;
2 |
3 | import net.bytten.comicviewer.IComicInfo;
4 | import android.net.Uri;
5 |
6 | public class XkcdComicInfo implements IComicInfo {
7 |
8 | public Uri img, link;
9 | public int num;
10 | public String title = "", alt = "";
11 | public boolean bookmarked;
12 |
13 | @Override
14 | public String getAlt() {
15 | return alt;
16 | }
17 |
18 | @Override
19 | public String getId() {
20 | return Integer.toString(num);
21 | }
22 |
23 | @Override
24 | public Uri getImage() {
25 | return img;
26 | }
27 |
28 | @Override
29 | public String getNextId() {
30 | int n = num + 1;
31 | // #404 is xkcd's error page!
32 | if (n == 404) ++n;
33 | return Integer.toString(n);
34 | }
35 |
36 | @Override
37 | public String getPrevId() {
38 | int n = num - 1;
39 | // #404 is xkcd's error page!
40 | if (n == 404) --n;
41 | return Integer.toString(n);
42 | }
43 |
44 | @Override
45 | public String getTitle() {
46 | return title;
47 | }
48 |
49 | @Override
50 | public String getUrl() {
51 | return "http://xkcd.com/"+getId()+"/";
52 | }
53 |
54 | @Override
55 | public boolean isBookmarked() {
56 | return bookmarked;
57 | }
58 |
59 | @Override
60 | public void setBookmarked(boolean b) {
61 | bookmarked = b;
62 | }
63 |
64 | @Override
65 | public Uri getLink() {
66 | return link;
67 | }
68 |
69 | }
70 |
--------------------------------------------------------------------------------
/xkcdViewer/src/main/java/net/bytten/xkcdviewer/XkcdViewerActivity.java:
--------------------------------------------------------------------------------
1 | /*
2 | * xkcdViewer - Android app to view xkcd comics with hover text
3 | * Copyright (C) 2009-2014 Tom Coxon, Tyler Breisacher, David McCullough,
4 | * Kristian Lundkvist, Ivan Vasiljević.
5 | *
6 | * xkcd is Copyright (C) Randall Munroe.
7 | *
8 | * This program is free software; you can redistribute it and/or modify
9 | * it under the terms of the GNU General Public License as published by
10 | * the Free Software Foundation; either version 2 of the License, or
11 | * (at your option) any later version.
12 | *
13 | * This program is distributed in the hope that it will be useful,
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 | * GNU General Public License for more details.
17 | *
18 | * You should have received a copy of the GNU General Public License along
19 | * with this program; if not, write to the Free Software Foundation, Inc.,
20 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 | */
22 | package net.bytten.xkcdviewer;
23 |
24 | import net.bytten.comicviewer.ArchiveActivity;
25 | import net.bytten.comicviewer.ComicViewerActivity;
26 | import net.bytten.comicviewer.IComicDefinition;
27 |
28 | public class XkcdViewerActivity extends ComicViewerActivity {
29 |
30 | @Override
31 | protected IComicDefinition makeComicDef() {
32 | return new XkcdComicDefinition();
33 | }
34 |
35 | @Override
36 | protected Class extends ArchiveActivity> getArchiveActivityClass() {
37 | return XkcdArchiveActivity.class;
38 | }
39 |
40 | @Override
41 | protected String getStringAboutText() {
42 | return getResources().getString(R.string.aboutText);
43 | }
44 |
45 | @Override
46 | protected String getStringAppName() {
47 | return getResources().getString(R.string.app_name);
48 | }
49 |
50 | }
51 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/xkcdViewer/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
10 |
11 |
12 |
15 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
35 |
36 |
37 |
38 |
39 |
40 |
43 |
44 |
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/xkcdViewer/src/main/java/net/bytten/xkcdviewer/XkcdComicDefinition.java:
--------------------------------------------------------------------------------
1 | package net.bytten.xkcdviewer;
2 |
3 | import java.util.regex.Pattern;
4 |
5 | import net.bytten.comicviewer.IComicDefinition;
6 | import net.bytten.comicviewer.IComicProvider;
7 |
8 | import android.net.Uri;
9 |
10 | public class XkcdComicDefinition implements IComicDefinition {
11 |
12 | static public final Pattern
13 | xkcdHomePattern = Pattern.compile(
14 | "http(s?)://(www\\.)?xkcd\\.com(/)?"),
15 | comicUrlPattern = Pattern.compile(
16 | "http(s?)://(www\\.)?xkcd\\.com/([0-9]+)(/)?"),
17 | archiveUrlPattern = Pattern.compile(
18 | "http(s?)://(www\\.)?xkcd\\.com/archive(/)?");
19 |
20 | private XkcdComicProvider provider;
21 |
22 | public XkcdComicDefinition() {
23 | provider = new XkcdComicProvider(this);
24 | }
25 |
26 | @Override
27 | public Uri getArchiveUrl() {
28 | return Uri.parse("http://xkcd.com/archive/");
29 | }
30 |
31 | @Override
32 | public String getAuthorLinkText() {
33 | return "xkcd Store";
34 | }
35 |
36 | @Override
37 | public Uri getAuthorLinkUrl() {
38 | return Uri.parse("http://store.xkcd.com/");
39 | }
40 |
41 | @Override
42 | public String getAuthorName() {
43 | return "Randall Munroe";
44 | }
45 |
46 | @Override
47 | public String getComicTitle() {
48 | return "xkcd";
49 | }
50 |
51 | @Override
52 | public String getComicTitleAbbrev() {
53 | return "xkcd";
54 | }
55 |
56 | @Override
57 | public String getPackageName() {
58 | return "net.bytten.xkcdviewer";
59 | }
60 |
61 | @Override
62 | public boolean hasAltText() {
63 | return true;
64 | }
65 |
66 | @Override
67 | public boolean idsAreNumbers() {
68 | return true;
69 | }
70 |
71 | @Override
72 | public boolean isArchiveUrl(Uri url) {
73 | return archiveUrlPattern.matcher(url.toString()).matches();
74 | }
75 |
76 | @Override
77 | public boolean isComicUrl(Uri url) {
78 | return comicUrlPattern.matcher(url.toString()).matches();
79 | }
80 |
81 | @Override
82 | public boolean isHomeUrl(Uri url) {
83 | return xkcdHomePattern.matcher(url.toString()).matches();
84 | }
85 |
86 | @Override
87 | public IComicProvider getProvider() {
88 | return provider;
89 | }
90 |
91 | @Override
92 | public Uri getDonateUrl() {
93 | return Uri.parse("https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=C9JRVA3NTULSL&lc=US&item_name=XkcdViewer%20donation&item_number=xkcdviewer¤cy_code=USD");
94 | }
95 |
96 | @Override
97 | public Uri getDeveloperUrl() {
98 | return Uri.parse("http://bytten-studio.com");
99 | }
100 |
101 | }
102 |
--------------------------------------------------------------------------------
/comicViewer/src/main/java/net/bytten/comicviewer/ArchiveData.java:
--------------------------------------------------------------------------------
1 | package net.bytten.comicviewer;
2 |
3 | import java.lang.ref.SoftReference;
4 | import java.util.Date;
5 | import java.util.HashMap;
6 | import java.util.List;
7 | import java.util.Map;
8 |
9 | import android.content.Context;
10 |
11 | public class ArchiveData {
12 |
13 | private static final Map> archives
14 | = new HashMap>();
15 |
16 | /* The maximum age the cache is allowed to reach in milliseconds */
17 | private static final long CACHE_AGE_LIMIT = 60*60*1000; // = 1 hour
18 |
19 | public static class ArchiveItem {
20 | public boolean bookmarked = false;
21 | public String title, comicId;
22 |
23 | @Override
24 | public String toString() {
25 | return title;
26 | }
27 | }
28 |
29 | /* Putting the cache in a SoftReference means the system can still
30 | * garbage collect it if low on memory. */
31 | private SoftReference> cache = null;
32 | private Date cacheModDate = null;
33 | private IComicDefinition comicDef;
34 |
35 | private ArchiveData(IComicDefinition comicDef) {
36 | this.comicDef = comicDef;
37 | }
38 |
39 | public static ArchiveData getArchive(IComicDefinition def) {
40 | SoftReference archRef = archives.get(def.getClass().getName());
41 | if (archRef == null || archRef.get() == null) {
42 | archRef = new SoftReference(new ArchiveData(def));
43 | archives.put(def.getClass().getName(), archRef);
44 | }
45 | archRef.get().comicDef = def;
46 | return archRef.get();
47 | }
48 |
49 | public boolean isCacheValid() {
50 | return cache != null && cache.get() != null && cacheModDate != null &&
51 | cacheModDate.before(new Date(cacheModDate.getTime() +
52 | CACHE_AGE_LIMIT));
53 | }
54 |
55 | /* Do NOT call in a UI thread. May block until data has been fetched.
56 | * Do NOT add or remove elements from the returned list (but bookmarking
57 | * may be altered). */
58 | public List getData(Context cxt) throws Exception {
59 | List cacheVal = cache == null ? null : cache.get();
60 | if (!isCacheValid())
61 | return fetchData(cxt);
62 | return cacheVal;
63 | }
64 |
65 | public void refresh(Context cxt) throws Exception {
66 | fetchData(cxt);
67 | }
68 |
69 | private List fetchData(Context cxt) throws Exception {
70 | List archiveItems = comicDef.getProvider().fetchArchive();
71 |
72 | for (ArchiveItem item: archiveItems) {
73 | if (BookmarksHelper.isBookmarked(cxt, item)) {
74 | item.bookmarked = true;
75 | }
76 | }
77 |
78 | if (cache != null)
79 | cache.clear();
80 | cache = new SoftReference>(archiveItems);
81 | cacheModDate = new Date();
82 | return archiveItems;
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/comicViewer/src/main/res/menu/mainmenu.xml:
--------------------------------------------------------------------------------
1 |
2 |
36 |
--------------------------------------------------------------------------------
/comicViewer/src/main/res/layout/main.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
13 |
20 |
26 |
32 |
36 |
39 |
45 |
48 |
51 |
57 |
58 |
59 |
60 |
61 |
63 |
64 |
66 |
67 |
68 |
--------------------------------------------------------------------------------
/comicViewer/src/main/java/net/bytten/comicviewer/BookmarksHelper.java:
--------------------------------------------------------------------------------
1 | package net.bytten.comicviewer;
2 |
3 | import java.util.ArrayList;
4 | import java.util.List;
5 |
6 | import net.bytten.comicviewer.ArchiveData.ArchiveItem;
7 | import android.content.Context;
8 | import android.database.Cursor;
9 | import android.database.sqlite.SQLiteDatabase;
10 | import android.database.sqlite.SQLiteOpenHelper;
11 |
12 | public class BookmarksHelper extends SQLiteOpenHelper {
13 |
14 | public static final String DB_NAME = "bookmarks";
15 | public static final int DB_VERSION = 1;
16 |
17 | public BookmarksHelper(Context context) {
18 | super(context, DB_NAME, null, DB_VERSION);
19 | }
20 |
21 | @Override
22 | public void onCreate(SQLiteDatabase db) {
23 | /* The field 'number' is so named because of historical reasons where
24 | * all supported comics were xkcd, whose comic ID is a number. */
25 | db.execSQL("CREATE TABLE bookmarks (number TEXT, title TEXT);");
26 | }
27 |
28 | @Override
29 | public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
30 | }
31 |
32 | protected static SQLiteDatabase dbInstance = null;
33 | public static SQLiteDatabase getDb(Context cxt) {
34 | if (dbInstance == null) {
35 | dbInstance = new BookmarksHelper(cxt).getWritableDatabase();
36 | }
37 | return dbInstance;
38 | }
39 |
40 | public static List getBookmarks(Context cxt) {
41 | final Cursor results = getDb(cxt).rawQuery("SELECT * FROM bookmarks", new String[]{});
42 | try {
43 | List list = new ArrayList();
44 | if (results.getCount() > 0) {
45 | results.moveToNext();
46 | while (!results.isAfterLast()) {
47 | ArchiveItem item = new ArchiveItem();
48 | item.comicId = results.getString(0);
49 | item.title = results.getString(1);
50 | item.bookmarked = true;
51 | list.add(item);
52 | results.moveToNext();
53 | }
54 | }
55 | return list;
56 | } finally {
57 | results.close();
58 | }
59 | }
60 | public static boolean isBookmarked(Context cxt, String comicNumber) {
61 | final Cursor results = getDb(cxt).rawQuery(
62 | "SELECT * FROM bookmarks WHERE number = ?",
63 | new String[]{comicNumber});
64 | try {
65 | return results.getCount() > 0;
66 | } finally {
67 | results.close();
68 | }
69 | }
70 | public static boolean isBookmarked(Context cxt, ArchiveItem item) {
71 | return isBookmarked(cxt, item.comicId);
72 | }
73 | public static void addBookmark(Context cxt, String comicNumber, String title) {
74 | getDb(cxt).execSQL(
75 | "INSERT INTO bookmarks VALUES (?, ?)",
76 | new Object[]{comicNumber, title});
77 | }
78 | public static void addBookmark(Context cxt, ArchiveItem item) {
79 | addBookmark(cxt, item.comicId, item.title);
80 | }
81 | public static void removeBookmark(Context cxt, String comicNumber) {
82 | getDb(cxt).execSQL(
83 | "DELETE FROM bookmarks WHERE number = ?",
84 | new Object[]{comicNumber});
85 | }
86 | public static void removeBookmark(Context cxt, ArchiveItem item) {
87 | removeBookmark(cxt, item.comicId);
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/comicViewer/src/main/java/net/bytten/comicviewer/HackedWebView.java:
--------------------------------------------------------------------------------
1 | package net.bytten.comicviewer;
2 |
3 | import android.content.Context;
4 | import android.util.AttributeSet;
5 | import android.view.MotionEvent;
6 | import android.webkit.WebView;
7 |
8 | public class HackedWebView extends WebView {
9 | public HackedWebView(Context cxt) {
10 | super(cxt);
11 | }
12 | public HackedWebView(Context cxt, AttributeSet attrs) {
13 | super(cxt, attrs);
14 | }
15 | public HackedWebView(Context cxt, AttributeSet attrs, int defStyle) {
16 | super(cxt, attrs, defStyle);
17 | }
18 |
19 | private float lastTouchX, lastTouchY;
20 | private boolean hasMoved = false;
21 | private boolean allowZoomButtons = false, allowPinchZoom = true;
22 |
23 | public boolean getAllowZoomButtons() {
24 | return allowZoomButtons;
25 | }
26 | public void setAllowZoomButtons(boolean b) {
27 | allowZoomButtons = b;
28 | }
29 |
30 | public void setAllowPinchZoom(boolean b) {
31 | allowPinchZoom = b;
32 | }
33 | public boolean getAllowPinchZoom() {
34 | return allowPinchZoom;
35 | }
36 |
37 | private boolean moved(MotionEvent evt) {
38 | return hasMoved ||
39 | Math.abs(evt.getX() - lastTouchX) > 10.0 ||
40 | Math.abs(evt.getY() - lastTouchY) > 10.0;
41 | }
42 |
43 | private static class PointerDownHack {
44 | public static boolean isPointerDown(MotionEvent evt) {
45 | return evt.getAction() == MotionEvent.ACTION_DOWN ||
46 | evt.getAction() == MotionEvent.ACTION_POINTER_DOWN ||
47 | /* These next three are deprecated, but we MUST use them to
48 | * remain compatible with all devices! */
49 | evt.getAction() == MotionEvent.ACTION_POINTER_1_DOWN ||
50 | evt.getAction() == MotionEvent.ACTION_POINTER_2_DOWN ||
51 | evt.getAction() == MotionEvent.ACTION_POINTER_3_DOWN;
52 | }
53 | public static int getPointerCount(MotionEvent evt) {
54 | return evt.getPointerCount();
55 | }
56 | };
57 | private boolean isPointerDown(MotionEvent evt) {
58 | if (VersionHacks.getSdkInt() >= 5)
59 | return PointerDownHack.isPointerDown(evt);
60 | return evt.getAction() == MotionEvent.ACTION_DOWN;
61 | }
62 | private int getPointerCount(MotionEvent evt) {
63 | if (VersionHacks.getSdkInt() >= 5)
64 | return PointerDownHack.getPointerCount(evt);
65 | return 1;
66 | }
67 |
68 | @Override
69 | public boolean onTouchEvent(MotionEvent evt) {
70 | /* Horrible hack #1: Enable pinch-zoom without zoom buttons */
71 | if (!getAllowZoomButtons() && isPointerDown(evt)) {
72 | boolean allowZoom = getPointerCount(evt) > 1;
73 | getSettings().setSupportZoom(allowZoom);
74 | getSettings().setBuiltInZoomControls(allowZoom && getAllowPinchZoom());
75 | }
76 |
77 | boolean consumed = super.onTouchEvent(evt);
78 |
79 | /* Horrible hack #2: enable click events following drag / pinch-zoom */
80 | if (isClickable()) {
81 | switch (evt.getAction()) {
82 | case MotionEvent.ACTION_DOWN:
83 | lastTouchX = evt.getX();
84 | lastTouchY = evt.getY();
85 | hasMoved = false;
86 | break;
87 | case MotionEvent.ACTION_MOVE:
88 | hasMoved = moved(evt);
89 | break;
90 | case MotionEvent.ACTION_UP:
91 | if (!moved(evt)) performClick();
92 | break;
93 | }
94 | }
95 |
96 | return consumed;
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/xkcdViewer/src/main/java/net/bytten/xkcdviewer/XkcdComicProvider.java:
--------------------------------------------------------------------------------
1 | package net.bytten.xkcdviewer;
2 |
3 | import java.io.*;
4 | import java.net.*;
5 | import java.util.ArrayList;
6 | import java.util.List;
7 | import java.util.Map;
8 | import java.util.regex.Matcher;
9 | import java.util.regex.Pattern;
10 |
11 | import net.bytten.comicviewer.IComicInfo;
12 | import net.bytten.comicviewer.IComicProvider;
13 | import net.bytten.comicviewer.Utility;
14 | import net.bytten.comicviewer.ArchiveData.ArchiveItem;
15 |
16 | import org.json.JSONObject;
17 | import org.json.JSONTokener;
18 |
19 | import android.net.Uri;
20 | import android.util.Log;
21 |
22 | public class XkcdComicProvider implements IComicProvider {
23 |
24 | private static final Pattern archiveItemPattern = Pattern.compile(
25 | // group(1): comic number; group(2): date; group(3): title
26 | "\\s*([^<]+)
\\s*");
27 | private static final String ARCHIVE_URL = "http://www.xkcd.com/archive/";
28 |
29 | private XkcdComicDefinition def;
30 |
31 | public XkcdComicProvider(XkcdComicDefinition def) {
32 | this.def = def;
33 | }
34 |
35 | @Override
36 | public Uri comicDataUrlForUrl(Uri url) {
37 | Matcher m = XkcdComicDefinition.comicUrlPattern
38 | .matcher(url.toString());
39 | if (m.matches())
40 | return createComicUrl(m.group(3));
41 | return null;
42 | }
43 |
44 | @Override
45 | public Uri createComicUrl(String comicId) {
46 | return Uri.parse("http://xkcd.com/"+comicId+"/info.0.json");
47 | }
48 |
49 | @Override
50 | public IComicInfo fetchComicInfo(Uri url) throws Exception {
51 | // Uses xkcd's JSON interface
52 | // (http://xkcd.com/json.html)
53 | String text = Utility.blockingReadUri(url);
54 | JSONObject obj = (JSONObject)new JSONTokener(text).nextValue();
55 | Log.d("json", obj.names().toString());
56 | XkcdComicInfo data = new XkcdComicInfo();
57 | data.img = Uri.parse(obj.getString("img"));
58 | byte [] encodedAlt = obj.getString("alt").getBytes("ISO-8859-1");
59 | data.alt = new String(encodedAlt, "UTF-8");
60 | data.num = obj.getInt("num");
61 | data.title = obj.getString("title");
62 | if (obj.has("link") && obj.getString("link").length() > 0) {
63 | data.link = Uri.parse(obj.getString("link"));
64 | }
65 | return data;
66 | }
67 |
68 | @Override
69 | public Uri fetchRandomComicUrl() throws Exception {
70 | HttpURLConnection http = (HttpURLConnection) new URL("https",
71 | "dynamic.xkcd.com", "/random/comic").openConnection();
72 | http.setInstanceFollowRedirects(false);
73 | String redirect = http.getHeaderField("Location");
74 | if (redirect != null) {
75 | Uri loc = Uri.parse(redirect);
76 | if (def.isComicUrl(loc)) {
77 | return comicDataUrlForUrl(loc);
78 | }
79 | }
80 | Log.w("headers", Integer.toString(http.getResponseCode()));
81 | Map> headers = http.getHeaderFields();
82 | for (String key: headers.keySet()) {
83 | if (key == null) continue;
84 | Log.w("headers", key);
85 | for (String val: headers.get(key)) {
86 | Log.w("headers", " "+val);
87 | }
88 | }
89 | return null;
90 | }
91 |
92 | @Override
93 | public Uri getFinalComicUrl() {
94 | return Uri.parse("http://xkcd.com/info.0.json");
95 | }
96 |
97 | @Override
98 | public String getFirstId() {
99 | return "1";
100 | }
101 |
102 | @Override
103 | public IComicInfo createEmptyComicInfo() {
104 | return new XkcdComicInfo();
105 | }
106 |
107 | @Override
108 | public List fetchArchive() throws Exception {
109 | List archiveItems = new ArrayList();
110 | URL url = new URL(ARCHIVE_URL);
111 | BufferedReader br = new BufferedReader(new InputStreamReader(Utility.openRedirectableConnection(url).getInputStream()));
112 |
113 | try {
114 | String line;
115 | while ((line = br.readLine()) != null) {
116 | Matcher m = archiveItemPattern.matcher(line);
117 | while (m.find()) {
118 | ArchiveItem item = new ArchiveItem();
119 | item.comicId = m.group(1);
120 | item.title = item.comicId + " - " + m.group(3);
121 | archiveItems.add(item);
122 | }
123 |
124 | Utility.allowInterrupt();
125 | }
126 |
127 | } finally {
128 | br.close();
129 | }
130 | return archiveItems;
131 | }
132 |
133 | @Override
134 | public Uri getExplainUrl(IComicInfo comic) {
135 | return Uri.parse("http://www.explainxkcd.com/wiki/index.php?title="+
136 | comic.getId());
137 | }
138 |
139 | }
140 |
--------------------------------------------------------------------------------
/comicViewer/src/main/java/net/bytten/comicviewer/Utility.java:
--------------------------------------------------------------------------------
1 | package net.bytten.comicviewer;
2 |
3 | import java.io.BufferedReader;
4 | import java.io.File;
5 | import java.io.FileOutputStream;
6 | import java.io.IOException;
7 | import java.io.InputStream;
8 | import java.io.InputStreamReader;
9 | import java.net.HttpURLConnection;
10 | import java.net.MalformedURLException;
11 | import java.net.URL;
12 | import java.net.URLConnection;
13 |
14 | import android.app.ProgressDialog;
15 | import android.content.Context;
16 | import android.content.DialogInterface;
17 | import android.content.DialogInterface.OnCancelListener;
18 | import android.net.Uri;
19 | import android.os.AsyncTask;
20 |
21 | public class Utility {
22 |
23 | public static URLConnection openRedirectableConnection(URL url) throws IOException {
24 | return openRedirectableConnection(url, 0);
25 | }
26 |
27 | private static URLConnection openRedirectableConnection(URL url, int redirects) throws IOException {
28 | URLConnection conn = url.openConnection();
29 | if (conn instanceof HttpURLConnection) {
30 | HttpURLConnection http = (HttpURLConnection)conn;
31 | int status = http.getResponseCode();
32 | if (status == -1) {
33 | throw new IOException("A certificate failure occurred. Make sure your device is fully up to date."); // In practice, that seems to be when this HTTP failure occurs
34 | }
35 | if (status == HttpURLConnection.HTTP_MOVED_TEMP || status == HttpURLConnection.HTTP_MOVED_PERM || status == HttpURLConnection.HTTP_SEE_OTHER) {
36 | if (redirects > 2)
37 | throw new IOException("Too many HTTP redirects");
38 | String location = http.getHeaderField("Location");
39 | if (location == null)
40 | throw new IOException("Invalid redirect");
41 | return openRedirectableConnection(new URL(location), redirects + 1);
42 | }
43 | }
44 | return conn;
45 | }
46 |
47 | public static String blockingReadUri(Uri uri) throws IOException,
48 | InterruptedException
49 | {
50 | InputStream is = openRedirectableConnection(new URL(uri.toString())).getInputStream();
51 | try {
52 | StringBuffer sb = new StringBuffer();
53 | BufferedReader br = new BufferedReader(new InputStreamReader(is));
54 | String line;
55 | while ((line = br.readLine()) != null) {
56 | sb.append(line);
57 | sb.append('\n');
58 | Utility.allowInterrupt();
59 | }
60 | return sb.toString();
61 | } finally {
62 | is.close();
63 | }
64 | }
65 |
66 | public static void allowInterrupt() throws InterruptedException {
67 | if (Thread.interrupted())
68 | throw new InterruptedException();
69 | }
70 |
71 | public static abstract class CancellableAsyncTaskWithProgressDialog
72 | extends AsyncTask
73 | {
74 | private ProgressDialog pd;
75 |
76 | private String title;
77 |
78 | public CancellableAsyncTaskWithProgressDialog(String title) {
79 | this.title = title;
80 | }
81 |
82 | public void start(Context cxt, String pdText, Params... params) {
83 | pd = ProgressDialog.show(cxt,
84 | title, pdText, true, true,
85 | new OnCancelListener() {
86 | public void onCancel(DialogInterface dialog) {
87 | cancel(true);
88 | }
89 | });
90 | execute(params);
91 | }
92 |
93 | @Override
94 | protected void onProgressUpdate(Integer... progress) {
95 | pd.setProgress(progress[0]);
96 | }
97 |
98 | @Override
99 | protected void onCancelled(Result result) {
100 | pd.dismiss();
101 | }
102 |
103 | @Override
104 | protected void onPostExecute(Result result) {
105 | pd.dismiss();
106 | }
107 | }
108 |
109 | public static void blockingSaveFile(File file, Uri uri) throws IOException,
110 | InterruptedException
111 | {
112 | FileOutputStream fos = null;
113 | InputStream is = null;
114 | try {
115 | fos = new FileOutputStream(file);
116 | is = openRedirectableConnection(new URL(uri.toString())).getInputStream();
117 |
118 | byte[] buffer = new byte[512];
119 | int count = -1;
120 | while ((count = is.read(buffer)) != -1) {
121 | fos.write(buffer, 0, count);
122 | Utility.allowInterrupt();
123 | }
124 | } finally {
125 | try {
126 | if (fos != null) fos.close();
127 | if (is != null) is.close();
128 | } catch (IOException ex) {}
129 | }
130 | }
131 |
132 | public static String getContentType(Uri uri) throws MalformedURLException, IOException {
133 | URLConnection conn = new URL(uri.toString()).openConnection();
134 | return conn.getContentType();
135 | }
136 |
137 | }
138 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # Attempt to set APP_HOME
46 | # Resolve links: $0 may be a link
47 | PRG="$0"
48 | # Need this for relative symlinks.
49 | while [ -h "$PRG" ] ; do
50 | ls=`ls -ld "$PRG"`
51 | link=`expr "$ls" : '.*-> \(.*\)$'`
52 | if expr "$link" : '/.*' > /dev/null; then
53 | PRG="$link"
54 | else
55 | PRG=`dirname "$PRG"`"/$link"
56 | fi
57 | done
58 | SAVED="`pwd`"
59 | cd "`dirname \"$PRG\"`/" >/dev/null
60 | APP_HOME="`pwd -P`"
61 | cd "$SAVED" >/dev/null
62 |
63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
64 |
65 | # Determine the Java command to use to start the JVM.
66 | if [ -n "$JAVA_HOME" ] ; then
67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
68 | # IBM's JDK on AIX uses strange locations for the executables
69 | JAVACMD="$JAVA_HOME/jre/sh/java"
70 | else
71 | JAVACMD="$JAVA_HOME/bin/java"
72 | fi
73 | if [ ! -x "$JAVACMD" ] ; then
74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
75 |
76 | Please set the JAVA_HOME variable in your environment to match the
77 | location of your Java installation."
78 | fi
79 | else
80 | JAVACMD="java"
81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
82 |
83 | Please set the JAVA_HOME variable in your environment to match the
84 | location of your Java installation."
85 | fi
86 |
87 | # Increase the maximum file descriptors if we can.
88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
89 | MAX_FD_LIMIT=`ulimit -H -n`
90 | if [ $? -eq 0 ] ; then
91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
92 | MAX_FD="$MAX_FD_LIMIT"
93 | fi
94 | ulimit -n $MAX_FD
95 | if [ $? -ne 0 ] ; then
96 | warn "Could not set maximum file descriptor limit: $MAX_FD"
97 | fi
98 | else
99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100 | fi
101 | fi
102 |
103 | # For Darwin, add options to specify how the application appears in the dock
104 | if $darwin; then
105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106 | fi
107 |
108 | # For Cygwin, switch paths to Windows format before running java
109 | if $cygwin ; then
110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112 | JAVACMD=`cygpath --unix "$JAVACMD"`
113 |
114 | # We build the pattern for arguments to be converted via cygpath
115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116 | SEP=""
117 | for dir in $ROOTDIRSRAW ; do
118 | ROOTDIRS="$ROOTDIRS$SEP$dir"
119 | SEP="|"
120 | done
121 | OURCYGPATTERN="(^($ROOTDIRS))"
122 | # Add a user-defined pattern to the cygpath arguments
123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125 | fi
126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
127 | i=0
128 | for arg in "$@" ; do
129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
131 |
132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134 | else
135 | eval `echo args$i`="\"$arg\""
136 | fi
137 | i=$((i+1))
138 | done
139 | case $i in
140 | (0) set -- ;;
141 | (1) set -- "$args0" ;;
142 | (2) set -- "$args0" "$args1" ;;
143 | (3) set -- "$args0" "$args1" "$args2" ;;
144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150 | esac
151 | fi
152 |
153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154 | function splitJvmOpts() {
155 | JVM_OPTS=("$@")
156 | }
157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159 |
160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
161 |
--------------------------------------------------------------------------------
/xkcdViewer/lint.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
--------------------------------------------------------------------------------
/comicViewer/src/main/java/net/bytten/comicviewer/ArchiveActivity.java:
--------------------------------------------------------------------------------
1 | package net.bytten.comicviewer;
2 |
3 | import java.io.IOException;
4 | import java.net.MalformedURLException;
5 | import java.util.ArrayList;
6 | import java.util.List;
7 |
8 | import net.bytten.comicviewer.ArchiveData.ArchiveItem;
9 | import android.app.AlertDialog;
10 | import android.app.ListActivity;
11 | import android.content.Context;
12 | import android.content.Intent;
13 | import android.os.Bundle;
14 | import android.view.LayoutInflater;
15 | import android.view.Menu;
16 | import android.view.MenuInflater;
17 | import android.view.MenuItem;
18 | import android.view.View;
19 | import android.view.ViewGroup;
20 | import android.widget.ArrayAdapter;
21 | import android.widget.CheckBox;
22 | import android.widget.CompoundButton;
23 | import android.widget.CompoundButton.OnCheckedChangeListener;
24 | import android.widget.ListView;
25 | import android.widget.TextView;
26 | import android.widget.Toast;
27 |
28 | public abstract class ArchiveActivity extends ListActivity {
29 | static public enum LoadType { ARCHIVE, BOOKMARKS, SEARCH_TITLE };
30 |
31 | private List archiveItems;
32 |
33 | protected String query = null;
34 | protected LoadType loadtype = LoadType.ARCHIVE;
35 | protected IComicDefinition comicDef;
36 | protected ArchiveData archive;
37 |
38 | protected abstract IComicDefinition makeComicDef();
39 |
40 | @Override
41 | protected void onCreate(Bundle savedInstanceState) {
42 | super.onCreate(savedInstanceState);
43 |
44 | comicDef = makeComicDef();
45 | archive = ArchiveData.getArchive(comicDef);
46 |
47 | final Intent intent = getIntent();
48 | query = intent.getStringExtra(getPackageName() + "query");
49 | loadtype = (LoadType) intent.getSerializableExtra(getPackageName() + "LoadType");
50 |
51 | resetContent();
52 | }
53 |
54 | protected void resetContent() {
55 | new Utility.CancellableAsyncTaskWithProgressDialog