├── .gitignore
├── .travis.yml
├── CHANGELOG.md
├── LICENSE
├── README.md
├── build.gradle
├── f_droid_logo.png
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── proguard-android.pro
└── src
└── main
├── AndroidManifest.xml
├── assets
└── LICENSE.txt
├── java
└── com
│ └── mareksebera
│ └── simpledilbert
│ ├── AppController.java
│ ├── core
│ ├── DilbertFragment.java
│ ├── DilbertFragmentActivity.java
│ ├── DilbertFragmentAdapter.java
│ └── DilbertFragmentInterface.java
│ ├── favorites
│ ├── DilbertFavoritedActivity.java
│ ├── DilbertFavoritedFragmentAdapter.java
│ └── FavoritedItem.java
│ ├── picker
│ ├── FolderPickerActivity.java
│ ├── FolderPickerAdapter.java
│ └── FolderPickerFragment.java
│ ├── preferences
│ ├── DilbertPreferences.java
│ └── DilbertPreferencesActivity.java
│ ├── utilities
│ ├── ActionBarUtility.java
│ ├── CustomTarget.java
│ ├── DownloadManagerBroadcastReceiver.java
│ ├── FindUrls.java
│ ├── FixedViewPager.java
│ ├── GetStripUrl.java
│ ├── GetStripUrlInterface.java
│ └── TLSSocketFactory.java
│ └── widget
│ └── WidgetProvider.java
└── res
├── drawable-hdpi
├── cancel.png
├── ic_launcher.png
├── ic_menu_datepicker.png
├── ic_menu_favorited.png
├── ic_menu_not_favorited.png
├── ic_menu_open_at.png
├── ic_menu_refresh.png
├── ic_menu_save.png
├── ic_menu_share.png
├── ic_menu_shuffle.png
├── ic_menu_zoom.png
├── ic_navigation_accept.png
├── widget_latest.png
├── widget_next.png
├── widget_preview.png
└── widget_previous.png
├── drawable-ldpi
├── cancel.png
├── file.png
├── folder.png
├── ic_launcher.png
├── ic_menu_datepicker.png
├── ic_menu_favorited.png
├── ic_menu_not_favorited.png
├── ic_menu_open_at.png
├── ic_menu_refresh.png
├── ic_menu_save.png
├── ic_menu_share.png
├── ic_menu_shuffle.png
├── ic_menu_zoom.png
├── ic_navigation_accept.png
├── widget_latest.png
├── widget_next.png
├── widget_preview.png
└── widget_previous.png
├── drawable-mdpi
├── cancel.png
├── ic_launcher.png
├── ic_menu_datepicker.png
├── ic_menu_favorited.png
├── ic_menu_not_favorited.png
├── ic_menu_open_at.png
├── ic_menu_refresh.png
├── ic_menu_save.png
├── ic_menu_share.png
├── ic_menu_shuffle.png
├── ic_menu_zoom.png
├── ic_navigation_accept.png
├── widget_latest.png
├── widget_next.png
├── widget_preview.png
└── widget_previous.png
├── drawable-xhdpi
├── cancel.png
├── ic_launcher.png
├── ic_menu_datepicker.png
├── ic_menu_favorited.png
├── ic_menu_not_favorited.png
├── ic_menu_open_at.png
├── ic_menu_refresh.png
├── ic_menu_save.png
├── ic_menu_share.png
├── ic_menu_shuffle.png
├── ic_menu_zoom.png
├── ic_navigation_accept.png
├── widget_latest.png
├── widget_next.png
├── widget_preview.png
└── widget_previous.png
├── drawable-xxhdpi
├── ic_launcher.png
└── widget_preview.png
├── drawable-xxxhdpi
├── ic_launcher.png
└── widget_preview.png
├── layout
├── activity_dilbert_fragments.xml
├── activity_folder_picker.xml
├── fragment_dilbert.xml
├── fragment_folder_picker.xml
├── horizontal_divider.xml
├── item_folder_picker.xml
├── preferences.xml
├── preferences_contents.xml
└── widget_layout.xml
├── values-cs
└── strings.xml
├── values-de
└── strings.xml
├── values-es
└── strings.xml
├── values-fr
└── strings.xml
├── values-it
└── strings.xml
├── values-pt
└── strings.xml
├── values-ru
└── strings.xml
├── values-sv
└── strings.xml
├── values
├── strings.xml
└── styles.xml
└── xml
├── daily_widget.xml
└── filepaths.xml
/.gitignore:
--------------------------------------------------------------------------------
1 | # Ant
2 | bin
3 | gen
4 | build.xml
5 | ant.properties
6 | local.properties
7 | proguard.cfg
8 | proguard-project.txt
9 |
10 | #Eclipse
11 | bin
12 | gen
13 | .project
14 | .classpath
15 | .settings
16 | .checkstyle
17 | ic_launcher-web.png
18 | doc
19 | proguard
20 |
21 | #Signing
22 | android.keystore
23 | keystore.readme
24 |
25 | #Gradle
26 | .gradle
27 | build
28 |
29 | # IntelliJ IDEA
30 | .idea
31 | *.iml
32 | *.ipr
33 | *.iws
34 | classes
35 | gen-external-apklibs
36 |
37 | # Maven
38 | target
39 | release.properties
40 | pom.xml.*
41 |
42 | #Other
43 | .DS_Store
44 | tmp
45 | libraries
46 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: android
2 | jdk: oraclejdk8
3 | sudo: false
4 | android:
5 | components:
6 | - tools
7 | - build-tools-28.0.3
8 | - extra-android-support
9 | - extra-android-m2repository
10 | - android-28
11 | licenses:
12 | - '.+'
13 | - 'android-sdk-license-.+'
14 | before_install:
15 | - yes | sdkmanager "platforms;android-28"
16 | - yes | sdkmanager "platforms;android-27"
17 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # CHANGELOG
2 |
3 | ## 4.7
4 |
5 | - Fixed network TLS support for devices 16 <= API <= 21
6 | - Fixed fetching strip title in some situations (was blocking widget also)
7 | - Replaced HttpClient with Volley and custom HurlStack for TLS handling
8 |
9 | ## 4.6
10 |
11 | - Updated SDK to v28
12 | - Fixed sharing strip and text only, thanks to @oldlauer
13 | - Fixed parsing remote HTML, thanks to @3DES
14 | - Fixed caching of preferences between processes
15 | - Fixed scaling of image, thanks to @jazzzz
16 |
17 | ## 4.5
18 |
19 | - New parsing (using twitter:image and twitter:title, for simplicity)
20 | - Downloading and saving strip title (only on refresh for already cached strips)
21 | - Displaying title in basic browsing and in widget (with option to turn off for widget in preferences)
22 | - Updated SDK to v27
23 | - Raised minimum Android API version from 9 to 14 (sorry, phones with 7 years obsolete OS)
24 |
25 | ## 4.4
26 |
27 | - Added offline mode - browsing only downloaded images
28 | - Added random feature when browsing favorites or offline
29 | - Updated SDK to v25
30 | - Function to open current strip in browser
31 | - Fixed handling storage permission on v23+
32 | - Added option to export all strip urls with respective dates as text
33 | - Fixed share function, that was previously broken because of Glide library
34 |
35 | ## 4.3
36 |
37 | - Compatibility with Android 6
38 | - Migrated from Android UIL to Glide library
39 | - Fixed some typos and translation issues
40 |
41 | ## 4.2
42 |
43 | - Fixed crashes on missing network connection
44 |
45 | ## 4.1
46 |
47 | - Removed settings "High quality", "Mobile networks"
48 | - Now compatible with new dilbert.com website design
49 | - Refresh now removes cached image in both memory and disk (fixes problem of broken image downloads)
50 | - Image is now loaded directly, if the URL is already cached, not waiting/spawning thread in AsyncTask
51 | - Handling new dilbert.com URL pattern
52 |
53 | ## 4.0
54 |
55 | - Support for Android Lollipop (5.0)
56 | - Material Design
57 | - Dark backgrounds by default
58 | - Fixed crash in changing download directory
59 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 |
2 | Apache License
3 | Version 2.0, January 2004
4 | http://www.apache.org/licenses/
5 |
6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7 |
8 | 1. Definitions.
9 |
10 | "License" shall mean the terms and conditions for use, reproduction,
11 | and distribution as defined by Sections 1 through 9 of this document.
12 |
13 | "Licensor" shall mean the copyright owner or entity authorized by
14 | the copyright owner that is granting the License.
15 |
16 | "Legal Entity" shall mean the union of the acting entity and all
17 | other entities that control, are controlled by, or are under common
18 | control with that entity. For the purposes of this definition,
19 | "control" means (i) the power, direct or indirect, to cause the
20 | direction or management of such entity, whether by contract or
21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
22 | outstanding shares, or (iii) beneficial ownership of such entity.
23 |
24 | "You" (or "Your") shall mean an individual or Legal Entity
25 | exercising permissions granted by this License.
26 |
27 | "Source" form shall mean the preferred form for making modifications,
28 | including but not limited to software source code, documentation
29 | source, and configuration files.
30 |
31 | "Object" form shall mean any form resulting from mechanical
32 | transformation or translation of a Source form, including but
33 | not limited to compiled object code, generated documentation,
34 | and conversions to other media types.
35 |
36 | "Work" shall mean the work of authorship, whether in Source or
37 | Object form, made available under the License, as indicated by a
38 | copyright notice that is included in or attached to the work
39 | (an example is provided in the Appendix below).
40 |
41 | "Derivative Works" shall mean any work, whether in Source or Object
42 | form, that is based on (or derived from) the Work and for which the
43 | editorial revisions, annotations, elaborations, or other modifications
44 | represent, as a whole, an original work of authorship. For the purposes
45 | of this License, Derivative Works shall not include works that remain
46 | separable from, or merely link (or bind by name) to the interfaces of,
47 | the Work and Derivative Works thereof.
48 |
49 | "Contribution" shall mean any work of authorship, including
50 | the original version of the Work and any modifications or additions
51 | to that Work or Derivative Works thereof, that is intentionally
52 | submitted to Licensor for inclusion in the Work by the copyright owner
53 | or by an individual or Legal Entity authorized to submit on behalf of
54 | the copyright owner. For the purposes of this definition, "submitted"
55 | means any form of electronic, verbal, or written communication sent
56 | to the Licensor or its representatives, including but not limited to
57 | communication on electronic mailing lists, source code control systems,
58 | and issue tracking systems that are managed by, or on behalf of, the
59 | Licensor for the purpose of discussing and improving the Work, but
60 | excluding communication that is conspicuously marked or otherwise
61 | designated in writing by the copyright owner as "Not a Contribution."
62 |
63 | "Contributor" shall mean Licensor and any individual or Legal Entity
64 | on behalf of whom a Contribution has been received by Licensor and
65 | subsequently incorporated within the Work.
66 |
67 | 2. Grant of Copyright License. Subject to the terms and conditions of
68 | this License, each Contributor hereby grants to You a perpetual,
69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70 | copyright license to reproduce, prepare Derivative Works of,
71 | publicly display, publicly perform, sublicense, and distribute the
72 | Work and such Derivative Works in Source or Object form.
73 |
74 | 3. Grant of Patent License. Subject to the terms and conditions of
75 | this License, each Contributor hereby grants to You a perpetual,
76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77 | (except as stated in this section) patent license to make, have made,
78 | use, offer to sell, sell, import, and otherwise transfer the Work,
79 | where such license applies only to those patent claims licensable
80 | by such Contributor that are necessarily infringed by their
81 | Contribution(s) alone or by combination of their Contribution(s)
82 | with the Work to which such Contribution(s) was submitted. If You
83 | institute patent litigation against any entity (including a
84 | cross-claim or counterclaim in a lawsuit) alleging that the Work
85 | or a Contribution incorporated within the Work constitutes direct
86 | or contributory patent infringement, then any patent licenses
87 | granted to You under this License for that Work shall terminate
88 | as of the date such litigation is filed.
89 |
90 | 4. Redistribution. You may reproduce and distribute copies of the
91 | Work or Derivative Works thereof in any medium, with or without
92 | modifications, and in Source or Object form, provided that You
93 | meet the following conditions:
94 |
95 | (a) You must give any other recipients of the Work or
96 | Derivative Works a copy of this License; and
97 |
98 | (b) You must cause any modified files to carry prominent notices
99 | stating that You changed the files; and
100 |
101 | (c) You must retain, in the Source form of any Derivative Works
102 | that You distribute, all copyright, patent, trademark, and
103 | attribution notices from the Source form of the Work,
104 | excluding those notices that do not pertain to any part of
105 | the Derivative Works; and
106 |
107 | (d) If the Work includes a "NOTICE" text file as part of its
108 | distribution, then any Derivative Works that You distribute must
109 | include a readable copy of the attribution notices contained
110 | within such NOTICE file, excluding those notices that do not
111 | pertain to any part of the Derivative Works, in at least one
112 | of the following places: within a NOTICE text file distributed
113 | as part of the Derivative Works; within the Source form or
114 | documentation, if provided along with the Derivative Works; or,
115 | within a display generated by the Derivative Works, if and
116 | wherever such third-party notices normally appear. The contents
117 | of the NOTICE file are for informational purposes only and
118 | do not modify the License. You may add Your own attribution
119 | notices within Derivative Works that You distribute, alongside
120 | or as an addendum to the NOTICE text from the Work, provided
121 | that such additional attribution notices cannot be construed
122 | as modifying the License.
123 |
124 | You may add Your own copyright statement to Your modifications and
125 | may provide additional or different license terms and conditions
126 | for use, reproduction, or distribution of Your modifications, or
127 | for any such Derivative Works as a whole, provided Your use,
128 | reproduction, and distribution of the Work otherwise complies with
129 | the conditions stated in this License.
130 |
131 | 5. Submission of Contributions. Unless You explicitly state otherwise,
132 | any Contribution intentionally submitted for inclusion in the Work
133 | by You to the Licensor shall be under the terms and conditions of
134 | this License, without any additional terms or conditions.
135 | Notwithstanding the above, nothing herein shall supersede or modify
136 | the terms of any separate license agreement you may have executed
137 | with Licensor regarding such Contributions.
138 |
139 | 6. Trademarks. This License does not grant permission to use the trade
140 | names, trademarks, service marks, or product names of the Licensor,
141 | except as required for reasonable and customary use in describing the
142 | origin of the Work and reproducing the content of the NOTICE file.
143 |
144 | 7. Disclaimer of Warranty. Unless required by applicable law or
145 | agreed to in writing, Licensor provides the Work (and each
146 | Contributor provides its Contributions) on an "AS IS" BASIS,
147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148 | implied, including, without limitation, any warranties or conditions
149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150 | PARTICULAR PURPOSE. You are solely responsible for determining the
151 | appropriateness of using or redistributing the Work and assume any
152 | risks associated with Your exercise of permissions under this License.
153 |
154 | 8. Limitation of Liability. In no event and under no legal theory,
155 | whether in tort (including negligence), contract, or otherwise,
156 | unless required by applicable law (such as deliberate and grossly
157 | negligent acts) or agreed to in writing, shall any Contributor be
158 | liable to You for damages, including any direct, indirect, special,
159 | incidental, or consequential damages of any character arising as a
160 | result of this License or out of the use or inability to use the
161 | Work (including but not limited to damages for loss of goodwill,
162 | work stoppage, computer failure or malfunction, or any and all
163 | other commercial damages or losses), even if such Contributor
164 | has been advised of the possibility of such damages.
165 |
166 | 9. Accepting Warranty or Additional Liability. While redistributing
167 | the Work or Derivative Works thereof, You may choose to offer,
168 | and charge a fee for, acceptance of support, warranty, indemnity,
169 | or other liability obligations and/or rights consistent with this
170 | License. However, in accepting such obligations, You may act only
171 | on Your own behalf and on Your sole responsibility, not on behalf
172 | of any other Contributor, and only if You agree to indemnify,
173 | defend, and hold each Contributor harmless for any liability
174 | incurred by, or claims asserted against, such Contributor by reason
175 | of your accepting any such warranty or additional liability.
176 |
177 | END OF TERMS AND CONDITIONS
178 |
179 | APPENDIX: How to apply the Apache License to your work.
180 |
181 | To apply the Apache License to your work, attach the following
182 | boilerplate notice, with the fields enclosed by brackets "[]"
183 | replaced with your own identifying information. (Don't include
184 | the brackets!) The text should be enclosed in the appropriate
185 | comment syntax for the file format. We also recommend that a
186 | file or class name and description of purpose be included on the
187 | same "printed page" as the copyright notice for easier
188 | identification within third-party archives.
189 |
190 | Copyright [yyyy] [name of copyright owner]
191 |
192 | Licensed under the Apache License, Version 2.0 (the "License");
193 | you may not use this file except in compliance with the License.
194 | You may obtain a copy of the License at
195 |
196 | http://www.apache.org/licenses/LICENSE-2.0
197 |
198 | Unless required by applicable law or agreed to in writing, software
199 | distributed under the License is distributed on an "AS IS" BASIS,
200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
201 | See the License for the specific language governing permissions and
202 | limitations under the License.
203 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Simple Dilbert 
2 |
3 | *Due to copyright infridgement application was depublished from Google Play, but will be still available on F-Droid FOSS repository*
4 |
5 | Go to download by clicking the image:
6 | [](https://f-droid.org/repository/browse/?fdid=com.mareksebera.simpledilbert)
7 |
8 | Whole project is published under Apache 2.0 license
9 |
10 | Feel free to create ports and/or custom versions.
11 |
12 | I'll be happy if anybody wants to improve this application.
13 |
14 | Package keys are hidden, so if you're interested in updating my uploaded application, just give me know after changes are pulled into master.
15 |
16 | Screenshots can be seen on gh-pages: http://smarek.github.io/Simple-Dilbert/
17 |
18 | Javadoc is published on gh-pages repository and available on: http://smarek.github.io/Simple-Dilbert/javadoc/
19 |
20 | ****
21 |
22 | # Donations
23 |
24 | - PayPal: marek.sebera@gmail.com
25 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | repositories {
3 | google()
4 | jcenter()
5 | maven { url "https://oss.sonatype.org/content/repositories/snapshots" }
6 | }
7 | dependencies {
8 | classpath 'com.android.tools.build:gradle:4.1.1'
9 | classpath 'com.github.ben-manes:gradle-versions-plugin:0.36.0'
10 | classpath 'com.vanniktech:gradle-android-javadoc-plugin:0.3.0'
11 | }
12 | }
13 |
14 | repositories {
15 | google()
16 | mavenCentral()
17 | jcenter()
18 | maven { url "https://jitpack.io" }
19 | }
20 |
21 | apply plugin: 'com.vanniktech.android.javadoc'
22 | apply plugin: 'com.android.application'
23 | apply plugin: 'com.github.ben-manes.versions'
24 |
25 | dependencies {
26 | implementation 'com.intellij:annotations:12.0'
27 | implementation 'com.github.chrisbanes:PhotoView:2.3.0'
28 | implementation 'joda-time:joda-time:2.10.8'
29 | implementation 'com.android.volley:volley:1.1.1'
30 | implementation 'androidx.appcompat:appcompat:1.2.0'
31 | implementation 'org.apache.commons:commons-text:1.9'
32 | implementation 'com.github.bumptech.glide:glide:4.11.0'
33 | implementation 'androidx.localbroadcastmanager:localbroadcastmanager:1.0.0'
34 | implementation 'androidx.preference:preference:1.1.1'
35 | }
36 |
37 | android {
38 | compileSdkVersion 29
39 |
40 | buildTypes {
41 | release {
42 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-android.pro'
43 | minifyEnabled true
44 | shrinkResources true
45 | }
46 | }
47 |
48 | defaultConfig {
49 | minSdkVersion 14
50 | targetSdkVersion 29
51 | versionName "4.7"
52 | versionCode 40
53 | applicationId "com.mareksebera.simpledilbert"
54 | }
55 |
56 | lintOptions {
57 | warningsAsErrors false
58 | abortOnError false
59 | ignore 'InvalidPackage', 'RtlHardcoded', 'UnusedAttribute'
60 | }
61 |
62 | compileOptions {
63 | sourceCompatibility JavaVersion.VERSION_1_8
64 | targetCompatibility JavaVersion.VERSION_1_8
65 | }
66 |
67 | packagingOptions {
68 | exclude 'META-INF/LICENSE.txt'
69 | exclude 'META-INF/NOTICE.txt'
70 | }
71 | }
72 |
73 | allprojects {
74 | gradle.projectsEvaluated {
75 | tasks.withType(JavaCompile) {
76 | options.compilerArgs << "-Xlint:unchecked" << "-Xlint:deprecation"
77 | }
78 | }
79 | }
80 |
81 |
82 |
--------------------------------------------------------------------------------
/f_droid_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smarek/Simple-Dilbert/5aafd77a4968e2b00f987760f0b914019fa27249/f_droid_logo.png
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | android.useAndroidX=true
2 | android.enableJetifier=true
3 | org.gradle.caching = true
4 | org.gradle.vfs.watch = true
5 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smarek/Simple-Dilbert/5aafd77a4968e2b00f987760f0b914019fa27249/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Wed Nov 11 14:55:21 CET 2020
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-6.7-all.zip
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | #
4 | # Copyright 2015 the original author or authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ##############################################################################
20 | ##
21 | ## Gradle start up script for UN*X
22 | ##
23 | ##############################################################################
24 |
25 | # Attempt to set APP_HOME
26 | # Resolve links: $0 may be a link
27 | PRG="$0"
28 | # Need this for relative symlinks.
29 | while [ -h "$PRG" ] ; do
30 | ls=`ls -ld "$PRG"`
31 | link=`expr "$ls" : '.*-> \(.*\)$'`
32 | if expr "$link" : '/.*' > /dev/null; then
33 | PRG="$link"
34 | else
35 | PRG=`dirname "$PRG"`"/$link"
36 | fi
37 | done
38 | SAVED="`pwd`"
39 | cd "`dirname \"$PRG\"`/" >/dev/null
40 | APP_HOME="`pwd -P`"
41 | cd "$SAVED" >/dev/null
42 |
43 | APP_NAME="Gradle"
44 | APP_BASE_NAME=`basename "$0"`
45 |
46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
48 |
49 | # Use the maximum available, or set MAX_FD != -1 to use that value.
50 | MAX_FD="maximum"
51 |
52 | warn () {
53 | echo "$*"
54 | }
55 |
56 | die () {
57 | echo
58 | echo "$*"
59 | echo
60 | exit 1
61 | }
62 |
63 | # OS specific support (must be 'true' or 'false').
64 | cygwin=false
65 | msys=false
66 | darwin=false
67 | nonstop=false
68 | case "`uname`" in
69 | CYGWIN* )
70 | cygwin=true
71 | ;;
72 | Darwin* )
73 | darwin=true
74 | ;;
75 | MINGW* )
76 | msys=true
77 | ;;
78 | NONSTOP* )
79 | nonstop=true
80 | ;;
81 | esac
82 |
83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
84 |
85 |
86 | # Determine the Java command to use to start the JVM.
87 | if [ -n "$JAVA_HOME" ] ; then
88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
89 | # IBM's JDK on AIX uses strange locations for the executables
90 | JAVACMD="$JAVA_HOME/jre/sh/java"
91 | else
92 | JAVACMD="$JAVA_HOME/bin/java"
93 | fi
94 | if [ ! -x "$JAVACMD" ] ; then
95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
96 |
97 | Please set the JAVA_HOME variable in your environment to match the
98 | location of your Java installation."
99 | fi
100 | else
101 | JAVACMD="java"
102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
103 |
104 | Please set the JAVA_HOME variable in your environment to match the
105 | location of your Java installation."
106 | fi
107 |
108 | # Increase the maximum file descriptors if we can.
109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
110 | MAX_FD_LIMIT=`ulimit -H -n`
111 | if [ $? -eq 0 ] ; then
112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
113 | MAX_FD="$MAX_FD_LIMIT"
114 | fi
115 | ulimit -n $MAX_FD
116 | if [ $? -ne 0 ] ; then
117 | warn "Could not set maximum file descriptor limit: $MAX_FD"
118 | fi
119 | else
120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
121 | fi
122 | fi
123 |
124 | # For Darwin, add options to specify how the application appears in the dock
125 | if $darwin; then
126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
127 | fi
128 |
129 | # For Cygwin or MSYS, switch paths to Windows format before running java
130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
133 |
134 | JAVACMD=`cygpath --unix "$JAVACMD"`
135 |
136 | # We build the pattern for arguments to be converted via cygpath
137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
138 | SEP=""
139 | for dir in $ROOTDIRSRAW ; do
140 | ROOTDIRS="$ROOTDIRS$SEP$dir"
141 | SEP="|"
142 | done
143 | OURCYGPATTERN="(^($ROOTDIRS))"
144 | # Add a user-defined pattern to the cygpath arguments
145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
147 | fi
148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
149 | i=0
150 | for arg in "$@" ; do
151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
153 |
154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
156 | else
157 | eval `echo args$i`="\"$arg\""
158 | fi
159 | i=`expr $i + 1`
160 | done
161 | case $i in
162 | 0) set -- ;;
163 | 1) set -- "$args0" ;;
164 | 2) set -- "$args0" "$args1" ;;
165 | 3) set -- "$args0" "$args1" "$args2" ;;
166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
172 | esac
173 | fi
174 |
175 | # Escape application args
176 | save () {
177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
178 | echo " "
179 | }
180 | APP_ARGS=`save "$@"`
181 |
182 | # Collect all arguments for the java command, following the shell quoting and substitution rules
183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
184 |
185 | exec "$JAVACMD" "$@"
186 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%" == "" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%" == "" set DIRNAME=.
29 | set APP_BASE_NAME=%~n0
30 | set APP_HOME=%DIRNAME%
31 |
32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
34 |
35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
37 |
38 | @rem Find java.exe
39 | if defined JAVA_HOME goto findJavaFromJavaHome
40 |
41 | set JAVA_EXE=java.exe
42 | %JAVA_EXE% -version >NUL 2>&1
43 | if "%ERRORLEVEL%" == "0" goto execute
44 |
45 | echo.
46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
47 | echo.
48 | echo Please set the JAVA_HOME variable in your environment to match the
49 | echo location of your Java installation.
50 |
51 | goto fail
52 |
53 | :findJavaFromJavaHome
54 | set JAVA_HOME=%JAVA_HOME:"=%
55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
56 |
57 | if exist "%JAVA_EXE%" goto execute
58 |
59 | echo.
60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
61 | echo.
62 | echo Please set the JAVA_HOME variable in your environment to match the
63 | echo location of your Java installation.
64 |
65 | goto fail
66 |
67 | :execute
68 | @rem Setup the command line
69 |
70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
71 |
72 |
73 | @rem Execute Gradle
74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
75 |
76 | :end
77 | @rem End local scope for the variables with windows NT shell
78 | if "%ERRORLEVEL%"=="0" goto mainEnd
79 |
80 | :fail
81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
82 | rem the _cmd.exe /c_ return code!
83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
84 | exit /b 1
85 |
86 | :mainEnd
87 | if "%OS%"=="Windows_NT" endlocal
88 |
89 | :omega
90 |
--------------------------------------------------------------------------------
/proguard-android.pro:
--------------------------------------------------------------------------------
1 | -dontwarn com.google.common.collect.*
2 | -dontwarn com.google.common.reflect.*
3 | -dontwarn org.joda.convert.**
4 | -dontwarn javax.script.ScriptEngineManager
5 | -dontwarn javax.script.ScriptEngine
6 |
--------------------------------------------------------------------------------
/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 |
7 |
14 |
19 |
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 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
--------------------------------------------------------------------------------
/src/main/assets/LICENSE.txt:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/src/main/java/com/mareksebera/simpledilbert/AppController.java:
--------------------------------------------------------------------------------
1 | package com.mareksebera.simpledilbert;
2 |
3 | import android.app.Application;
4 | import android.view.ViewConfiguration;
5 |
6 | import com.mareksebera.simpledilbert.preferences.DilbertPreferences;
7 |
8 | import org.joda.time.DateTimeZone;
9 |
10 | import java.lang.reflect.Field;
11 |
12 | public final class AppController extends Application {
13 |
14 | static {
15 | /**
16 | * Set default time-zone, because strips are published in New York
17 | * timezone on midnight
18 | * */
19 | DateTimeZone.setDefault(DilbertPreferences.TIME_ZONE);
20 | }
21 |
22 | @Override
23 | public void onCreate() {
24 | super.onCreate();
25 | forceMenuOverflow();
26 | }
27 |
28 | private void forceMenuOverflow() {
29 | try {
30 | ViewConfiguration config = ViewConfiguration.get(this);
31 | Field menuKeyField = ViewConfiguration.class
32 | .getDeclaredField("sHasPermanentMenuKey");
33 | if (menuKeyField != null) {
34 | menuKeyField.setAccessible(true);
35 | menuKeyField.setBoolean(config, false);
36 | }
37 | } catch (Throwable ignored) {
38 | }
39 | }
40 |
41 | }
42 |
--------------------------------------------------------------------------------
/src/main/java/com/mareksebera/simpledilbert/core/DilbertFragment.java:
--------------------------------------------------------------------------------
1 | package com.mareksebera.simpledilbert.core;
2 |
3 | import android.content.Context;
4 | import android.content.Intent;
5 | import android.graphics.Bitmap;
6 | import android.graphics.Bitmap.CompressFormat;
7 | import android.graphics.drawable.Drawable;
8 | import android.net.Uri;
9 | import android.os.AsyncTask.Status;
10 | import android.os.Bundle;
11 | import android.util.Log;
12 | import android.view.LayoutInflater;
13 | import android.view.Menu;
14 | import android.view.MenuInflater;
15 | import android.view.MenuItem;
16 | import android.view.View;
17 | import android.view.View.OnLongClickListener;
18 | import android.view.ViewGroup;
19 | import android.widget.ProgressBar;
20 | import android.widget.Toast;
21 |
22 | import androidx.annotation.NonNull;
23 | import androidx.annotation.Nullable;
24 | import androidx.core.content.FileProvider;
25 | import androidx.fragment.app.Fragment;
26 | import androidx.fragment.app.FragmentActivity;
27 | import androidx.localbroadcastmanager.content.LocalBroadcastManager;
28 |
29 | import com.bumptech.glide.Glide;
30 | import com.bumptech.glide.load.DataSource;
31 | import com.bumptech.glide.load.engine.DiskCacheStrategy;
32 | import com.bumptech.glide.load.engine.GlideException;
33 | import com.bumptech.glide.request.RequestListener;
34 | import com.bumptech.glide.request.RequestOptions;
35 | import com.bumptech.glide.request.target.Target;
36 | import com.bumptech.glide.request.transition.Transition;
37 | import com.github.chrisbanes.photoview.OnPhotoTapListener;
38 | import com.github.chrisbanes.photoview.PhotoView;
39 | import com.mareksebera.simpledilbert.R;
40 | import com.mareksebera.simpledilbert.favorites.DilbertFavoritedActivity;
41 | import com.mareksebera.simpledilbert.preferences.DilbertPreferences;
42 | import com.mareksebera.simpledilbert.utilities.CustomTarget;
43 | import com.mareksebera.simpledilbert.utilities.GetStripUrl;
44 | import com.mareksebera.simpledilbert.utilities.GetStripUrlInterface;
45 |
46 | import org.joda.time.LocalDate;
47 |
48 | import java.io.File;
49 | import java.io.FileOutputStream;
50 |
51 | import static android.view.MenuItem.SHOW_AS_ACTION_IF_ROOM;
52 |
53 | public final class DilbertFragment extends Fragment {
54 |
55 | static final String BROADCAST_TITLE_UPDATE = "com.mareksebera.simpledilbert.broadcast.TITLE";
56 | public static final String ARGUMENT_DATE = "string_ARGUMENT_DATE";
57 | private static final int MENU_SAVE = -1, MENU_FAVORITE = -2,
58 | MENU_ZOOM = -3, MENU_SHARE = -4, MENU_REFRESH = -5, MENU_OPEN_AT = -6, MENU_OPEN_IN_BROWSER = -7;
59 | private final OnLongClickListener imageLongClickListener = v -> {
60 | if (getActivity() != null) {
61 | FragmentActivity sfa = getActivity();
62 | if (sfa instanceof DilbertFragmentInterface) {
63 | ((DilbertFragmentInterface) sfa).toggleActionBar();
64 | }
65 | }
66 | return true;
67 | };
68 | private PhotoView image;
69 | private ProgressBar progress;
70 | private final RequestListener dilbertImageLoadingListener = new RequestListener() {
71 | @Override
72 | public boolean onLoadFailed(@Nullable GlideException e, Object model, Target target, boolean isFirstResource) {
73 | if (image != null) {
74 | image.setImageResource(R.drawable.cancel);
75 | }
76 | if (progress != null) {
77 | progress.setVisibility(View.GONE);
78 | }
79 | if (getActivity() != null)
80 | Toast.makeText(getActivity(),
81 | R.string.loading_exception_error, Toast.LENGTH_SHORT)
82 | .show();
83 | return false;
84 | }
85 |
86 | @Override
87 | public boolean onResourceReady(Bitmap resource, Object model, Target target, DataSource dataSource, boolean isFirstResource) {
88 | if (progress != null) {
89 | progress.setVisibility(View.GONE);
90 | }
91 | applyZoomLevel();
92 | return false;
93 | }
94 | };
95 | private final GetStripUrlInterface getStripURIlListener = new GetStripUrlInterface() {
96 |
97 | @Override
98 | public void imageLoadFailed(String url, Throwable reason) {
99 | dilbertImageLoadingListener.onLoadFailed(null, url, null, true);
100 | }
101 |
102 | @Override
103 | public void displayImage(String url, String title) {
104 | if (image == null || getArguments() == null)
105 | return;
106 | Log.d("GetStripUrlListener", "url: " + url);
107 | if (url != null) {
108 | preferences.saveCurrentUrl(getArguments().getString(ARGUMENT_DATE), url);
109 | preferences.saveCurrentTitle(getArguments().getString(ARGUMENT_DATE), title);
110 | }
111 | Glide.with(DilbertFragment.this.getContext())
112 | .asBitmap()
113 | .load(url)
114 | .apply(new RequestOptions().dontAnimate().fitCenter().diskCacheStrategy(DiskCacheStrategy.ALL).error(R.drawable.cancel))
115 | .listener(dilbertImageLoadingListener)
116 | .into(image);
117 | Context c = getContext();
118 | if (c != null) {
119 | LocalBroadcastManager.getInstance(c).sendBroadcast(new Intent(BROADCAST_TITLE_UPDATE));
120 | }
121 | }
122 | };
123 | private DilbertPreferences preferences;
124 | private GetStripUrl loadTask;
125 | private final OnPhotoTapListener photoTapListener = (view, x, y) -> refreshAction();
126 | private int zoomLevel = 0;
127 |
128 | public DilbertFragment() {
129 | }
130 |
131 | @Override
132 | public void onDestroyView() {
133 | progress = null;
134 | image = null;
135 | super.onDestroyView();
136 | }
137 |
138 | @Override
139 | public void onCreate(Bundle savedInstanceState) {
140 | super.onCreate(savedInstanceState);
141 | this.preferences = new DilbertPreferences(getActivity());
142 | this.zoomLevel = preferences.getDefaultZoomLevel();
143 | setHasOptionsMenu(true);
144 | }
145 |
146 | private LocalDate getDateFromArguments() {
147 | return LocalDate.parse(getArguments().getString(ARGUMENT_DATE),
148 | DilbertPreferences.DATE_FORMATTER);
149 | }
150 |
151 | @Override
152 | public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
153 | Bundle savedInstanceState) {
154 | View fragment = inflater.inflate(R.layout.fragment_dilbert, container,
155 | false);
156 | assert fragment != null;
157 | this.image = fragment.findViewById(R.id.fragment_imageview);
158 | this.image.setOnLongClickListener(imageLongClickListener);
159 | fragment.setOnLongClickListener(imageLongClickListener);
160 | this.image.setOnPhotoTapListener(photoTapListener);
161 | this.progress = fragment
162 | .findViewById(R.id.fragment_progressbar);
163 | String cachedUrl = preferences.getCachedUrl(getDateFromArguments());
164 | String cachedTitle = preferences.getCachedTitle(getDateFromArguments());
165 | if (null != cachedUrl) {
166 | getStripURIlListener.displayImage(cachedUrl, cachedTitle);
167 | } else {
168 | this.loadTask = new GetStripUrl(getContext(), getStripURIlListener, preferences,
169 | getDateFromArguments());
170 | this.loadTask.execute();
171 | }
172 | return fragment;
173 | }
174 |
175 | @Override
176 | public void onPrepareOptionsMenu(Menu menu) {
177 | if (menu.findItem(MENU_FAVORITE) != null) {
178 | MenuItem favorite = menu.findItem(MENU_FAVORITE);
179 | modifyFavoriteItem(favorite);
180 | }
181 | }
182 |
183 | private void modifyFavoriteItem(MenuItem favorite) {
184 | boolean isFavorite = preferences.isFavorited(getDateFromArguments());
185 | favorite.setTitle(isFavorite ? R.string.menu_favorite_remove
186 | : R.string.menu_favorite_add);
187 | favorite.setIcon(isFavorite ? R.drawable.ic_menu_favorited
188 | : R.drawable.ic_menu_not_favorited);
189 | }
190 |
191 | @Override
192 | public boolean onOptionsItemSelected(MenuItem item) {
193 | switch (item.getItemId()) {
194 | case MENU_FAVORITE:
195 | preferences.toggleIsFavorited(getDateFromArguments());
196 | modifyFavoriteItem(item);
197 | return true;
198 | case MENU_ZOOM:
199 | applyZoomLevel();
200 | return true;
201 | case MENU_SHARE:
202 | shareCurrentStrip();
203 | return true;
204 | case MENU_REFRESH:
205 | refreshAction();
206 | return true;
207 | case MENU_OPEN_AT:
208 | preferences.saveCurrentDate(getDateFromArguments());
209 | if (getActivity() != null) {
210 | getActivity().finish();
211 | }
212 | return true;
213 | case MENU_OPEN_IN_BROWSER:
214 | try {
215 | startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(preferences.getCachedUrl(getDateFromArguments()))));
216 | } catch (Throwable t) {
217 | Log.e("DilbertFragment", "Cannot ACTION_VIEW url", t);
218 | }
219 | return true;
220 | case MENU_SAVE:
221 | preferences.downloadImageViaManager(getActivity(),
222 | preferences.getCachedUrl(getDateFromArguments()),
223 | getDateFromArguments());
224 | return true;
225 | }
226 | return super.onOptionsItemSelected(item);
227 | }
228 |
229 | private void applyZoomLevel() {
230 | if (image != null && image.isZoomable()) {
231 | final float scale;
232 | switch (zoomLevel) {
233 | case 0:
234 | default:
235 | scale = image.getMinimumScale();
236 | break;
237 | case 1:
238 | scale = image.getMediumScale();
239 | break;
240 | case 2:
241 | scale = image.getMaximumScale();
242 | break;
243 | }
244 | image.setScale(scale, 0, 0, true);
245 | }
246 | }
247 |
248 | private void refreshAction() {
249 | Glide.get(getContext()).clearMemory();
250 | preferences.removeCache(getDateFromArguments());
251 | if (this.loadTask == null
252 | || this.loadTask.getStatus() != Status.PENDING) {
253 | this.loadTask = new GetStripUrl(getContext(), getStripURIlListener, preferences,
254 | getDateFromArguments(), progress);
255 | }
256 | this.loadTask.execute();
257 | }
258 |
259 | private void shareCurrentStrip() {
260 | // Share only text
261 | String date = getArguments().getString(ARGUMENT_DATE);
262 | if (preferences.isSharingImage()) {
263 | Intent i = new Intent(Intent.ACTION_SEND);
264 | i.setType("text/plain");
265 | i.putExtra(Intent.EXTRA_SUBJECT, "Dilbert " + date
266 | + " #simpledilbert");
267 | i.putExtra(
268 | Intent.EXTRA_TEXT,
269 | "Dilbert "
270 | + date
271 | + " #simpledilbert https://dilbert.com/strip/"
272 | + date
273 | );
274 | startActivity(Intent.createChooser(i,
275 | getString(R.string.share_chooser)));
276 | return;
277 | }
278 |
279 | // Share image
280 | String url = preferences.getCachedUrl(getDateFromArguments());
281 | if (url == null) {
282 | Log.d("DilbertFragment", "Will not share null URL");
283 | return;
284 | }
285 | Glide.with(DilbertFragment.this.getContext()).asBitmap().load(url)
286 | .into(new CustomTarget() {
287 | @Override
288 | public void onResourceReady(@NonNull Bitmap resource, @Nullable Transition super Bitmap> transition) {
289 | try {
290 | String date = getArguments().getString(ARGUMENT_DATE);
291 | Intent i = new Intent(Intent.ACTION_SEND);
292 | i.setType("image/jpeg");
293 | i.putExtra(Intent.EXTRA_SUBJECT, "Dilbert " + date
294 | + " #simpledilbert");
295 | i.putExtra(Intent.EXTRA_TEXT, "Dilbert " + date
296 | + " #simpledilbert");
297 | File tmp = File
298 | .createTempFile("dilbert_", ".jpg",
299 | getActivity()
300 | .getExternalCacheDir()
301 | );
302 | FileOutputStream out = new FileOutputStream(tmp);
303 | resource.compress(CompressFormat.JPEG, 100, out);
304 | out.close();
305 | FragmentActivity activity = getActivity();
306 | Uri u = FileProvider.getUriForFile(activity, activity.getPackageName() + ".provider", tmp);
307 | i.putExtra(Intent.EXTRA_STREAM, u);
308 | startActivity(Intent.createChooser(i,
309 | getString(R.string.share_chooser)));
310 |
311 | } catch (Throwable e) {
312 | if (getActivity() != null)
313 | Toast.makeText(getActivity(),
314 | R.string.loading_exception_error,
315 | Toast.LENGTH_LONG).show();
316 | }
317 | }
318 |
319 | @Override
320 | public void onLoadCleared(@Nullable Drawable placeholder) {
321 |
322 | }
323 | }
324 | );
325 | }
326 |
327 | @Override
328 | public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
329 | final int category = 0;
330 | menu.add(category, MENU_FAVORITE, 1, R.string.menu_favorite_remove)
331 | .setIcon(R.drawable.ic_menu_not_favorited).setShowAsAction(SHOW_AS_ACTION_IF_ROOM);
332 | menu.add(category, MENU_ZOOM, 4, R.string.menu_zoom)
333 | .setIcon(R.drawable.ic_menu_zoom).setShowAsAction(SHOW_AS_ACTION_IF_ROOM);
334 | menu.add(category, MENU_SAVE, 3, R.string.menu_download)
335 | .setIcon(R.drawable.ic_menu_save).setShowAsAction(SHOW_AS_ACTION_IF_ROOM);
336 | menu.add(category, MENU_SHARE, 2, R.string.menu_share)
337 | .setIcon(R.drawable.ic_menu_share).setShowAsAction(SHOW_AS_ACTION_IF_ROOM);
338 |
339 | if (getActivity() != null && getActivity() instanceof DilbertFavoritedActivity) {
340 | menu.add(category, MENU_OPEN_AT, 5, R.string.menu_open_at)
341 | .setIcon(R.drawable.ic_menu_open_at).setShowAsAction(SHOW_AS_ACTION_IF_ROOM);
342 | }
343 |
344 | menu.add(category, MENU_REFRESH, 4, R.string.menu_refresh)
345 | .setIcon(R.drawable.ic_menu_refresh).setShowAsAction(SHOW_AS_ACTION_IF_ROOM);
346 | menu.add(category, MENU_OPEN_IN_BROWSER, 3, R.string.menu_open_in_browser)
347 | .setIcon(R.drawable.ic_menu_open_at).setShowAsAction(SHOW_AS_ACTION_IF_ROOM);
348 | }
349 |
350 | }
351 |
--------------------------------------------------------------------------------
/src/main/java/com/mareksebera/simpledilbert/core/DilbertFragmentActivity.java:
--------------------------------------------------------------------------------
1 | package com.mareksebera.simpledilbert.core;
2 |
3 | import android.app.DatePickerDialog;
4 | import android.content.BroadcastReceiver;
5 | import android.content.Context;
6 | import android.content.Intent;
7 | import android.content.IntentFilter;
8 | import android.net.Uri;
9 | import android.os.Bundle;
10 | import android.view.Menu;
11 | import android.view.MenuItem;
12 |
13 | import com.mareksebera.simpledilbert.R;
14 | import com.mareksebera.simpledilbert.favorites.DilbertFavoritedActivity;
15 | import com.mareksebera.simpledilbert.preferences.DilbertPreferences;
16 | import com.mareksebera.simpledilbert.preferences.DilbertPreferencesActivity;
17 | import com.mareksebera.simpledilbert.utilities.ActionBarUtility;
18 | import com.mareksebera.simpledilbert.utilities.FindUrls;
19 |
20 | import org.joda.time.LocalDate;
21 |
22 | import java.util.Calendar;
23 | import java.util.Locale;
24 |
25 | import androidx.appcompat.app.AppCompatActivity;
26 | import androidx.localbroadcastmanager.content.LocalBroadcastManager;
27 | import androidx.viewpager.widget.ViewPager;
28 |
29 | import static android.view.MenuItem.SHOW_AS_ACTION_IF_ROOM;
30 | import static android.view.MenuItem.SHOW_AS_ACTION_NEVER;
31 |
32 | public final class
33 | DilbertFragmentActivity extends AppCompatActivity implements DilbertFragmentInterface {
34 |
35 | private static final int MENU_DATEPICKER = 1, MENU_LATEST = 3, MENU_OLDEST = 4,
36 | MENU_SHOW_FAVORITES = 5, MENU_SHUFFLE = 6, MENU_SETTINGS = 7, MENU_SHOW_OFFLINE = 8;
37 | private ViewPager viewPager;
38 | private DilbertFragmentAdapter adapter;
39 | private DilbertPreferences preferences;
40 | private final DatePickerDialog.OnDateSetListener dilbertOnDateSetListener = (view, year, monthOfYear, dayOfMonth) -> {
41 | LocalDate selDate = LocalDate.parse(String.format(new Locale(
42 | "en"), "%d-%d-%d", year, monthOfYear + 1, dayOfMonth),
43 | DilbertPreferences.DATE_FORMATTER);
44 | setCurrentDate(selDate);
45 | };
46 | private final ViewPager.OnPageChangeListener pageChangedListener = new ViewPager.OnPageChangeListener() {
47 |
48 | @Override
49 | public void onPageSelected(int position) {
50 | preferences.saveCurrentDate(adapter.getDateForPosition(position));
51 | setTitle(adapter.getPageTitle(position));
52 | }
53 |
54 | @Override
55 | public void onPageScrolled(int arg0, float arg1, int arg2) {
56 | }
57 |
58 | @Override
59 | public void onPageScrollStateChanged(int arg0) {
60 | }
61 | };
62 | private final BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
63 | @Override
64 | public void onReceive(Context context, Intent intent) {
65 | setTitle(adapter.getPageTitle(viewPager.getCurrentItem()));
66 | }
67 | };
68 |
69 | private void setCurrentDate(LocalDate date) {
70 | preferences.saveCurrentDate(date);
71 | viewPager.setCurrentItem(adapter.getPositionForDate(date));
72 | }
73 |
74 | @Override
75 | protected void onCreate(Bundle savedInstance) {
76 | preferences = new DilbertPreferences(this);
77 | setTheme(preferences.isDarkLayoutEnabled() ? R.style.AppThemeDark
78 | : R.style.AppThemeLight);
79 | super.onCreate(savedInstance);
80 | if (preferences.isForceLandscape())
81 | setRequestedOrientation(preferences.getLandscapeOrientation());
82 | setContentView(R.layout.activity_dilbert_fragments);
83 | viewPager = findViewById(R.id.view_pager);
84 | adapter = new DilbertFragmentAdapter(getSupportFragmentManager(), preferences);
85 | viewPager.setAdapter(adapter);
86 | viewPager.addOnPageChangeListener(pageChangedListener);
87 | if (preferences.isToolbarsHidden())
88 | ActionBarUtility.toggleActionBar(this, viewPager);
89 | tryHandleUrlIntent();
90 | }
91 |
92 | private void tryHandleUrlIntent() {
93 | if (getIntent() != null && getIntent().getData() != null) {
94 | Uri path = getIntent().getData();
95 | LocalDate intentDate = FindUrls.extractCurrentDateFromIntentUrl(path);
96 | if (intentDate != null)
97 | setCurrentDate(intentDate);
98 | }
99 | }
100 |
101 | @Override
102 | protected void onResume() {
103 | super.onResume();
104 | LocalBroadcastManager.getInstance(this).registerReceiver(broadcastReceiver, new IntentFilter(DilbertFragment.BROADCAST_TITLE_UPDATE));
105 | viewPager.setCurrentItem(adapter.getPositionForDate(preferences
106 | .getCurrentDate()));
107 | }
108 |
109 | @Override
110 | protected void onPause() {
111 | super.onPause();
112 | LocalBroadcastManager.getInstance(this).unregisterReceiver(broadcastReceiver);
113 | }
114 |
115 | @Override
116 | public boolean onCreateOptionsMenu(Menu menu) {
117 | final int category = 0;
118 | menu.add(category, MENU_DATEPICKER, 1, R.string.menu_datepicker)
119 | .setIcon(R.drawable.ic_menu_datepicker)
120 | .setShowAsAction(SHOW_AS_ACTION_IF_ROOM);
121 | menu.add(category, MENU_SHUFFLE, 2, R.string.menu_random)
122 | .setIcon(R.drawable.ic_menu_shuffle)
123 | .setShowAsAction(SHOW_AS_ACTION_IF_ROOM);
124 | menu.add(category, MENU_SHOW_FAVORITES, 6, R.string.menu_show_favorite)
125 | .setShowAsAction(SHOW_AS_ACTION_NEVER);
126 | menu.add(category, MENU_LATEST, 5, R.string.menu_latest)
127 | .setShowAsAction(SHOW_AS_ACTION_NEVER);
128 | menu.add(category, MENU_OLDEST, 5, R.string.menu_oldest)
129 | .setShowAsAction(SHOW_AS_ACTION_NEVER);
130 | menu.add(category, MENU_SHOW_OFFLINE, 6, R.string.menu_show_offline)
131 | .setShowAsAction(SHOW_AS_ACTION_NEVER);
132 | menu.add(category, MENU_SETTINGS, 8, R.string.menu_settings)
133 | .setShowAsAction(SHOW_AS_ACTION_NEVER);
134 | return super.onCreateOptionsMenu(menu);
135 | }
136 |
137 | private void showDatePicker() {
138 | Calendar c = Calendar.getInstance();
139 | c.setTime(adapter.getDateForPosition(viewPager.getCurrentItem())
140 | .toDate());
141 | DatePickerDialog dialog = new DatePickerDialog(this,
142 | dilbertOnDateSetListener, c.get(Calendar.YEAR),
143 | c.get(Calendar.MONTH), c.get(Calendar.DAY_OF_MONTH));
144 | dialog.show();
145 | }
146 |
147 | @Override
148 | public boolean onOptionsItemSelected(MenuItem item) {
149 | switch (item.getItemId()) {
150 | case MENU_DATEPICKER:
151 | showDatePicker();
152 | return true;
153 | case MENU_LATEST:
154 | setCurrentDate(LocalDate.now());
155 | return true;
156 | case MENU_OLDEST:
157 | setCurrentDate(DilbertPreferences.getFirstStripDate());
158 | return true;
159 | case MENU_SHOW_FAVORITES:
160 | startActivity(new Intent(this, DilbertFavoritedActivity.class));
161 | return true;
162 | case MENU_SHUFFLE:
163 | setCurrentDate(DilbertPreferences.getRandomDate());
164 | return true;
165 | case MENU_SHOW_OFFLINE:
166 | Intent offlineIntent = new Intent(this, DilbertFavoritedActivity.class);
167 | offlineIntent.putExtra(DilbertFavoritedActivity.INTENT_OFFLINE, true);
168 | startActivity(offlineIntent);
169 | return true;
170 | case MENU_SETTINGS:
171 | startActivity(new Intent(this, DilbertPreferencesActivity.class));
172 | finish();
173 | return true;
174 | }
175 | return super.onOptionsItemSelected(item);
176 | }
177 |
178 | // Compat helper method
179 | public void toggleActionBar() {
180 | preferences.setIsToolbarsHidden(!preferences.isToolbarsHidden());
181 | ActionBarUtility.toggleActionBar(this, viewPager);
182 | }
183 |
184 | }
185 |
--------------------------------------------------------------------------------
/src/main/java/com/mareksebera/simpledilbert/core/DilbertFragmentAdapter.java:
--------------------------------------------------------------------------------
1 | package com.mareksebera.simpledilbert.core;
2 |
3 | import android.os.Bundle;
4 |
5 | import com.mareksebera.simpledilbert.preferences.DilbertPreferences;
6 |
7 | import org.joda.time.Days;
8 | import org.joda.time.LocalDate;
9 |
10 | import androidx.fragment.app.Fragment;
11 | import androidx.fragment.app.FragmentManager;
12 | import androidx.fragment.app.FragmentPagerAdapter;
13 |
14 | final class DilbertFragmentAdapter extends FragmentPagerAdapter {
15 |
16 | private int countCache;
17 | private DilbertPreferences preferences;
18 |
19 | DilbertFragmentAdapter(FragmentManager fm, DilbertPreferences preferences) {
20 | super(fm);
21 | this.countCache = Days.daysBetween(
22 | DilbertPreferences.getFirstStripDate(),
23 | LocalDate.now()).getDays() + 1;
24 | this.preferences = preferences;
25 | }
26 |
27 | @Override
28 | public CharSequence getPageTitle(int position) {
29 | String cachedTitle = preferences.getCachedTitle(getDateForPosition(position));
30 | return getDateForPosition(position).toString(
31 | DilbertPreferences.NICE_DATE_FORMATTER) + ((cachedTitle == null || cachedTitle.isEmpty()) ? "" : " : " + cachedTitle);
32 | }
33 |
34 | @Override
35 | public Fragment getItem(int position) {
36 | Fragment f = new DilbertFragment();
37 | Bundle bundle = new Bundle();
38 | bundle.putString(
39 | DilbertFragment.ARGUMENT_DATE,
40 | getDateForPosition(position).toString(
41 | DilbertPreferences.DATE_FORMATTER));
42 | f.setArguments(bundle);
43 | return f;
44 | }
45 |
46 | LocalDate getDateForPosition(int position) {
47 | return LocalDate.now().minusDays(
48 | (getCount() - position) - 1);
49 | }
50 |
51 | @Override
52 | public int getCount() {
53 | return countCache;
54 | }
55 |
56 | int getPositionForDate(LocalDate date) {
57 | return getCount()
58 | - Days.daysBetween(date,
59 | LocalDate.now())
60 | .plus(Days.ONE).getDays();
61 | }
62 |
63 | }
64 |
--------------------------------------------------------------------------------
/src/main/java/com/mareksebera/simpledilbert/core/DilbertFragmentInterface.java:
--------------------------------------------------------------------------------
1 | package com.mareksebera.simpledilbert.core;
2 |
3 | public interface DilbertFragmentInterface {
4 |
5 | void toggleActionBar();
6 |
7 | }
8 |
--------------------------------------------------------------------------------
/src/main/java/com/mareksebera/simpledilbert/favorites/DilbertFavoritedActivity.java:
--------------------------------------------------------------------------------
1 | package com.mareksebera.simpledilbert.favorites;
2 |
3 | import android.os.Bundle;
4 | import android.view.Menu;
5 | import android.view.MenuItem;
6 | import android.widget.Toast;
7 |
8 | import com.mareksebera.simpledilbert.R;
9 | import com.mareksebera.simpledilbert.core.DilbertFragmentInterface;
10 | import com.mareksebera.simpledilbert.preferences.DilbertPreferences;
11 | import com.mareksebera.simpledilbert.utilities.ActionBarUtility;
12 |
13 | import java.util.Random;
14 |
15 | import androidx.appcompat.app.AppCompatActivity;
16 | import androidx.viewpager.widget.ViewPager;
17 |
18 | import static android.view.MenuItem.SHOW_AS_ACTION_IF_ROOM;
19 |
20 | public final class DilbertFavoritedActivity extends AppCompatActivity implements DilbertFragmentInterface {
21 |
22 | public static final String INTENT_OFFLINE = "intent_extra_offline_mode";
23 | private ViewPager viewPager;
24 | private final Random random = new Random();
25 | private DilbertFavoritedFragmentAdapter adapter;
26 | public static final int MENU_RANDOM = 1;
27 |
28 | private final ViewPager.OnPageChangeListener pageChangedListener = new ViewPager.OnPageChangeListener() {
29 |
30 | @Override
31 | public void onPageSelected(int position) {
32 | setTitle(adapter.getPageTitle(position));
33 | }
34 |
35 | @Override
36 | public void onPageScrolled(int arg0, float arg1, int arg2) {
37 | }
38 |
39 | @Override
40 | public void onPageScrollStateChanged(int arg0) {
41 | }
42 | };
43 |
44 | @Override
45 | protected void onCreate(Bundle savedInstance) {
46 | DilbertPreferences preferences = new DilbertPreferences(this);
47 | if (preferences.isForceLandscape())
48 | setRequestedOrientation(preferences.getLandscapeOrientation());
49 | setTheme(preferences.isDarkLayoutEnabled() ? R.style.AppThemeDark
50 | : R.style.AppThemeLight);
51 | super.onCreate(savedInstance);
52 | setContentView(R.layout.activity_dilbert_fragments);
53 | if (getSupportActionBar() != null) {
54 | getSupportActionBar().setDisplayHomeAsUpEnabled(true);
55 | }
56 | boolean isOfflineMode = getIntent().getBooleanExtra(INTENT_OFFLINE, false);
57 | viewPager = findViewById(R.id.view_pager);
58 | adapter = new DilbertFavoritedFragmentAdapter(
59 | getSupportFragmentManager(), isOfflineMode ? preferences.getCachedDates() : preferences.getFavoritedItems());
60 | if (adapter.getCount() == 0) {
61 | Toast.makeText(this, R.string.toast_no_favorites, Toast.LENGTH_LONG)
62 | .show();
63 | finish();
64 | }
65 | viewPager.addOnPageChangeListener(pageChangedListener);
66 | viewPager.setAdapter(adapter);
67 | viewPager.setCurrentItem(adapter.getCount());
68 | if (preferences.isToolbarsHidden())
69 | ActionBarUtility.toggleActionBar(this, viewPager);
70 | }
71 |
72 | @Override
73 | public boolean onCreateOptionsMenu(Menu menu) {
74 | menu.add(0, MENU_RANDOM, 1, R.string.menu_random)
75 | .setIcon(R.drawable.ic_menu_shuffle)
76 | .setShowAsAction(SHOW_AS_ACTION_IF_ROOM);
77 | return super.onCreateOptionsMenu(menu);
78 | }
79 |
80 | @Override
81 | public boolean onOptionsItemSelected(MenuItem item) {
82 | switch (item.getItemId()) {
83 | case MENU_RANDOM:
84 | viewPager.setCurrentItem(random.nextInt(adapter.getCount()));
85 | return true;
86 | case android.R.id.home:
87 | finish();
88 | return true;
89 | default:
90 | return super.onOptionsItemSelected(item);
91 | }
92 | }
93 |
94 | // Compat helper method
95 | public void toggleActionBar() {
96 | ActionBarUtility.toggleActionBar(this, viewPager);
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/src/main/java/com/mareksebera/simpledilbert/favorites/DilbertFavoritedFragmentAdapter.java:
--------------------------------------------------------------------------------
1 | package com.mareksebera.simpledilbert.favorites;
2 |
3 | import android.os.Bundle;
4 |
5 | import com.mareksebera.simpledilbert.core.DilbertFragment;
6 | import com.mareksebera.simpledilbert.preferences.DilbertPreferences;
7 |
8 | import java.util.List;
9 |
10 | import androidx.fragment.app.Fragment;
11 | import androidx.fragment.app.FragmentManager;
12 | import androidx.fragment.app.FragmentPagerAdapter;
13 |
14 | final class DilbertFavoritedFragmentAdapter extends FragmentPagerAdapter {
15 |
16 | private List favorites = null;
17 |
18 | DilbertFavoritedFragmentAdapter(FragmentManager fm,
19 | List list) {
20 | super(fm);
21 | this.favorites = list;
22 | }
23 |
24 | @Override
25 | public CharSequence getPageTitle(int position) {
26 | return favorites.get(position).getDate().toString(
27 | DilbertPreferences.DATE_FORMATTER);
28 | }
29 |
30 | @Override
31 | public Fragment getItem(int position) {
32 | Fragment f = new DilbertFragment();
33 | Bundle bundle = new Bundle();
34 | bundle.putString(DilbertFragment.ARGUMENT_DATE, favorites.get(position)
35 | .getDate().toString(DilbertPreferences.DATE_FORMATTER));
36 | f.setArguments(bundle);
37 | return f;
38 | }
39 |
40 | @Override
41 | public int getCount() {
42 | return favorites == null ? 0 : favorites.size();
43 | }
44 |
45 | }
46 |
--------------------------------------------------------------------------------
/src/main/java/com/mareksebera/simpledilbert/favorites/FavoritedItem.java:
--------------------------------------------------------------------------------
1 | package com.mareksebera.simpledilbert.favorites;
2 |
3 | import org.joda.time.LocalDate;
4 |
5 | public final class FavoritedItem {
6 |
7 | private LocalDate date;
8 |
9 | public FavoritedItem(LocalDate d) {
10 | if (d == null) throw new AssertionError("FavoritedItem: LocalDate is null");
11 | date = d;
12 | }
13 |
14 | public LocalDate getDate() {
15 | return date;
16 | }
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/src/main/java/com/mareksebera/simpledilbert/picker/FolderPickerActivity.java:
--------------------------------------------------------------------------------
1 | package com.mareksebera.simpledilbert.picker;
2 |
3 | import android.os.Bundle;
4 | import android.view.MenuItem;
5 |
6 | import com.mareksebera.simpledilbert.R;
7 |
8 | import androidx.appcompat.app.AppCompatActivity;
9 |
10 | public final class FolderPickerActivity extends AppCompatActivity {
11 |
12 | @Override
13 | protected void onCreate(Bundle savedInstanceState) {
14 | super.onCreate(savedInstanceState);
15 | if (getSupportActionBar() != null) {
16 | getSupportActionBar().setDisplayHomeAsUpEnabled(true);
17 | }
18 | setContentView(R.layout.activity_folder_picker);
19 | }
20 |
21 | @Override
22 | public boolean onOptionsItemSelected(MenuItem item) {
23 | if (item.getItemId() == android.R.id.home) {
24 | finish();
25 | return true;
26 | }
27 | return super.onOptionsItemSelected(item);
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/main/java/com/mareksebera/simpledilbert/picker/FolderPickerAdapter.java:
--------------------------------------------------------------------------------
1 | package com.mareksebera.simpledilbert.picker;
2 |
3 | import android.os.Environment;
4 | import android.view.LayoutInflater;
5 | import android.view.View;
6 | import android.view.ViewGroup;
7 | import android.widget.BaseAdapter;
8 | import android.widget.ImageView;
9 | import android.widget.TextView;
10 | import android.widget.Toast;
11 |
12 | import com.mareksebera.simpledilbert.R;
13 |
14 | import org.jetbrains.annotations.Nullable;
15 |
16 | import java.io.File;
17 | import java.io.FileFilter;
18 | import java.util.Arrays;
19 | import java.util.Comparator;
20 |
21 | import androidx.fragment.app.FragmentActivity;
22 |
23 | public final class FolderPickerAdapter extends BaseAdapter {
24 |
25 | private FragmentActivity context;
26 | private File currentPath;
27 | private File[] currentFolder;
28 | private boolean hasParent = false;
29 | private boolean shouldShowHidden;
30 | private boolean shouldShowFiles;
31 |
32 | FolderPickerAdapter(FragmentActivity activity, boolean shouldShowHidden, boolean shouldShowFiles) {
33 | assert activity != null;
34 | this.context = activity;
35 | this.shouldShowFiles = shouldShowFiles;
36 | this.shouldShowHidden = shouldShowHidden;
37 | setPath(null);
38 | }
39 |
40 | void setPath(File path) {
41 | if (path == null) path = Environment.getExternalStorageDirectory();
42 | if (!path.isDirectory()) {
43 | return;
44 | }
45 | if (!path.canRead()) {
46 | Toast.makeText(context, R.string.folder_picker_cannot_open, Toast.LENGTH_SHORT).show();
47 | return;
48 | }
49 | this.currentPath = path;
50 | this.currentFolder = path.listFiles(getFileFilter());
51 | if (this.currentFolder == null)
52 | currentFolder = new File[0];
53 | Arrays.sort(this.currentFolder, new DirAlphaComparator());
54 | this.hasParent = currentPath.getParentFile() != null;
55 | notifyDataSetChanged();
56 | }
57 |
58 | @Override
59 | public int getCount() {
60 | return currentFolder.length + (hasParent ? 1 : 0);
61 | }
62 |
63 | @Override
64 | public Object getItem(int position) {
65 | if (hasParent)
66 | position--;
67 | if (position == -1) {
68 | return currentPath.getParentFile();
69 | }
70 | if (currentFolder.length >= position) {
71 | return currentFolder[position];
72 | }
73 | return null;
74 | }
75 |
76 | @Override
77 | public long getItemId(int position) {
78 | return position;
79 | }
80 |
81 | @Nullable
82 | @Override
83 | public View getView(int position, View convertView, ViewGroup parent) {
84 | ViewHolder vh;
85 | if (convertView == null) {
86 | vh = new ViewHolder();
87 | convertView = LayoutInflater.from(context).inflate(R.layout.item_folder_picker, parent, false);
88 | if (convertView != null) {
89 | vh.icon = convertView.findViewById(R.id.item_folder_picker_icon);
90 | vh.title = convertView.findViewById(R.id.item_folder_picker_text);
91 | convertView.setTag(vh);
92 | }
93 | } else {
94 | if (convertView.getTag() instanceof ViewHolder) {
95 | vh = (ViewHolder) convertView.getTag();
96 | } else {
97 | return convertView;
98 | }
99 | }
100 | File item = (File) getItem(position);
101 | if (item != null) {
102 | if (hasParent && currentPath.getParentFile().getAbsolutePath().equals(item.getAbsolutePath())) {
103 | vh.title.setText(String.format(".. (%s)", item.getName().equalsIgnoreCase("") ? "/" : item.getName()));
104 | } else {
105 | vh.title.setText(item.getName());
106 | }
107 | vh.icon.setImageResource(item.isDirectory() ? R.drawable.folder : R.drawable.file);
108 | }
109 | return convertView;
110 | }
111 |
112 | private FileFilter getFileFilter() {
113 | return pathname -> !(!shouldShowHidden && pathname.getName().startsWith(".")) && !(!shouldShowFiles && !pathname.isDirectory());
114 | }
115 |
116 | void setShowFiles(boolean showFiles) {
117 | this.shouldShowFiles = showFiles;
118 | setPath(currentPath);
119 | }
120 |
121 | void setShowHidden(boolean showHidden) {
122 | this.shouldShowHidden = showHidden;
123 | setPath(currentPath);
124 | }
125 |
126 | File getCurrentFolder() {
127 | return currentPath;
128 | }
129 |
130 | static class ViewHolder {
131 | TextView title;
132 | ImageView icon;
133 | }
134 |
135 | final static class DirAlphaComparator implements Comparator {
136 |
137 | public int compare(File filea, File fileb) {
138 | if (filea.isDirectory() && !fileb.isDirectory()) {
139 | return -1;
140 | } else if (!filea.isDirectory() && fileb.isDirectory()) {
141 | return 1;
142 | } else {
143 | return filea.getName().compareToIgnoreCase(fileb.getName());
144 | }
145 | }
146 | }
147 | }
148 |
--------------------------------------------------------------------------------
/src/main/java/com/mareksebera/simpledilbert/picker/FolderPickerFragment.java:
--------------------------------------------------------------------------------
1 | package com.mareksebera.simpledilbert.picker;
2 |
3 | import android.app.Activity;
4 | import android.content.Intent;
5 | import android.content.SharedPreferences;
6 | import android.net.Uri;
7 | import android.os.Bundle;
8 | import android.os.Environment;
9 | import android.view.LayoutInflater;
10 | import android.view.Menu;
11 | import android.view.MenuInflater;
12 | import android.view.MenuItem;
13 | import android.view.View;
14 | import android.view.ViewGroup;
15 | import android.widget.ListView;
16 | import android.widget.TextView;
17 | import android.widget.Toast;
18 |
19 | import com.mareksebera.simpledilbert.R;
20 | import com.mareksebera.simpledilbert.utilities.ActionBarUtility;
21 |
22 | import java.io.File;
23 |
24 | import androidx.fragment.app.ListFragment;
25 | import androidx.preference.PreferenceManager;
26 |
27 | public final class FolderPickerFragment extends ListFragment {
28 |
29 | private static final int MENU_SHOW_HIDDEN = 1, MENU_SHOW_FILES = 2, MENU_ACCEPT = 3, MENU_GO_DEFAULT = 4;
30 | private static final String PREF_SHOW_HIDDEN = "folder_picker_fragment.show_hidden";
31 | private static final String PREF_SHOW_FILES = "folder_picker_fragment.show_files";
32 | private SharedPreferences preferences;
33 | private FolderPickerAdapter folderPickerAdapter;
34 | private TextView currentPath;
35 |
36 | public FolderPickerFragment() {
37 | }
38 |
39 | @Override
40 | public void onCreate(Bundle savedInstanceState) {
41 | super.onCreate(savedInstanceState);
42 | preferences = PreferenceManager.getDefaultSharedPreferences(getActivity());
43 | folderPickerAdapter = new FolderPickerAdapter(getActivity(), isShowHidden(), isShowFiles());
44 | setListAdapter(folderPickerAdapter);
45 | setHasOptionsMenu(true);
46 | if (getActivity() != null && getActivity().getIntent() != null && getActivity().getIntent().getData() != null) {
47 | File requestedPath = new File(getActivity().getIntent().getData().getPath());
48 | if (requestedPath.isDirectory() && requestedPath.canRead()) {
49 | folderPickerAdapter.setPath(requestedPath);
50 | }
51 | }
52 | }
53 |
54 | @Override
55 | public void onListItemClick(ListView l, View v, int position, long id) {
56 | File click = (File) folderPickerAdapter.getItem(position);
57 | if (click != null) {
58 | if (click.isDirectory()) {
59 | folderPickerAdapter.setPath(click);
60 | currentPath.setText(folderPickerAdapter.getCurrentFolder().getAbsolutePath());
61 | }
62 | }
63 | }
64 |
65 | @Override
66 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
67 | View inflated = inflater.inflate(R.layout.fragment_folder_picker, container, false);
68 | assert inflated != null;
69 | inflated.setPadding(0, ActionBarUtility.getActionBarHeightDip(container == null ? inflated.getContext() : container.getContext()), 0, 0);
70 | currentPath = inflated.findViewById(R.id.fragment_folder_picker_current_path);
71 | currentPath.setText(folderPickerAdapter.getCurrentFolder().getAbsolutePath());
72 | return inflated;
73 | }
74 |
75 | @Override
76 | public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
77 | menu.add(Menu.NONE, MENU_ACCEPT, Menu.NONE, R.string.folder_picker_select_this)
78 | .setIcon(R.drawable.ic_navigation_accept)
79 | .setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
80 | menu.add(Menu.NONE, MENU_SHOW_HIDDEN, Menu.NONE, R.string.folder_picker_show_hidden).setCheckable(true);
81 | menu.add(Menu.NONE, MENU_SHOW_FILES, Menu.NONE, R.string.folder_picker_show_files).setCheckable(true);
82 | menu.add(Menu.NONE, MENU_GO_DEFAULT, Menu.NONE, R.string.folder_picker_go_to_default);
83 | super.onCreateOptionsMenu(menu, inflater);
84 | }
85 |
86 | @Override
87 | public void onPrepareOptionsMenu(Menu menu) {
88 | MenuItem show_files, show_hidden;
89 | if ((show_files = menu.findItem(MENU_SHOW_FILES)) != null) {
90 | show_files.setChecked(isShowFiles());
91 | }
92 | if ((show_hidden = menu.findItem(MENU_SHOW_HIDDEN)) != null) {
93 | show_hidden.setChecked(isShowHidden());
94 | }
95 | super.onPrepareOptionsMenu(menu);
96 | }
97 |
98 | @Override
99 | public boolean onOptionsItemSelected(MenuItem item) {
100 | switch (item.getItemId()) {
101 | case MENU_SHOW_FILES:
102 | toggleShowFiles();
103 | folderPickerAdapter.setShowFiles(isShowFiles());
104 | return true;
105 | case MENU_SHOW_HIDDEN:
106 | toggleShowHidden();
107 | folderPickerAdapter.setShowHidden(isShowHidden());
108 | return true;
109 | case MENU_ACCEPT:
110 | File current = folderPickerAdapter.getCurrentFolder();
111 | if (!current.canWrite()) {
112 | Toast.makeText(getActivity(), R.string.folder_picker_cannot_write, Toast.LENGTH_SHORT).show();
113 | return true;
114 | }
115 | Intent result = new Intent(null, Uri.fromFile(current));
116 | getActivity().setResult(Activity.RESULT_OK, result);
117 | getActivity().finish();
118 | return true;
119 | case MENU_GO_DEFAULT:
120 | folderPickerAdapter.setPath(getActivity().getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS));
121 | return true;
122 | }
123 | return super.onOptionsItemSelected(item);
124 | }
125 |
126 | private boolean isShowHidden() {
127 | return preferences.getBoolean(PREF_SHOW_HIDDEN, false);
128 | }
129 |
130 | private boolean isShowFiles() {
131 | return preferences.getBoolean(PREF_SHOW_FILES, false);
132 | }
133 |
134 | private void toggleShowHidden() {
135 | preferences.edit().putBoolean(PREF_SHOW_HIDDEN, !isShowHidden()).apply();
136 | }
137 |
138 | private void toggleShowFiles() {
139 | preferences.edit().putBoolean(PREF_SHOW_FILES, !isShowFiles()).apply();
140 | }
141 | }
142 |
--------------------------------------------------------------------------------
/src/main/java/com/mareksebera/simpledilbert/preferences/DilbertPreferencesActivity.java:
--------------------------------------------------------------------------------
1 | package com.mareksebera.simpledilbert.preferences;
2 |
3 | import android.Manifest;
4 | import android.app.AlertDialog;
5 | import android.appwidget.AppWidgetManager;
6 | import android.content.ComponentName;
7 | import android.content.Intent;
8 | import android.content.pm.PackageManager;
9 | import android.net.Uri;
10 | import android.os.Build;
11 | import android.os.Bundle;
12 | import android.util.Log;
13 | import android.view.MenuItem;
14 | import android.view.View;
15 | import android.view.View.OnClickListener;
16 | import android.widget.Button;
17 | import android.widget.CheckBox;
18 | import android.widget.LinearLayout;
19 | import android.widget.TextView;
20 | import android.widget.Toast;
21 |
22 | import com.mareksebera.simpledilbert.R;
23 | import com.mareksebera.simpledilbert.core.DilbertFragmentActivity;
24 | import com.mareksebera.simpledilbert.picker.FolderPickerActivity;
25 | import com.mareksebera.simpledilbert.widget.WidgetProvider;
26 |
27 | import java.io.File;
28 | import java.io.InputStream;
29 | import java.util.Map;
30 |
31 | import androidx.appcompat.app.AppCompatActivity;
32 | import androidx.core.app.ActivityCompat;
33 |
34 | import static android.appwidget.AppWidgetManager.ACTION_APPWIDGET_UPDATE;
35 | import static android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_ID;
36 | import static android.appwidget.AppWidgetManager.getInstance;
37 |
38 | public final class DilbertPreferencesActivity extends AppCompatActivity {
39 |
40 | private static final int REQUEST_DOWNLOAD_TARGET = 1;
41 | private static final String TAG = "DilbertPreferencesAct";
42 | private final OnClickListener licenseOnClickListener = v -> showLicenseDialog();
43 | private final OnClickListener authorOnClickListener = v -> {
44 | Intent emailIntent = new Intent(Intent.ACTION_SENDTO, Uri.fromParts(
45 | "mailto", "marek@msebera.cz", null));
46 | emailIntent.putExtra(Intent.EXTRA_SUBJECT, "Simple Dilbert");
47 | startActivity(Intent.createChooser(emailIntent, "Simple Dilbert"));
48 | };
49 | private CheckBox force_landscape, force_dark, hide_toolbars,
50 | share_image, reverse_landscape,
51 | open_at_latest_strip, widget_always_show_latest, widget_show_title;
52 | private TextView download_path;
53 | private Button export_urls;
54 | private DilbertPreferences preferences;
55 | private final OnClickListener downloadPathClickListener = new OnClickListener() {
56 |
57 | @Override
58 | public void onClick(View v) {
59 | if (!isStoragePermissionGranted()) {
60 | Toast.makeText(DilbertPreferencesActivity.this, "Storage permission denied, cannot continue", Toast.LENGTH_LONG).show();
61 | return;
62 | }
63 | Intent downloadPathSelector = new Intent(
64 | DilbertPreferencesActivity.this, FolderPickerActivity.class);
65 | downloadPathSelector.setData(Uri.fromFile(new File(preferences.getDownloadTarget())));
66 | startActivityForResult(downloadPathSelector,
67 | REQUEST_DOWNLOAD_TARGET);
68 | }
69 | };
70 | private final OnClickListener defaultZoomLevelClickListener = new OnClickListener() {
71 | @Override
72 | public void onClick(View v) {
73 | new AlertDialog.Builder(DilbertPreferencesActivity.this)
74 | .setTitle(R.string.pref_default_zoom_level)
75 | .setSingleChoiceItems(new CharSequence[]{"Minimum", "Medium", "Maximum"}, preferences.getDefaultZoomLevel(), (dialog, which) -> preferences.setDefaultZoomLevel(which))
76 | .setCancelable(true)
77 | .setNeutralButton(android.R.string.ok, (dialog, which) -> dialog.dismiss())
78 | .create()
79 | .show();
80 | }
81 | };
82 | private final OnClickListener exportUrlsListener = new OnClickListener() {
83 | @Override
84 | public void onClick(View v) {
85 | StringBuilder sb = new StringBuilder();
86 | for (Map.Entry e : preferences.getCachedUrls().entrySet()) {
87 | sb.append(String.format("%s :: %s\n", e.getKey(), e.getValue()));
88 | }
89 | Intent sharingIntent = new Intent(android.content.Intent.ACTION_SEND);
90 | sharingIntent.setType("text/plain");
91 | sharingIntent.putExtra(android.content.Intent.EXTRA_SUBJECT, "Subject Here");
92 | sharingIntent.putExtra(android.content.Intent.EXTRA_TEXT, sb.toString());
93 | startActivity(Intent.createChooser(sharingIntent, getResources().getString(R.string.menu_share)));
94 | }
95 | };
96 |
97 | protected void onActivityResult(int requestCode, int resultCode, Intent data) {
98 | if (requestCode != REQUEST_DOWNLOAD_TARGET) {
99 | super.onActivityResult(requestCode, resultCode, data);
100 | return;
101 | }
102 | if (resultCode != RESULT_OK)
103 | return;
104 | if (data != null) {
105 | Uri path = data.getData();
106 | if (path != null && path.getPath() != null) {
107 | preferences.setDownloadTarget(new File(path.getPath()).getAbsolutePath());
108 | }
109 | }
110 | }
111 |
112 | private CharSequence getLicenseText() {
113 | String rtn = "";
114 | try {
115 | InputStream stream = getAssets().open("LICENSE.txt");
116 | java.util.Scanner s = new java.util.Scanner(stream)
117 | .useDelimiter("\\A");
118 | rtn = s.hasNext() ? s.next() : "";
119 | } catch (Exception | Error e) {
120 | Log.e(TAG, "License couldn't be retrieved", e);
121 | }
122 | return rtn;
123 | }
124 |
125 | private void showLicenseDialog() {
126 | AlertDialog.Builder builder = new AlertDialog.Builder(this);
127 | builder.setTitle(R.string.apache_license_2_0);
128 | builder.setMessage(getLicenseText());
129 | builder.setNeutralButton(android.R.string.cancel,
130 | (dialog, which) -> dialog.dismiss());
131 | builder.show();
132 | }
133 |
134 | @Override
135 | protected void onCreate(Bundle savedInstanceState) {
136 | preferences = new DilbertPreferences(this);
137 | if (preferences.isForceLandscape())
138 | setRequestedOrientation(preferences.getLandscapeOrientation());
139 | setTheme(preferences.isDarkLayoutEnabled() ? R.style.AppThemeDark
140 | : R.style.AppThemeLight);
141 | super.onCreate(savedInstanceState);
142 | if (getSupportActionBar() != null) {
143 | getSupportActionBar().setDisplayHomeAsUpEnabled(true);
144 | }
145 | setContentView(R.layout.preferences);
146 | setTitle(R.string.title_preferences);
147 | force_landscape = findViewById(R.id.pref_force_landscape);
148 | force_dark = findViewById(R.id.pref_force_dark_background);
149 | hide_toolbars = findViewById(R.id.pref_hide_toolbars);
150 | share_image = findViewById(R.id.pref_share_image);
151 | download_path = findViewById(R.id.pref_download_path);
152 | reverse_landscape = findViewById(R.id.pref_reverse_landscape);
153 | open_at_latest_strip = findViewById(R.id.pref_open_at_latest_strip);
154 | widget_always_show_latest = findViewById(R.id.pref_widget_always_latest);
155 | widget_show_title = findViewById(R.id.pref_widget_show_title);
156 | export_urls = findViewById(R.id.pref_export_urls);
157 | TextView default_zoom_level = findViewById(R.id.pref_default_zoom_level);
158 | TextView author = findViewById(R.id.app_author);
159 | LinearLayout download_path_layout = findViewById(R.id.pref_download_path_layout);
160 | TextView license = findViewById(R.id.pref_show_license);
161 | default_zoom_level.setOnClickListener(defaultZoomLevelClickListener);
162 | download_path_layout.setOnClickListener(downloadPathClickListener);
163 | license.setOnClickListener(licenseOnClickListener);
164 | author.setOnClickListener(authorOnClickListener);
165 | force_landscape.setOnCheckedChangeListener((buttonView, isChecked) -> {
166 | reverse_landscape.setEnabled(isChecked);
167 | reverse_landscape.setChecked(reverse_landscape.isChecked() && isChecked);
168 | });
169 | export_urls.setOnClickListener(exportUrlsListener);
170 | }
171 |
172 | @Override
173 | public void onBackPressed() {
174 | startActivity(new Intent(this, DilbertFragmentActivity.class));
175 | finish();
176 | }
177 |
178 | @Override
179 | protected void onResume() {
180 | super.onResume();
181 | force_landscape.setChecked(preferences.isForceLandscape());
182 | force_dark.setChecked(preferences.isDarkLayoutEnabled());
183 | hide_toolbars.setChecked(preferences.isToolbarsHidden());
184 | download_path.setText(preferences.getDownloadTarget());
185 | share_image.setChecked(preferences.isSharingImage());
186 | reverse_landscape.setVisibility(View.VISIBLE);
187 | reverse_landscape.setEnabled(preferences.isForceLandscape());
188 | reverse_landscape.setChecked(preferences.isReversedLandscape() && preferences.isForceLandscape());
189 | open_at_latest_strip.setChecked(preferences.isShouldOpenAtLatestStrip());
190 | widget_always_show_latest.setChecked(preferences.isWidgetAlwaysShowLatest());
191 | widget_show_title.setChecked(preferences.isWidgetShowTitle());
192 | export_urls.setEnabled(true);
193 | }
194 |
195 | @Override
196 | public boolean onOptionsItemSelected(MenuItem item) {
197 | switch (item.getItemId()) {
198 | case android.R.id.home:
199 | onBackPressed();
200 | return true;
201 | }
202 | return super.onOptionsItemSelected(item);
203 | }
204 |
205 | @Override
206 | protected void onPause() {
207 | super.onPause();
208 | preferences.setIsDarkLayoutEnabled(force_dark.isChecked());
209 | preferences.setIsForceLandscape(force_landscape.isChecked());
210 | preferences.setIsToolbarsHidden(hide_toolbars.isChecked());
211 | preferences.setIsSharingImage(share_image.isChecked());
212 | preferences.setIsReversedLandscape(reverse_landscape.isChecked());
213 | preferences.setShouldOpenAtLatestStrip(open_at_latest_strip.isChecked());
214 | preferences.setWidgetAlwaysShowLatest(widget_always_show_latest.isChecked());
215 | preferences.setWidgetShowTitle(widget_show_title.isChecked());
216 | export_urls.setEnabled(false);
217 | updateWidgets();
218 | }
219 |
220 | private void updateWidgets() {
221 | try {
222 | AppWidgetManager awm = getInstance(this);
223 | int[] ids = awm == null ? new int[0] : awm.getAppWidgetIds(
224 | new ComponentName(this, WidgetProvider.class));
225 | if (ids != null)
226 | for (int id : ids) {
227 | Intent updateIntent = new Intent();
228 | updateIntent
229 | .setAction(ACTION_APPWIDGET_UPDATE);
230 | updateIntent.putExtra(EXTRA_APPWIDGET_ID,
231 | id);
232 | sendBroadcast(updateIntent);
233 | }
234 | } catch (Throwable e) {
235 | e.printStackTrace();
236 | }
237 | }
238 |
239 | private boolean isStoragePermissionGranted() {
240 | if (Build.VERSION.SDK_INT >= 23) {
241 | if (checkSelfPermission(android.Manifest.permission.WRITE_EXTERNAL_STORAGE)
242 | == PackageManager.PERMISSION_GRANTED) {
243 | Log.v(TAG, "Permission is granted");
244 | return true;
245 | } else {
246 |
247 | Log.v(TAG, "Permission is revoked");
248 | ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);
249 | return false;
250 | }
251 | } else { //permission is automatically granted on sdk<23 upon installation
252 | Log.v(TAG, "Permission is granted");
253 | return true;
254 | }
255 | }
256 |
257 | }
258 |
--------------------------------------------------------------------------------
/src/main/java/com/mareksebera/simpledilbert/utilities/ActionBarUtility.java:
--------------------------------------------------------------------------------
1 | package com.mareksebera.simpledilbert.utilities;
2 |
3 | import android.annotation.TargetApi;
4 | import android.content.Context;
5 | import android.content.res.Resources;
6 | import android.os.Build;
7 | import android.util.Log;
8 | import android.util.TypedValue;
9 | import android.widget.FrameLayout;
10 |
11 | import androidx.appcompat.app.AppCompatActivity;
12 | import androidx.viewpager.widget.ViewPager;
13 |
14 | public final class ActionBarUtility {
15 |
16 | public static int getActionBarHeightDip(Context c) {
17 | float scale = c.getResources().getDisplayMetrics().density;
18 | return (int) ((48 * scale) + 0.5);
19 | }
20 |
21 | public static void toggleActionBar(AppCompatActivity actionBarActivity,
22 | ViewPager viewPager) {
23 | try {
24 | if (actionBarActivity == null || actionBarActivity.getSupportActionBar() == null)
25 | return;
26 | FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) viewPager
27 | .getLayoutParams();
28 | if (lp == null)
29 | return;
30 | if (actionBarActivity.getSupportActionBar().isShowing()) {
31 | actionBarActivity.getSupportActionBar().hide();
32 | lp.topMargin = 0;
33 | viewPager.setLayoutParams(lp);
34 | } else {
35 | actionBarActivity.getSupportActionBar().show();
36 | lp.topMargin = getActionBarHeightCompat(actionBarActivity);
37 | viewPager.setLayoutParams(lp);
38 | }
39 | } catch (Throwable t) {
40 | Log.e("DilbertFragmentActivity", "Toggle ActionBar failed", t);
41 | }
42 | }
43 |
44 | @TargetApi(Build.VERSION_CODES.HONEYCOMB)
45 | private static int getActionBarHeightCompat(AppCompatActivity actionBarActivity) {
46 | TypedValue tv = new TypedValue();
47 | Resources.Theme activityTheme = actionBarActivity.getTheme();
48 | boolean resolved = false;
49 | if (activityTheme != null) {
50 | resolved = activityTheme.resolveAttribute(
51 | android.R.attr.actionBarSize, tv, true);
52 | }
53 | return resolved ? actionBarActivity.getResources()
54 | .getDimensionPixelSize(tv.resourceId) : 40;
55 |
56 | }
57 |
58 | }
59 |
--------------------------------------------------------------------------------
/src/main/java/com/mareksebera/simpledilbert/utilities/CustomTarget.java:
--------------------------------------------------------------------------------
1 | package com.mareksebera.simpledilbert.utilities;
2 |
3 | import android.graphics.Bitmap;
4 | import android.graphics.drawable.Drawable;
5 | import android.view.View;
6 | import android.widget.ImageView;
7 |
8 | import com.bumptech.glide.request.Request;
9 | import com.bumptech.glide.request.target.CustomViewTarget;
10 | import com.bumptech.glide.request.target.ImageViewTarget;
11 | import com.bumptech.glide.request.target.SizeReadyCallback;
12 | import com.bumptech.glide.request.target.Target;
13 | import com.bumptech.glide.request.transition.Transition;
14 | import com.bumptech.glide.util.Util;
15 |
16 | import androidx.annotation.NonNull;
17 | import androidx.annotation.Nullable;
18 |
19 | /**
20 | * A base {@link Target} for loading resources ({@link android.graphics.Bitmap}, {@link Drawable}
21 | * etc) that are used outside of {@link android.view.View}s.
22 | *
23 | *
If you're loading a resource into a {@link View}, use
24 | * {@link com.bumptech.glide.RequestBuilder#into(ImageView)}, a subclass of {@link ImageViewTarget},
25 | * or {@link CustomViewTarget}. Using this class to load resources into {@link View}s can prevent
26 | * Glide from correctly cancelling any previous loads, which may result in incorrect images
27 | * appearing in the view, especially in scrolling views like
28 | * android.support.v7.widget.RecyclerView
29 | *
30 | *
You MUST implement {@link #onLoadCleared(Drawable)} and ensure that all references to
31 | * any resource passed into the target in {@link #onResourceReady(Object, Transition)} are removed
32 | * before {@link #onLoadCleared(Drawable)} completes. Failing to do so can result in graphical
33 | * corruption, crashes caused by recycled {@link Bitmap}s, and other undefined behavior. It is never
34 | * safe to leave {@link #onLoadCleared(Drawable)} unimplemented or empty. Even if you do not
35 | * manually clear this {@link Target}, Glide may do so automatically after certain lifecycle events
36 | * in android.support.v4.app.Fragments and {@link android.app.Activity}s.
37 | *
38 | *
This class can only be used with {@link Target#SIZE_ORIGINAL} or when the desired resource
39 | * dimensions are known when the {@link Target} is created. If you'd like to run some asynchronous
40 | * process and make full use of {@link #getSize(SizeReadyCallback)} and {@link SizeReadyCallback},
41 | * extend {@link Target} directly instead of using this class.
42 | *
43 | * @param The type of resource that will be loaded (e.g. {@link Bitmap}).
44 | */
45 | public abstract class CustomTarget implements Target {
46 |
47 | private final int width;
48 | private final int height;
49 |
50 | @Nullable
51 | private Request request;
52 |
53 | /**
54 | * Creates a new {@link CustomTarget} that will attempt to load the resource in its original size.
55 | *
56 | *
This constructor can cause very memory inefficient loads if the resource is large and can
57 | * cause OOMs. It's provided as a convenience for when you'd like to specify dimensions with
58 | * {@link com.bumptech.glide.request.RequestOptions#override(int)}. In all other cases, prefer
59 | * {@link #CustomTarget(int, int)}.
60 | */
61 | public CustomTarget() {
62 | this(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL);
63 | }
64 |
65 | /**
66 | * Creates a new {@code CustomTarget} that will return the given {@code width} and {@link @code}
67 | * as the requested size (unless overridden by
68 | * {@link com.bumptech.glide.request.RequestOptions#override(int)} in the request).
69 | *
70 | * @param width The requested width (>= 0, or == Target.SIZE_ORIGINAL).
71 | * @param height The requested height (>= 0, or == Target.SIZE_ORIGINAL).
72 | */
73 | public CustomTarget(int width, int height) {
74 | if (!Util.isValidDimensions(width, height)) {
75 | throw new IllegalArgumentException(
76 | "Width and height must both be > 0 or Target#SIZE_ORIGINAL, but given" + " width: "
77 | + width + " and height: " + height);
78 | }
79 |
80 | this.width = width;
81 | this.height = height;
82 | }
83 |
84 | @Override
85 | public void onStart() {
86 | // Intentionally empty, this can be optionally implemented by subclasses.
87 | }
88 |
89 | @Override
90 | public void onStop() {
91 | // Intentionally empty, this can be optionally implemented by subclasses.
92 | }
93 |
94 | @Override
95 | public void onDestroy() {
96 | // Intentionally empty, this can be optionally implemented by subclasses.
97 | }
98 |
99 | @Override
100 | public void onLoadStarted(@Nullable Drawable placeholder) {
101 | // Intentionally empty, this can be optionally implemented by subclasses.
102 | }
103 |
104 | @Override
105 | public void onLoadFailed(@Nullable Drawable errorDrawable) {
106 | // Intentionally empty, this can be optionally implemented by subclasses.
107 | }
108 |
109 | @Override
110 | public final void getSize(@NonNull SizeReadyCallback cb) {
111 | cb.onSizeReady(width, height);
112 | }
113 |
114 | @Override
115 | public final void removeCallback(@NonNull SizeReadyCallback cb) {
116 | // Do nothing, this class does not retain SizeReadyCallbacks.
117 | }
118 |
119 | @Override
120 | public final void setRequest(@Nullable Request request) {
121 | this.request = request;
122 | }
123 |
124 | @Nullable
125 | @Override
126 | public final Request getRequest() {
127 | return request;
128 | }
129 | }
--------------------------------------------------------------------------------
/src/main/java/com/mareksebera/simpledilbert/utilities/DownloadManagerBroadcastReceiver.java:
--------------------------------------------------------------------------------
1 | package com.mareksebera.simpledilbert.utilities;
2 |
3 | import android.annotation.TargetApi;
4 | import android.app.DownloadManager;
5 | import android.content.BroadcastReceiver;
6 | import android.content.Context;
7 | import android.content.Intent;
8 | import android.net.Uri;
9 | import android.os.Build;
10 | import android.util.Log;
11 | import android.widget.Toast;
12 |
13 | import com.mareksebera.simpledilbert.preferences.DilbertPreferences;
14 |
15 | import java.io.File;
16 |
17 | public final class DownloadManagerBroadcastReceiver extends BroadcastReceiver {
18 |
19 | @Override
20 | @TargetApi(Build.VERSION_CODES.HONEYCOMB)
21 | public void onReceive(Context context, Intent intent) {
22 | try {
23 | DilbertPreferences preferences = new DilbertPreferences(context);
24 | DownloadManager dm = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
25 | Uri downloadedFilePath = dm.getUriForDownloadedFile(intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, 0));
26 | if (downloadedFilePath == null) {
27 | Log.d("DlManagerBReceiver", "No Uri for downloaded file from DownloadManager");
28 | return;
29 | }
30 | File downloadedFile = new File(downloadedFilePath.getPath());
31 | if (downloadedFilePath.getLastPathSegment() == null) {
32 | return;
33 | }
34 | String scheduledTargetPath = preferences.getScheduledTargetPath(downloadedFilePath.getLastPathSegment().substring(0, 10));
35 | if (scheduledTargetPath != null) {
36 | if (!downloadedFile.renameTo(new File(scheduledTargetPath))) {
37 | Toast.makeText(context, "Couldn't move picture to selected Folder", Toast.LENGTH_SHORT).show();
38 | }
39 | }
40 | } catch (Throwable t) {
41 | Log.e("DlManagerBReceiver", "Error while moving downloaded file to desired target folder", t);
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/main/java/com/mareksebera/simpledilbert/utilities/FindUrls.java:
--------------------------------------------------------------------------------
1 | package com.mareksebera.simpledilbert.utilities;
2 |
3 | import android.net.Uri;
4 | import android.util.Log;
5 |
6 | import com.mareksebera.simpledilbert.preferences.DilbertPreferences;
7 |
8 | import org.joda.time.LocalDate;
9 |
10 | import java.util.Scanner;
11 | import java.util.regex.Matcher;
12 | import java.util.regex.Pattern;
13 |
14 | import androidx.annotation.NonNull;
15 |
16 | final public class FindUrls {
17 | private static final String LOG_TAG = "FindUrls";
18 |
19 | private static final Pattern url_match_pattern = Pattern
20 | .compile(".*data-image=\"([^\"]+)\".*");
21 | private static final Pattern date_match_pattern = Pattern
22 | .compile(".*([\\d]{4}-[\\d]{2}-[\\d]{2}).*");
23 | private static final Pattern title_match_pattern = Pattern
24 | .compile(".*\"comic-title-name\">([^<]+)<.*");
25 | private static final Pattern twitter_title_match_pattern = Pattern
26 | .compile(".*content=\"(.*)\".*");
27 |
28 | private FindUrls() {
29 | }
30 |
31 | @NonNull
32 | static String[] extractUrlAndTitle(String httpResponse) {
33 | String foundUrl = null;
34 | String foundTitle = null;
35 | boolean hasFoundUrl = false;
36 | boolean hasFoundTitle = false;
37 | try {
38 | Scanner scan;
39 | scan = new Scanner(httpResponse);
40 |
41 | while (!hasFoundUrl || !hasFoundTitle) {
42 | if (!scan.hasNextLine()) break;
43 |
44 | String line = scan.nextLine();
45 |
46 | if (line.contains("data-image")) {
47 | Matcher m = url_match_pattern.matcher(line);
48 | if (m.matches()) {
49 | foundUrl = FindUrls.correctUrl(m.group(1));
50 | hasFoundUrl = true;
51 | }
52 | } else if (line.contains("comic-title-name")) {
53 | Matcher m = title_match_pattern.matcher(line);
54 | if (m.matches()) {
55 | foundTitle = m.group(1);
56 | hasFoundTitle = true;
57 | }
58 | } else if (!hasFoundTitle && line.contains("twitter:title")) {
59 | Matcher m = twitter_title_match_pattern.matcher(line);
60 | if(m.matches()) {
61 | foundTitle = m.group(1);
62 | hasFoundTitle = true;
63 | }
64 | }
65 | }
66 |
67 | scan.close();
68 | } catch (Throwable t) {
69 | Log.e(LOG_TAG, "Error Occurred", t);
70 | }
71 | return new String[]{foundUrl, foundTitle};
72 | }
73 |
74 | /**
75 | * Will correct urls not starting with http://, https:// and those starting with "//"
76 | *
77 | * @param foundUrl String url in raw form parsed out of html
78 | * @return String corrected URL
79 | */
80 | private static String correctUrl(String foundUrl) {
81 | if (!foundUrl.startsWith("https://") && !foundUrl.startsWith("http://")) {
82 | if (foundUrl.startsWith("//")) {
83 | foundUrl = "https:" + foundUrl;
84 | } else {
85 | foundUrl = "https://" + foundUrl;
86 | }
87 | }
88 | return foundUrl;
89 | }
90 |
91 | public static LocalDate extractCurrentDateFromIntentUrl(Uri path) {
92 | try {
93 | Matcher m = date_match_pattern.matcher(path.toString());
94 | if (m.matches()) {
95 | return LocalDate.parse(m.group(1), DilbertPreferences.DATE_FORMATTER);
96 | }
97 | } catch (Throwable t) {
98 | Log.e(LOG_TAG, "extractCurrentDateFromIntentUrl failed", t);
99 | }
100 | return null;
101 | }
102 | }
--------------------------------------------------------------------------------
/src/main/java/com/mareksebera/simpledilbert/utilities/FixedViewPager.java:
--------------------------------------------------------------------------------
1 | package com.mareksebera.simpledilbert.utilities;
2 |
3 | import android.content.Context;
4 | import android.util.AttributeSet;
5 | import android.view.MotionEvent;
6 |
7 | import org.jetbrains.annotations.NotNull;
8 |
9 | import androidx.viewpager.widget.ViewPager;
10 |
11 | final public class FixedViewPager extends ViewPager {
12 |
13 | public FixedViewPager(Context context, AttributeSet attrs) {
14 | super(context, attrs);
15 | }
16 |
17 | public FixedViewPager(Context context) {
18 | super(context);
19 | }
20 |
21 | public boolean onInterceptTouchEvent(final MotionEvent event) {
22 | if (isEnabled()) {
23 | try {
24 | return super.onInterceptTouchEvent(event);
25 | } catch (final Throwable e) {
26 | // if you read this: don't worry! just close this class and do
27 | // something else!
28 | }
29 | }
30 | return false;
31 |
32 | }
33 |
34 | @Override
35 | public boolean onTouchEvent(MotionEvent arg0) {
36 | try {
37 | return super.onTouchEvent(arg0);
38 | } catch (final Throwable t) {
39 | return false;
40 | }
41 | }
42 |
43 | @Override
44 | public boolean performClick() {
45 | try {
46 | return super.performClick();
47 | } catch (final Throwable t) {
48 | return false;
49 | }
50 | }
51 |
52 | @Override
53 | public boolean dispatchTouchEvent(@NotNull MotionEvent ev) {
54 | try {
55 | return super.dispatchTouchEvent(ev);
56 | } catch (final Throwable t) {
57 | return false;
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/main/java/com/mareksebera/simpledilbert/utilities/GetStripUrl.java:
--------------------------------------------------------------------------------
1 | package com.mareksebera.simpledilbert.utilities;
2 |
3 | import android.accounts.NetworkErrorException;
4 | import android.content.Context;
5 | import android.os.AsyncTask;
6 | import android.os.Build;
7 | import android.util.Log;
8 | import android.view.View;
9 | import android.widget.ProgressBar;
10 |
11 | import com.android.volley.Request;
12 | import com.android.volley.RequestQueue;
13 | import com.android.volley.toolbox.HurlStack;
14 | import com.android.volley.toolbox.RequestFuture;
15 | import com.android.volley.toolbox.StringRequest;
16 | import com.android.volley.toolbox.Volley;
17 | import com.mareksebera.simpledilbert.preferences.DilbertPreferences;
18 |
19 | import org.joda.time.LocalDate;
20 |
21 | import java.lang.ref.WeakReference;
22 | import java.security.KeyManagementException;
23 | import java.security.NoSuchAlgorithmException;
24 | import java.util.concurrent.ExecutionException;
25 | import java.util.concurrent.TimeUnit;
26 | import java.util.concurrent.TimeoutException;
27 |
28 | public final class GetStripUrl extends AsyncTask {
29 |
30 | private static final String TAG = "GetStripUrl";
31 | private final DilbertPreferences preferences;
32 | private WeakReference progressBar;
33 | private final LocalDate currDate;
34 | private final GetStripUrlInterface listener;
35 | private String[] handledResponse;
36 | private RequestQueue volleyRequestQueue;
37 |
38 | public GetStripUrl(Context ctx, GetStripUrlInterface listener,
39 | DilbertPreferences preferences, LocalDate currDate) {
40 | this(ctx, listener, preferences, currDate, null);
41 | }
42 |
43 | public GetStripUrl(Context ctx, GetStripUrlInterface listener,
44 | DilbertPreferences preferences, LocalDate currDate,
45 | ProgressBar progressBar) {
46 | this.preferences = preferences;
47 | this.progressBar = new WeakReference<>(progressBar);
48 | this.currDate = currDate;
49 | this.listener = listener;
50 | HurlStack volleyStack = null;
51 | // Only use custom TLSSocketFactory when OS provided one, does not support TLS1.1+ by default
52 | if (Build.VERSION.SDK_INT >= 16 && Build.VERSION.SDK_INT < 22) {
53 | try {
54 | volleyStack = new HurlStack(null, new TLSSocketFactory());
55 | } catch (KeyManagementException e) {
56 | e.printStackTrace();
57 | } catch (NoSuchAlgorithmException e) {
58 | e.printStackTrace();
59 | }
60 | }
61 | this.volleyRequestQueue = Volley.newRequestQueue(ctx, volleyStack);
62 | }
63 |
64 | @Override
65 | protected String[] doInBackground(Void... params) {
66 | if (this.currDate == null) {
67 | Log.e(TAG, "Cannot load for null date");
68 | return null;
69 | }
70 | String cached = this.preferences.getCachedUrl(this.currDate);
71 | if (cached != null) {
72 | return new String[]{cached, this.preferences.getCachedTitle(this.currDate)};
73 | }
74 |
75 | RequestFuture future = RequestFuture.newFuture();
76 | volleyRequestQueue.add(
77 | new StringRequest(Request.Method.GET, "https://dilbert.com/strip/"
78 | + currDate.toString(DilbertPreferences.DATE_FORMATTER) + "/", future, future)
79 | );
80 |
81 | try {
82 | handledResponse = handleParse(future.get(15, TimeUnit.SECONDS));
83 | } catch (InterruptedException e) {
84 | e.printStackTrace();
85 | } catch (ExecutionException e) {
86 | e.printStackTrace();
87 | } catch (TimeoutException e) {
88 | e.printStackTrace();
89 | }
90 |
91 | return handledResponse;
92 | }
93 |
94 | private String[] handleParse(String responseString) {
95 | String[] found = FindUrls.extractUrlAndTitle(responseString);
96 | if (found.length == 2 && found[0] != null && found[1] != null) {
97 | preferences
98 | .saveCurrentUrl(currDate
99 | .toString(DilbertPreferences.DATE_FORMATTER), found[0]);
100 | preferences
101 | .saveCurrentTitle(currDate
102 | .toString(DilbertPreferences.DATE_FORMATTER), found[1]);
103 | }
104 | return found;
105 | }
106 |
107 | @Override
108 | protected void onPostExecute(String[] result) {
109 | if (result == null) {
110 | if (listener != null) {
111 | listener.imageLoadFailed(preferences.getCachedUrl(currDate),
112 | new NetworkErrorException("Network Denied"));
113 | } else {
114 | Log.e(TAG, "Listener is NULL");
115 | }
116 | } else {
117 | if (listener != null) {
118 | listener.displayImage(result[0], result[1]);
119 | } else {
120 | Log.e(TAG, "listener is NULL");
121 | }
122 | }
123 | }
124 |
125 | /**
126 | * Indicates that there is any work in progress
127 | */
128 | @Override
129 | protected void onPreExecute() {
130 | if (progressBar.get() != null) {
131 | progressBar.get().setVisibility(View.VISIBLE);
132 | }
133 | }
134 | }
135 |
--------------------------------------------------------------------------------
/src/main/java/com/mareksebera/simpledilbert/utilities/GetStripUrlInterface.java:
--------------------------------------------------------------------------------
1 | package com.mareksebera.simpledilbert.utilities;
2 |
3 | public interface GetStripUrlInterface {
4 |
5 | void displayImage(String url, String title);
6 |
7 | void imageLoadFailed(String url, Throwable reason);
8 |
9 | }
10 |
--------------------------------------------------------------------------------
/src/main/java/com/mareksebera/simpledilbert/utilities/TLSSocketFactory.java:
--------------------------------------------------------------------------------
1 | package com.mareksebera.simpledilbert.utilities;
2 |
3 | import java.io.IOException;
4 | import java.net.InetAddress;
5 | import java.net.Socket;
6 | import java.security.KeyManagementException;
7 | import java.security.NoSuchAlgorithmException;
8 |
9 | import javax.net.ssl.SSLContext;
10 | import javax.net.ssl.SSLSocket;
11 | import javax.net.ssl.SSLSocketFactory;
12 |
13 | class TLSSocketFactory extends SSLSocketFactory {
14 |
15 | private SSLSocketFactory delegate;
16 |
17 | TLSSocketFactory() throws KeyManagementException, NoSuchAlgorithmException {
18 | SSLContext context = SSLContext.getInstance("TLS");
19 | context.init(null, null, null);
20 | delegate = context.getSocketFactory();
21 | }
22 |
23 | @Override
24 | public String[] getDefaultCipherSuites() {
25 | return delegate.getDefaultCipherSuites();
26 | }
27 |
28 | @Override
29 | public String[] getSupportedCipherSuites() {
30 | return delegate.getSupportedCipherSuites();
31 | }
32 |
33 | @Override
34 | public Socket createSocket() throws IOException {
35 | return enableTLSOnSocket(delegate.createSocket());
36 | }
37 |
38 | @Override
39 | public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException {
40 | return enableTLSOnSocket(delegate.createSocket(s, host, port, autoClose));
41 | }
42 |
43 | @Override
44 | public Socket createSocket(String host, int port) throws IOException {
45 | return enableTLSOnSocket(delegate.createSocket(host, port));
46 | }
47 |
48 | @Override
49 | public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException {
50 | return enableTLSOnSocket(delegate.createSocket(host, port, localHost, localPort));
51 | }
52 |
53 | @Override
54 | public Socket createSocket(InetAddress host, int port) throws IOException {
55 | return enableTLSOnSocket(delegate.createSocket(host, port));
56 | }
57 |
58 | @Override
59 | public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException {
60 | return enableTLSOnSocket(delegate.createSocket(address, port, localAddress, localPort));
61 | }
62 |
63 | private Socket enableTLSOnSocket(Socket socket) {
64 | if ((socket instanceof SSLSocket)) {
65 | ((SSLSocket) socket).setEnabledProtocols(new String[]{"TLSv1.1", "TLSv1.2"});
66 | }
67 | return socket;
68 | }
69 | }
--------------------------------------------------------------------------------
/src/main/java/com/mareksebera/simpledilbert/widget/WidgetProvider.java:
--------------------------------------------------------------------------------
1 | package com.mareksebera.simpledilbert.widget;
2 |
3 | import android.app.PendingIntent;
4 | import android.appwidget.AppWidgetManager;
5 | import android.appwidget.AppWidgetProvider;
6 | import android.content.Context;
7 | import android.content.Intent;
8 | import android.graphics.Bitmap;
9 | import android.view.View;
10 | import android.widget.RemoteViews;
11 | import android.widget.Toast;
12 |
13 | import com.bumptech.glide.Glide;
14 | import com.bumptech.glide.load.DataSource;
15 | import com.bumptech.glide.load.engine.GlideException;
16 | import com.bumptech.glide.request.RequestListener;
17 | import com.bumptech.glide.request.RequestOptions;
18 | import com.bumptech.glide.request.target.AppWidgetTarget;
19 | import com.bumptech.glide.request.target.Target;
20 | import com.mareksebera.simpledilbert.R;
21 | import com.mareksebera.simpledilbert.core.DilbertFragmentActivity;
22 | import com.mareksebera.simpledilbert.preferences.DilbertPreferences;
23 | import com.mareksebera.simpledilbert.utilities.GetStripUrl;
24 | import com.mareksebera.simpledilbert.utilities.GetStripUrlInterface;
25 |
26 | import org.jetbrains.annotations.NotNull;
27 | import org.joda.time.LocalDate;
28 |
29 | import androidx.annotation.Nullable;
30 |
31 | public final class WidgetProvider extends AppWidgetProvider {
32 |
33 | private static final String INTENT_PREVIOUS = "com.mareksebera.simpledilbert.widget.PREVIOUS";
34 | private static final String INTENT_NEXT = "com.mareksebera.simpledilbert.widget.NEXT";
35 | private static final String INTENT_LATEST = "com.mareksebera.simpledilbert.widget.LATEST";
36 | private static final String INTENT_RANDOM = "com.mareksebera.simpledilbert.widget.RANDOM";
37 | private static final String INTENT_REFRESH = "com.mareksebera.simpledilbert.widget.REFRESH";
38 | private static final String INTENT_DISPLAY = "com.mareksebera.simpledilbert.widget.DISPLAY";
39 |
40 | private static Toast currentToast = null;
41 |
42 | private static PendingIntent getPendingIntent(String INTENT,
43 | Context context, int appWidgetId) {
44 | Intent intent = new Intent(INTENT);
45 | intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
46 | intent.setPackage(context.getPackageName());
47 | return PendingIntent.getBroadcast(context, 0, intent,
48 | PendingIntent.FLAG_UPDATE_CURRENT);
49 | }
50 |
51 | private static void updateAppWidget(final Context context,
52 | final AppWidgetManager appWidgetManager, final int appWidgetId) {
53 | final DilbertPreferences prefs = new DilbertPreferences(context);
54 | final RemoteViews views = new RemoteViews(context.getPackageName(),
55 | R.layout.widget_layout);
56 | views.setOnClickPendingIntent(R.id.widget_previous,
57 | getPendingIntent(INTENT_PREVIOUS, context, appWidgetId));
58 | views.setOnClickPendingIntent(R.id.widget_next,
59 | getPendingIntent(INTENT_NEXT, context, appWidgetId));
60 | views.setOnClickPendingIntent(R.id.widget_latest,
61 | getPendingIntent(INTENT_LATEST, context, appWidgetId));
62 | views.setOnClickPendingIntent(R.id.widget_random,
63 | getPendingIntent(INTENT_RANDOM, context, appWidgetId));
64 | views.setOnClickPendingIntent(R.id.widget_image,
65 | getPendingIntent(INTENT_DISPLAY, context, appWidgetId));
66 | views.setOnClickPendingIntent(R.id.widget_refresh,
67 | getPendingIntent(INTENT_REFRESH, context, appWidgetId));
68 | views.setOnClickPendingIntent(R.id.widget_full_layout,
69 | getPendingIntent(INTENT_DISPLAY, context, appWidgetId));
70 |
71 | final LocalDate currentDate = prefs.getDateForWidgetId(appWidgetId);
72 |
73 | views.setViewVisibility(R.id.widget_next, prefs.isWidgetAlwaysShowLatest() ? View.GONE : View.VISIBLE);
74 | views.setViewVisibility(R.id.widget_previous, prefs.isWidgetAlwaysShowLatest() ? View.GONE : View.VISIBLE);
75 | views.setViewVisibility(R.id.widget_random, prefs.isWidgetAlwaysShowLatest() ? View.GONE : View.VISIBLE);
76 | views.setViewVisibility(R.id.widget_latest, prefs.isWidgetAlwaysShowLatest() ? View.GONE : View.VISIBLE);
77 |
78 | final String cachedUrl = prefs.getCachedUrl(currentDate);
79 | final String cachedTitle = prefs.getCachedTitle(currentDate);
80 | views.setViewVisibility(R.id.widget_progress, View.VISIBLE);
81 | views.setTextViewText(R.id.widget_title, prefs.getDateForWidgetId(appWidgetId)
82 | .toString(DilbertPreferences.NICE_DATE_FORMATTER));
83 | if (prefs.isWidgetShowTitle() && cachedTitle != null && !cachedTitle.isEmpty()) {
84 | views.setViewVisibility(R.id.widget_strip_title, View.VISIBLE);
85 | views.setTextViewText(R.id.widget_strip_title, cachedTitle);
86 | } else {
87 | views.setViewVisibility(R.id.widget_strip_title, View.GONE);
88 | }
89 | appWidgetManager.updateAppWidget(appWidgetId, views);
90 | if (cachedUrl == null) {
91 | new GetStripUrl(context, new GetStripUrlInterface() {
92 |
93 | @Override
94 | public void imageLoadFailed(String url, Throwable reason) {
95 | currentToast = Toast.makeText(context, "Image Loading failed",
96 | Toast.LENGTH_SHORT);
97 | currentToast.show();
98 | views.setImageViewResource(R.id.widget_image,
99 | R.drawable.cancel);
100 | views.setViewVisibility(R.id.widget_progress, View.GONE);
101 | appWidgetManager.updateAppWidget(appWidgetId, views);
102 | }
103 |
104 | @Override
105 | public void displayImage(String url, String title) {
106 | updateAppWidget(context, appWidgetManager, appWidgetId);
107 | }
108 | }, prefs, currentDate).execute();
109 | } else {
110 | Glide.with(context)
111 | .asBitmap()
112 | .load(cachedUrl)
113 | .apply(new RequestOptions().dontAnimate())
114 | .listener(new RequestListener() {
115 | @Override
116 | public boolean onLoadFailed(@Nullable GlideException e, Object model, Target target, boolean isFirstResource) {
117 | updateAppWidget(context, appWidgetManager, appWidgetId);
118 | return false;
119 | }
120 |
121 | @Override
122 | public boolean onResourceReady(Bitmap resource, Object model, Target target, DataSource dataSource, boolean isFirstResource) {
123 | views.setViewVisibility(R.id.widget_progress, View.GONE);
124 | appWidgetManager.updateAppWidget(appWidgetId, views);
125 | Glide.with(context)
126 | .asBitmap()
127 | .load(cachedUrl)
128 | .apply(new RequestOptions().dontAnimate())
129 | .into(new AppWidgetTarget(context, R.id.widget_image, views, appWidgetId));
130 | return false;
131 | }
132 | })
133 | .into(new AppWidgetTarget(context, R.id.widget_image, views, appWidgetId));
134 | }
135 |
136 | }
137 |
138 | @Override
139 | public void onReceive(@NotNull Context context, @NotNull Intent intent) {
140 | String action = intent.getAction();
141 | if (intent.getExtras() == null)
142 | return;
143 | final int appWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1);
144 | final DilbertPreferences preferences = new DilbertPreferences(context);
145 | if (action == null || appWidgetId == -1) {
146 | super.onReceive(context, intent);
147 | return;
148 | }
149 | if (currentToast != null)
150 | currentToast.cancel();
151 | switch (action) {
152 | case INTENT_PREVIOUS:
153 | preferences.saveDateForWidgetId(appWidgetId, preferences
154 | .getDateForWidgetId(appWidgetId).minusDays(1));
155 | break;
156 | case INTENT_NEXT:
157 | preferences.saveDateForWidgetId(appWidgetId, preferences
158 | .getDateForWidgetId(appWidgetId).plusDays(1));
159 | break;
160 | case INTENT_LATEST:
161 | preferences.saveDateForWidgetId(appWidgetId,
162 | LocalDate.now());
163 | break;
164 | case INTENT_RANDOM:
165 | preferences.saveDateForWidgetId(appWidgetId,
166 | DilbertPreferences.getRandomDate());
167 | break;
168 | case INTENT_REFRESH:
169 | preferences
170 | .removeCache(preferences.getDateForWidgetId(appWidgetId));
171 | break;
172 | case INTENT_DISPLAY:
173 | preferences.saveCurrentDate(preferences
174 | .getDateForWidgetId(appWidgetId));
175 | Intent display = new Intent(context, DilbertFragmentActivity.class);
176 | display.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
177 | context.startActivity(display);
178 | break;
179 | case AppWidgetManager.ACTION_APPWIDGET_UPDATE:
180 | LocalDate current = preferences.getDateForWidgetId(appWidgetId);
181 | if (current.equals(LocalDate.now()
182 | .minusDays(1))) {
183 | preferences.saveDateForWidgetId(appWidgetId,
184 | LocalDate.now());
185 | }
186 | break;
187 | }
188 | updateAppWidget(context, AppWidgetManager.getInstance(context),
189 | appWidgetId);
190 | if (currentToast != null)
191 | currentToast.show();
192 | super.onReceive(context, intent);
193 | }
194 |
195 | @Override
196 | public void onUpdate(Context context, AppWidgetManager appWidgetManager,
197 | int[] appWidgetIds) {
198 | for (int appWidgetId : appWidgetIds) {
199 | updateAppWidget(context, appWidgetManager, appWidgetId);
200 | }
201 | }
202 |
203 | @Override
204 | public void onDeleted(Context context, int[] appWidgetIds) {
205 | if (appWidgetIds == null)
206 | return;
207 | DilbertPreferences prefs = new DilbertPreferences(context);
208 | for (int widgetId : appWidgetIds) {
209 | prefs.deleteDateForWidgetId(widgetId);
210 | }
211 | }
212 | }
213 |
--------------------------------------------------------------------------------
/src/main/res/drawable-hdpi/cancel.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smarek/Simple-Dilbert/5aafd77a4968e2b00f987760f0b914019fa27249/src/main/res/drawable-hdpi/cancel.png
--------------------------------------------------------------------------------
/src/main/res/drawable-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smarek/Simple-Dilbert/5aafd77a4968e2b00f987760f0b914019fa27249/src/main/res/drawable-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/src/main/res/drawable-hdpi/ic_menu_datepicker.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smarek/Simple-Dilbert/5aafd77a4968e2b00f987760f0b914019fa27249/src/main/res/drawable-hdpi/ic_menu_datepicker.png
--------------------------------------------------------------------------------
/src/main/res/drawable-hdpi/ic_menu_favorited.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smarek/Simple-Dilbert/5aafd77a4968e2b00f987760f0b914019fa27249/src/main/res/drawable-hdpi/ic_menu_favorited.png
--------------------------------------------------------------------------------
/src/main/res/drawable-hdpi/ic_menu_not_favorited.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smarek/Simple-Dilbert/5aafd77a4968e2b00f987760f0b914019fa27249/src/main/res/drawable-hdpi/ic_menu_not_favorited.png
--------------------------------------------------------------------------------
/src/main/res/drawable-hdpi/ic_menu_open_at.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smarek/Simple-Dilbert/5aafd77a4968e2b00f987760f0b914019fa27249/src/main/res/drawable-hdpi/ic_menu_open_at.png
--------------------------------------------------------------------------------
/src/main/res/drawable-hdpi/ic_menu_refresh.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smarek/Simple-Dilbert/5aafd77a4968e2b00f987760f0b914019fa27249/src/main/res/drawable-hdpi/ic_menu_refresh.png
--------------------------------------------------------------------------------
/src/main/res/drawable-hdpi/ic_menu_save.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smarek/Simple-Dilbert/5aafd77a4968e2b00f987760f0b914019fa27249/src/main/res/drawable-hdpi/ic_menu_save.png
--------------------------------------------------------------------------------
/src/main/res/drawable-hdpi/ic_menu_share.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smarek/Simple-Dilbert/5aafd77a4968e2b00f987760f0b914019fa27249/src/main/res/drawable-hdpi/ic_menu_share.png
--------------------------------------------------------------------------------
/src/main/res/drawable-hdpi/ic_menu_shuffle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smarek/Simple-Dilbert/5aafd77a4968e2b00f987760f0b914019fa27249/src/main/res/drawable-hdpi/ic_menu_shuffle.png
--------------------------------------------------------------------------------
/src/main/res/drawable-hdpi/ic_menu_zoom.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smarek/Simple-Dilbert/5aafd77a4968e2b00f987760f0b914019fa27249/src/main/res/drawable-hdpi/ic_menu_zoom.png
--------------------------------------------------------------------------------
/src/main/res/drawable-hdpi/ic_navigation_accept.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smarek/Simple-Dilbert/5aafd77a4968e2b00f987760f0b914019fa27249/src/main/res/drawable-hdpi/ic_navigation_accept.png
--------------------------------------------------------------------------------
/src/main/res/drawable-hdpi/widget_latest.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smarek/Simple-Dilbert/5aafd77a4968e2b00f987760f0b914019fa27249/src/main/res/drawable-hdpi/widget_latest.png
--------------------------------------------------------------------------------
/src/main/res/drawable-hdpi/widget_next.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smarek/Simple-Dilbert/5aafd77a4968e2b00f987760f0b914019fa27249/src/main/res/drawable-hdpi/widget_next.png
--------------------------------------------------------------------------------
/src/main/res/drawable-hdpi/widget_preview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smarek/Simple-Dilbert/5aafd77a4968e2b00f987760f0b914019fa27249/src/main/res/drawable-hdpi/widget_preview.png
--------------------------------------------------------------------------------
/src/main/res/drawable-hdpi/widget_previous.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smarek/Simple-Dilbert/5aafd77a4968e2b00f987760f0b914019fa27249/src/main/res/drawable-hdpi/widget_previous.png
--------------------------------------------------------------------------------
/src/main/res/drawable-ldpi/cancel.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smarek/Simple-Dilbert/5aafd77a4968e2b00f987760f0b914019fa27249/src/main/res/drawable-ldpi/cancel.png
--------------------------------------------------------------------------------
/src/main/res/drawable-ldpi/file.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smarek/Simple-Dilbert/5aafd77a4968e2b00f987760f0b914019fa27249/src/main/res/drawable-ldpi/file.png
--------------------------------------------------------------------------------
/src/main/res/drawable-ldpi/folder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smarek/Simple-Dilbert/5aafd77a4968e2b00f987760f0b914019fa27249/src/main/res/drawable-ldpi/folder.png
--------------------------------------------------------------------------------
/src/main/res/drawable-ldpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smarek/Simple-Dilbert/5aafd77a4968e2b00f987760f0b914019fa27249/src/main/res/drawable-ldpi/ic_launcher.png
--------------------------------------------------------------------------------
/src/main/res/drawable-ldpi/ic_menu_datepicker.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smarek/Simple-Dilbert/5aafd77a4968e2b00f987760f0b914019fa27249/src/main/res/drawable-ldpi/ic_menu_datepicker.png
--------------------------------------------------------------------------------
/src/main/res/drawable-ldpi/ic_menu_favorited.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smarek/Simple-Dilbert/5aafd77a4968e2b00f987760f0b914019fa27249/src/main/res/drawable-ldpi/ic_menu_favorited.png
--------------------------------------------------------------------------------
/src/main/res/drawable-ldpi/ic_menu_not_favorited.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smarek/Simple-Dilbert/5aafd77a4968e2b00f987760f0b914019fa27249/src/main/res/drawable-ldpi/ic_menu_not_favorited.png
--------------------------------------------------------------------------------
/src/main/res/drawable-ldpi/ic_menu_open_at.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smarek/Simple-Dilbert/5aafd77a4968e2b00f987760f0b914019fa27249/src/main/res/drawable-ldpi/ic_menu_open_at.png
--------------------------------------------------------------------------------
/src/main/res/drawable-ldpi/ic_menu_refresh.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smarek/Simple-Dilbert/5aafd77a4968e2b00f987760f0b914019fa27249/src/main/res/drawable-ldpi/ic_menu_refresh.png
--------------------------------------------------------------------------------
/src/main/res/drawable-ldpi/ic_menu_save.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smarek/Simple-Dilbert/5aafd77a4968e2b00f987760f0b914019fa27249/src/main/res/drawable-ldpi/ic_menu_save.png
--------------------------------------------------------------------------------
/src/main/res/drawable-ldpi/ic_menu_share.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smarek/Simple-Dilbert/5aafd77a4968e2b00f987760f0b914019fa27249/src/main/res/drawable-ldpi/ic_menu_share.png
--------------------------------------------------------------------------------
/src/main/res/drawable-ldpi/ic_menu_shuffle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smarek/Simple-Dilbert/5aafd77a4968e2b00f987760f0b914019fa27249/src/main/res/drawable-ldpi/ic_menu_shuffle.png
--------------------------------------------------------------------------------
/src/main/res/drawable-ldpi/ic_menu_zoom.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smarek/Simple-Dilbert/5aafd77a4968e2b00f987760f0b914019fa27249/src/main/res/drawable-ldpi/ic_menu_zoom.png
--------------------------------------------------------------------------------
/src/main/res/drawable-ldpi/ic_navigation_accept.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smarek/Simple-Dilbert/5aafd77a4968e2b00f987760f0b914019fa27249/src/main/res/drawable-ldpi/ic_navigation_accept.png
--------------------------------------------------------------------------------
/src/main/res/drawable-ldpi/widget_latest.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smarek/Simple-Dilbert/5aafd77a4968e2b00f987760f0b914019fa27249/src/main/res/drawable-ldpi/widget_latest.png
--------------------------------------------------------------------------------
/src/main/res/drawable-ldpi/widget_next.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smarek/Simple-Dilbert/5aafd77a4968e2b00f987760f0b914019fa27249/src/main/res/drawable-ldpi/widget_next.png
--------------------------------------------------------------------------------
/src/main/res/drawable-ldpi/widget_preview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smarek/Simple-Dilbert/5aafd77a4968e2b00f987760f0b914019fa27249/src/main/res/drawable-ldpi/widget_preview.png
--------------------------------------------------------------------------------
/src/main/res/drawable-ldpi/widget_previous.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smarek/Simple-Dilbert/5aafd77a4968e2b00f987760f0b914019fa27249/src/main/res/drawable-ldpi/widget_previous.png
--------------------------------------------------------------------------------
/src/main/res/drawable-mdpi/cancel.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smarek/Simple-Dilbert/5aafd77a4968e2b00f987760f0b914019fa27249/src/main/res/drawable-mdpi/cancel.png
--------------------------------------------------------------------------------
/src/main/res/drawable-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smarek/Simple-Dilbert/5aafd77a4968e2b00f987760f0b914019fa27249/src/main/res/drawable-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/src/main/res/drawable-mdpi/ic_menu_datepicker.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smarek/Simple-Dilbert/5aafd77a4968e2b00f987760f0b914019fa27249/src/main/res/drawable-mdpi/ic_menu_datepicker.png
--------------------------------------------------------------------------------
/src/main/res/drawable-mdpi/ic_menu_favorited.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smarek/Simple-Dilbert/5aafd77a4968e2b00f987760f0b914019fa27249/src/main/res/drawable-mdpi/ic_menu_favorited.png
--------------------------------------------------------------------------------
/src/main/res/drawable-mdpi/ic_menu_not_favorited.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smarek/Simple-Dilbert/5aafd77a4968e2b00f987760f0b914019fa27249/src/main/res/drawable-mdpi/ic_menu_not_favorited.png
--------------------------------------------------------------------------------
/src/main/res/drawable-mdpi/ic_menu_open_at.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smarek/Simple-Dilbert/5aafd77a4968e2b00f987760f0b914019fa27249/src/main/res/drawable-mdpi/ic_menu_open_at.png
--------------------------------------------------------------------------------
/src/main/res/drawable-mdpi/ic_menu_refresh.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smarek/Simple-Dilbert/5aafd77a4968e2b00f987760f0b914019fa27249/src/main/res/drawable-mdpi/ic_menu_refresh.png
--------------------------------------------------------------------------------
/src/main/res/drawable-mdpi/ic_menu_save.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smarek/Simple-Dilbert/5aafd77a4968e2b00f987760f0b914019fa27249/src/main/res/drawable-mdpi/ic_menu_save.png
--------------------------------------------------------------------------------
/src/main/res/drawable-mdpi/ic_menu_share.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smarek/Simple-Dilbert/5aafd77a4968e2b00f987760f0b914019fa27249/src/main/res/drawable-mdpi/ic_menu_share.png
--------------------------------------------------------------------------------
/src/main/res/drawable-mdpi/ic_menu_shuffle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smarek/Simple-Dilbert/5aafd77a4968e2b00f987760f0b914019fa27249/src/main/res/drawable-mdpi/ic_menu_shuffle.png
--------------------------------------------------------------------------------
/src/main/res/drawable-mdpi/ic_menu_zoom.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smarek/Simple-Dilbert/5aafd77a4968e2b00f987760f0b914019fa27249/src/main/res/drawable-mdpi/ic_menu_zoom.png
--------------------------------------------------------------------------------
/src/main/res/drawable-mdpi/ic_navigation_accept.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smarek/Simple-Dilbert/5aafd77a4968e2b00f987760f0b914019fa27249/src/main/res/drawable-mdpi/ic_navigation_accept.png
--------------------------------------------------------------------------------
/src/main/res/drawable-mdpi/widget_latest.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smarek/Simple-Dilbert/5aafd77a4968e2b00f987760f0b914019fa27249/src/main/res/drawable-mdpi/widget_latest.png
--------------------------------------------------------------------------------
/src/main/res/drawable-mdpi/widget_next.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smarek/Simple-Dilbert/5aafd77a4968e2b00f987760f0b914019fa27249/src/main/res/drawable-mdpi/widget_next.png
--------------------------------------------------------------------------------
/src/main/res/drawable-mdpi/widget_preview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smarek/Simple-Dilbert/5aafd77a4968e2b00f987760f0b914019fa27249/src/main/res/drawable-mdpi/widget_preview.png
--------------------------------------------------------------------------------
/src/main/res/drawable-mdpi/widget_previous.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smarek/Simple-Dilbert/5aafd77a4968e2b00f987760f0b914019fa27249/src/main/res/drawable-mdpi/widget_previous.png
--------------------------------------------------------------------------------
/src/main/res/drawable-xhdpi/cancel.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smarek/Simple-Dilbert/5aafd77a4968e2b00f987760f0b914019fa27249/src/main/res/drawable-xhdpi/cancel.png
--------------------------------------------------------------------------------
/src/main/res/drawable-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smarek/Simple-Dilbert/5aafd77a4968e2b00f987760f0b914019fa27249/src/main/res/drawable-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/src/main/res/drawable-xhdpi/ic_menu_datepicker.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smarek/Simple-Dilbert/5aafd77a4968e2b00f987760f0b914019fa27249/src/main/res/drawable-xhdpi/ic_menu_datepicker.png
--------------------------------------------------------------------------------
/src/main/res/drawable-xhdpi/ic_menu_favorited.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smarek/Simple-Dilbert/5aafd77a4968e2b00f987760f0b914019fa27249/src/main/res/drawable-xhdpi/ic_menu_favorited.png
--------------------------------------------------------------------------------
/src/main/res/drawable-xhdpi/ic_menu_not_favorited.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smarek/Simple-Dilbert/5aafd77a4968e2b00f987760f0b914019fa27249/src/main/res/drawable-xhdpi/ic_menu_not_favorited.png
--------------------------------------------------------------------------------
/src/main/res/drawable-xhdpi/ic_menu_open_at.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smarek/Simple-Dilbert/5aafd77a4968e2b00f987760f0b914019fa27249/src/main/res/drawable-xhdpi/ic_menu_open_at.png
--------------------------------------------------------------------------------
/src/main/res/drawable-xhdpi/ic_menu_refresh.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smarek/Simple-Dilbert/5aafd77a4968e2b00f987760f0b914019fa27249/src/main/res/drawable-xhdpi/ic_menu_refresh.png
--------------------------------------------------------------------------------
/src/main/res/drawable-xhdpi/ic_menu_save.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smarek/Simple-Dilbert/5aafd77a4968e2b00f987760f0b914019fa27249/src/main/res/drawable-xhdpi/ic_menu_save.png
--------------------------------------------------------------------------------
/src/main/res/drawable-xhdpi/ic_menu_share.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smarek/Simple-Dilbert/5aafd77a4968e2b00f987760f0b914019fa27249/src/main/res/drawable-xhdpi/ic_menu_share.png
--------------------------------------------------------------------------------
/src/main/res/drawable-xhdpi/ic_menu_shuffle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smarek/Simple-Dilbert/5aafd77a4968e2b00f987760f0b914019fa27249/src/main/res/drawable-xhdpi/ic_menu_shuffle.png
--------------------------------------------------------------------------------
/src/main/res/drawable-xhdpi/ic_menu_zoom.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smarek/Simple-Dilbert/5aafd77a4968e2b00f987760f0b914019fa27249/src/main/res/drawable-xhdpi/ic_menu_zoom.png
--------------------------------------------------------------------------------
/src/main/res/drawable-xhdpi/ic_navigation_accept.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smarek/Simple-Dilbert/5aafd77a4968e2b00f987760f0b914019fa27249/src/main/res/drawable-xhdpi/ic_navigation_accept.png
--------------------------------------------------------------------------------
/src/main/res/drawable-xhdpi/widget_latest.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smarek/Simple-Dilbert/5aafd77a4968e2b00f987760f0b914019fa27249/src/main/res/drawable-xhdpi/widget_latest.png
--------------------------------------------------------------------------------
/src/main/res/drawable-xhdpi/widget_next.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smarek/Simple-Dilbert/5aafd77a4968e2b00f987760f0b914019fa27249/src/main/res/drawable-xhdpi/widget_next.png
--------------------------------------------------------------------------------
/src/main/res/drawable-xhdpi/widget_preview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smarek/Simple-Dilbert/5aafd77a4968e2b00f987760f0b914019fa27249/src/main/res/drawable-xhdpi/widget_preview.png
--------------------------------------------------------------------------------
/src/main/res/drawable-xhdpi/widget_previous.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smarek/Simple-Dilbert/5aafd77a4968e2b00f987760f0b914019fa27249/src/main/res/drawable-xhdpi/widget_previous.png
--------------------------------------------------------------------------------
/src/main/res/drawable-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smarek/Simple-Dilbert/5aafd77a4968e2b00f987760f0b914019fa27249/src/main/res/drawable-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/src/main/res/drawable-xxhdpi/widget_preview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smarek/Simple-Dilbert/5aafd77a4968e2b00f987760f0b914019fa27249/src/main/res/drawable-xxhdpi/widget_preview.png
--------------------------------------------------------------------------------
/src/main/res/drawable-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smarek/Simple-Dilbert/5aafd77a4968e2b00f987760f0b914019fa27249/src/main/res/drawable-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/src/main/res/drawable-xxxhdpi/widget_preview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smarek/Simple-Dilbert/5aafd77a4968e2b00f987760f0b914019fa27249/src/main/res/drawable-xxxhdpi/widget_preview.png
--------------------------------------------------------------------------------
/src/main/res/layout/activity_dilbert_fragments.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/src/main/res/layout/activity_folder_picker.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
14 |
15 |
--------------------------------------------------------------------------------
/src/main/res/layout/fragment_dilbert.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
13 |
14 |
21 |
22 |
27 |
28 |
--------------------------------------------------------------------------------
/src/main/res/layout/fragment_folder_picker.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
15 |
16 |
21 |
22 |
--------------------------------------------------------------------------------
/src/main/res/layout/horizontal_divider.xml:
--------------------------------------------------------------------------------
1 |
2 |
13 |
--------------------------------------------------------------------------------
/src/main/res/layout/item_folder_picker.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
18 |
19 |
25 |
26 |
--------------------------------------------------------------------------------
/src/main/res/layout/preferences.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/src/main/res/layout/preferences_contents.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
10 |
15 |
16 |
17 |
18 |
26 |
27 |
32 |
33 |
34 |
35 |
43 |
44 |
52 |
53 |
61 |
62 |
70 |
71 |
79 |
80 |
90 |
91 |
96 |
97 |
98 |
99 |
107 |
108 |
116 |
117 |
122 |
123 |
135 |
136 |
144 |
145 |
146 |
147 |
148 |
153 |
154 |
155 |
156 |
162 |
163 |
164 |
165 |
171 |
172 |
173 |
174 |
--------------------------------------------------------------------------------
/src/main/res/layout/widget_layout.xml:
--------------------------------------------------------------------------------
1 |
2 |
12 |
13 |
19 |
20 |
32 |
33 |
44 |
45 |
56 |
57 |
68 |
69 |
78 |
79 |
88 |
89 |
90 |
95 |
96 |
107 |
108 |
114 |
115 |
123 |
124 |
130 |
131 |
132 |
--------------------------------------------------------------------------------
/src/main/res/values-cs/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Vybrat datum
4 | Aplikaci vytvořil Marek Sebera 2013–2015, marek@msebera.cz
5 | Dilbert
6 | Dilbert.com není dostupná. Načítání selhalo.
7 | Zobraz nejnovější
8 | Zobraz nejstarší
9 | Aktualizovat
10 | Váš telefon nepodporuje ukládání obrázků
11 | Uložit do telefonu
12 | Přidat do oblíbených
13 | Odebrat z oblíbených
14 | Zobrazit oblíbené
15 | Zvětšit
16 | Nemáte žádné oblíbené
17 | Náhodný díl
18 | Sdílet komiks Dilbert
19 | Sdílet
20 | Nastavení
21 | Vynutit horizontální zobrazení
22 | Invertovat horizontální zobrazení
23 | Použít tmavé zobrazení
24 | Skrývat lišty
25 | Zobrazit softwarovou licenci
26 | Nastavení
27 | Adresář pro stahování:
28 | Nesdílet obrázek, pouze odkaz
29 | Další
30 | Nejnovější
31 | Předchozí
32 | Náhodný
33 | Otevřít tento díl
34 | Adresář není zapisovatelný
35 | Vybrat aktuální adresář
36 | Zbrazit skryté soubory
37 | Zobrazit soubory
38 | Přejít na výchozí adresář
39 | Adresář nelze otevřít
40 | Vždy otevřít na posledním komiks
41 | Ukázat vždy poslední komiks
42 | Obecná nastavení
43 | Nastavení aplikace
44 | Nastavení widgetů
45 | Webový prohlížeč
46 | Základní zvětšení
47 | Zobrazit offline
48 |
49 |
50 |
--------------------------------------------------------------------------------
/src/main/res/values-de/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Datum auswählen
5 | von Marek Sebera 2013–2015, marek@msebera.cz
6 | Dilbert
7 | Dilbert.com ist nicht verfügbar. Laden fehlgeschlagen.
8 | Neueste anzeigen
9 | Älteste anzeigen
10 | Aktualisieren
11 | Auf diesem Gerät nicht unterstützt
12 | Aktuelles Bild speichern
13 | Als Favorit markieren
14 | Nicht mehr als Favorit markieren
15 | Favoriten anzeigen
16 | Zoom
17 | Sie haben keine Lieblingsstrips
18 | Zufällig
19 | Dilbert Comics teilen
20 | Dies teilen
21 | Nächstes
22 | Neueste
23 | Vorheriges
24 | Zufällig
25 | Einstellungen
26 | Ausrichtung im Querformat erzwingen
27 | Reverse-Querformat
28 | Dunkle Hintergründe verwenden
29 | Werkzeugleisten standardmäßig verbergen
30 | Softwarelizenz anzeigen
31 | Einstellungen
32 | Download-Zielordner:
33 | Nicht Bild, sondern nur einen Link teilen
34 | Öffnen an diesem Tag
35 | Aktueller Ordner ist nicht beschreibbar
36 | Wählen Sie diesen Ordner
37 | Versteckte Dateien anzeigen
38 | Dateien anzeigen
39 | Zur Standard-Ordner
40 | Kann nicht geöffnet werden Ordner
41 | Immer auf den neuesten Streifen öffnen
42 | Widget zeigen immer neuesten Streifen
43 | General Settings
44 | Application Settings
45 | Widgets Settings
46 | Browser öffnen
47 | Standard-Zoomstufe
48 | Browsen Sie offline
49 |
50 |
51 |
--------------------------------------------------------------------------------
/src/main/res/values-es/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Seleccione Fechas
5 | Hecho por Marek Sebera 2013–2015, marek@msebera.cz
6 | Dilbert
7 | Dilbert.com no está disponible. La página no pudo cargarse.
8 | Ver últimos
9 | Mostrar más antiguos
10 | Actualizar
11 | No compatible con este dispositivo
12 | Guardar imagen actual
13 | Favorito
14 | Desmarcar como favorito
15 | Mostrar favoritos
16 | Zoom
17 | No tiene tiras favoritas
18 | Aleatorio
19 | Comparte Dilbert Comics
20 | Compartir
21 | Siguiente
22 | Lo último
23 | Anterior
24 | Aleatorio
25 | Preferencias
26 | Forzar orientación horizontal
27 | Modo horizontal inversa
28 | Usar fondos oscuros
29 | Ocultar barras de herramientas por defecto
30 | Mostrar la licencia del software
31 | Configuración
32 | Carpeta de destino de Descargas:
33 | No comparta la imagen, sólo una URL
34 | Abrir este día
35 | Carpeta actual no tiene permisos de escritura
36 | Seleccione esta carpeta
37 | Mostrar archivos ocultos
38 | Mostrar todos los archivos
39 | Ir a la carpeta predeterminada
40 | No se puede abrir la carpeta
41 | Abra siempre en la última franja
42 | Widget Muestra siempre última franja
43 | General Settings
44 | Application Settings
45 | Widgets Settings
46 | Navegador abierto
47 | Elegir nivel de zoom predeterminado
48 | Navegar fuera de línea
49 |
50 |
51 |
--------------------------------------------------------------------------------
/src/main/res/values-fr/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Choisir une date
5 | Conçu par Marek Sebera 2013–2015
6 | Dilbert
7 | Dilbert.com n\'est pas disponible. Échec du chargement.
8 | Afficher le plus récent
9 | Afficher le plus ancien
10 | Actualiser
11 | Non supporté sur cet appareil
12 | Sauvegarder l\'image actuelle
13 | Favoris
14 | Retirer des favoris
15 | Afficher les favoris
16 | Zoomer
17 | Vous n\'avez pas de strip favoris
18 | Au hasard
19 | Partager les bandes dessinées Dilbert
20 | Partager ça
21 | Suivant
22 | Plus récent
23 | Précédent
24 | Au hasard
25 | Préférences
26 | Forcer l\'orientation paysage
27 | Inverser l\'orientation paysage
28 | Utiliser des arrières-plans foncés
29 | Cacher les barres d\'outils par défaut
30 | Afficher la licence de logiciel
31 | Réglages
32 | Dossier de destination pour les téléchargements:
33 | Ne pas partager d\'image, seulement l\'URL
34 | Ouvrir ce jour
35 | Le dossier actuel n\'est pas inscriptible
36 | Sélectionner ce dossier
37 | Afficher les fichiers cachés
38 | Afficher les fichiers
39 | Aller dans le dossier par défaut
40 | Impossible d\'ouvrir le dossier
41 | Toujours ouvrir le dernier strip
42 | Le widget montre toujours le dernier strip
43 | Réglages généraux
44 | Réglages de l\'application
45 | Réglages du widget
46 | Ouvrir le navigateur
47 | Choisir le niveau de zoom par défaut
48 | Naviguer hors connexion
49 |
50 |
51 |
--------------------------------------------------------------------------------
/src/main/res/values-it/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Seleziona data
5 | Realizzato da Marek Sebera 2013–2015, marek@msebera.cz
6 | Dilbert
7 | Dilbert.com non disponibile. Caricamento non riuscito.
8 | Visualizza più recenti
9 | Visualizza meno recenti
10 | Aggiorna
11 | Non supportato su questo dispositivo
12 | Salva immagine corrente
13 | Preferiti
14 | Non preferito
15 | Visualizza preferiti
16 | Zoom
17 | Nessuna stringa tra i preferiti
18 | Casuale
19 | Condividi Dilbert Comics
20 | Condividi
21 | Prossimo
22 | Più recente
23 | Precedente
24 | Casuale
25 | Preferenze
26 | Forza Orientamento Paesaggio
27 | Modalità orizzontale Reverse
28 | Usa Sfondi Scuri
29 | Nascondi la Barra Strumenti di default
30 | Visualizza Licenza Software
31 | Impostazioni
32 | Cartella di destinazione: Download
33 | Non condividere l\'immagine, solo l\'URL
34 | Aprire questo giorno
35 | Cartella corrente non è scrivibile
36 | Selezionare questa cartella
37 | Mostra file nascosti
38 | Mostra file
39 | Vai alla cartella predefinita
40 | Impossibile aprire la cartella
41 | Aprire sempre in ultima striscia
42 | Widget Mostra sempre ultima striscia
43 | General Settings
44 | Application Settings
45 | Widgets Settings
46 | Aprire il browser
47 | Scegliere il livello di zoom predefinito
48 | Navigare offline
49 |
50 |
51 |
--------------------------------------------------------------------------------
/src/main/res/values-pt/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Escolha data
5 | Feito por Marek Sebera 2013–2015, marek@msebera.cz
6 | Dilbert
7 | Dilbert.com não está disponível. Carregamento falhou.
8 | Mostrar mais recente
9 | Mostrar mais antiga
10 | Atualizar
11 | Não suportado neste aparelho
12 | Salvar imagem atual
13 | Favoritar
14 | Excluir dos favoritos
15 | Mostrar favoritos
16 | Zoom
17 | Você não tem tiras favoritas
18 | Aleatório
19 | Compartilhar Quadrinhos do Dilbert
20 | Compartilhar esta
21 | Próxima
22 | Mais recente
23 | Anterior
24 | Aleatório
25 | Preferências
26 | Forçar Orientação Paisagem
27 | Modo paisagem reverter
28 | Use Fundos Escuros
29 | Ocultar Barras de Ferramentas como Padrão
30 | Exibir Licença de Software
31 | Configurações
32 | Pasta de destino dos downloads:
33 | Não compartilhar a imagem, apenas uma URL
34 | Abrir este dia
35 | Pasta atual não é gravável
36 | Select this folder
37 | Selecione esta pasta
38 | Mostrar arquivos
39 | Vá até a pasta padrão
40 | Não pode abrir a pasta
41 | Abra sempre na mais recente faixa
42 | Widget mostrar sempre mais recente faixa
43 | General Settings
44 | Application Settings
45 | Widgets Settings
46 | Abrir navegador
47 | Escolher o nível de zoom padrão
48 | Navegar offline
49 |
50 |
51 |
--------------------------------------------------------------------------------
/src/main/res/values-ru/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Выберите дату
5 | Автор: Marek Sebera 2013–2015, marek@msebera.cz
6 | Дилберт
7 | Не удалось загрузить - Dilbert.com недоступен.
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 | Делиться только URL, а не рисунками
34 | Открыть в этот день
35 | Текущая папка не доступна для записи
36 | Выберите эту папку
37 | Показывать скрытые файлы
38 | Показать файлы
39 | Перейти к папке по умолчанию
40 | Не могу открыть папку
41 | Всегда открывайте на последней полосе
42 | Виджет всегда покажут последнюю полосу
43 | General Settings
44 | Application Settings
45 | Widgets Settings
46 | Открыть браузер
47 | Выберите уровень масштабирования по умолчанию
48 | автономный режим
49 |
50 |
51 |
--------------------------------------------------------------------------------
/src/main/res/values-sv/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Välj datum
5 | Gjord av Marek Sebera 2013–2015, marek@msebera.cz
6 | Dilbert
7 | Dilbert.com är ej tillgänglig. Laddning misslyckades.
8 | Visa senast
9 | Visa äldst
10 | Uppdatera
11 | Stöds inte på denna enhet
12 | Spara aktuella bild
13 | Favorit
14 | Ta bort favorit
15 | Visa favoriter
16 | Zoom
17 | Du har inga favorit serier
18 | Slumpvis
19 | Dela Dilbert Comics
20 | Dela detta
21 | Nästa
22 | Senast
23 | Föregående
24 | Slumpvis
25 | Preferenser
26 | Tvinga fram Liggande Orientering
27 | Omvänd liggande läge
28 | Använd Mörka Bakgrunder
29 | Göm Verktygsfält som Standard
30 | Visa Programvarulicens
31 | Inställningar
32 | Laddar ner målmapp:
33 | Dela inte bilden, bara en URL
34 | Öppna denna dag
35 | Aktuell mapp är inte skrivbar
36 | Markera den här mappen
37 | Visa dolda filer
38 | Visa filer
39 | Gå till standardmappen
40 | Kan inte öppna mappen
41 | Alltid öppet på senaste strip
42 | Widget visar alltid senaste remsa
43 | General Settings
44 | Application Settings
45 | Widgets Settings
46 | Öppna webbläsaren
47 | Välj standardzoomnivå
48 | bläddra offline
49 |
50 |
51 |
--------------------------------------------------------------------------------
/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Simple Dilbert
4 | Apache License 2.0
5 | Choose date
6 | Made by Marek Sebera 2013–2015, marek@msebera.cz
7 | Dilbert
8 | Dilbert.com is not available. Loading failed.
9 | Show latest
10 | Show oldest
11 | Browse offline
12 | Refresh
13 | Open in Browser
14 | Unsupported on this device
15 | Save current image
16 | Favorite
17 | Unfavorite
18 | Show favorites
19 | Zoom
20 | You have no favorite strips
21 | Random
22 | Share Dilbert Comics
23 | Share this
24 | Next
25 | Latest
26 | Previous
27 | Random
28 | Preferences
29 | Force Landscape Orientation
30 | Reverse Landscape Mode
31 | Use Dark Backgrounds
32 | Hide Toolbars By Default
33 | Display Software License
34 | Settings
35 | Downloads target folder:
36 | Don\'t share image, just a URL
37 | Horizontal divider
38 | Open this day
39 | Current folder is not writable
40 | Select this folder
41 | Show hidden files
42 | Show files
43 | Go to default folder
44 | Cannot open folder
45 | Always open on latest strip
46 | Widget show always latest strip
47 | Widget show strip title
48 | General Settings
49 | Application Settings
50 | Widgets Settings
51 | Choose default zoom level
52 | Export cached URLs
53 |
54 |
55 |
--------------------------------------------------------------------------------
/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/src/main/res/xml/daily_widget.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/src/main/res/xml/filepaths.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------