Compare commits
156 commits
Author | SHA1 | Date | |
---|---|---|---|
|
774c5bec59 | ||
|
c65aab3840 | ||
|
c2bea7bec0 | ||
|
36bd1af0b9 | ||
|
67b0bbbfd2 | ||
|
b80cc3ab04 | ||
|
37f586912f | ||
|
de378b8adc | ||
|
8161353de7 | ||
|
c42b4995b3 | ||
|
5a5d1d66b3 | ||
|
ceb0002546 | ||
|
0cf003ea38 | ||
|
904f2af20a | ||
|
d923630b42 | ||
|
a76a463cd4 | ||
|
c9c10a6fd4 | ||
|
e5866ffb2b | ||
|
97d9bf434e | ||
|
f1464b5f8f | ||
|
369abf14a5 | ||
|
ceb98c4258 | ||
|
4ebd393250 | ||
|
f3b3c6a160 | ||
|
ff62aa5a07 | ||
|
6fbd399a4b | ||
|
fa7e37ccba | ||
|
20d75acd39 | ||
|
7b1897b2e0 | ||
|
93aadae64b | ||
|
a7dd4a6166 | ||
|
cf1f0100ba | ||
|
875f613cd9 | ||
|
ea1ee6bc40 | ||
|
a66d7bf3b2 | ||
|
c5849d7d6a | ||
|
cc16b84011 | ||
|
0f6b1c3ec3 | ||
|
a686fea141 | ||
|
f9951f3b16 | ||
|
073a688db9 | ||
|
1f3cde9326 | ||
|
8326c2196c | ||
|
7361d4bc3f | ||
|
1948c28cff | ||
|
d1be7f5215 | ||
|
8d019b6c3b | ||
|
0708fcb02b | ||
|
c5e3a42005 | ||
|
8c3c7da2ab | ||
|
3bc6afad03 | ||
|
194fbf6927 | ||
|
6d45620181 | ||
|
c9d3693515 | ||
|
a618da97d8 | ||
|
3ec8ab89c6 | ||
|
938a54fc74 | ||
|
0732fe1b91 | ||
|
a7054d3ff8 | ||
25020c1692 | |||
b1fb5d646a | |||
|
51c3f68f22 | ||
|
a80f6ebb45 | ||
|
49c6a97ce7 | ||
|
4f44d1acd3 | ||
|
24571f6921 | ||
|
fb8eb60974 | ||
|
b833311353 | ||
|
6f310985d6 | ||
|
6d62c70359 | ||
|
d34cbe1ed1 | ||
|
66cbf9a47d | ||
|
b1d5e980c2 | ||
|
f693418d64 | ||
|
04e89e516c | ||
|
5854075080 | ||
|
1f8babd708 | ||
|
2618bdf5ad | ||
|
19884c33c1 | ||
|
2289c04f0f | ||
|
1129f1848a | ||
|
c77fd1db01 | ||
|
b072111b8b | ||
|
351b2d6447 | ||
|
ddc2549c66 | ||
|
c1d34fc5db | ||
|
33db3656fa | ||
|
e194d2159e | ||
|
e66c72d255 | ||
|
4cd88400ee | ||
|
48a00b099e | ||
|
e6b2fa606e | ||
|
0b66ef801f | ||
|
e1ea19f02a | ||
|
af9e3ad8a3 | ||
|
d76fe4b82b | ||
|
dc07beafc3 | ||
|
423970ce39 | ||
|
215f67308e | ||
|
376449486b | ||
|
1e15374229 | ||
|
dbbd3cef35 | ||
|
918adcf358 | ||
|
957573ea83 | ||
|
65ba10712f | ||
|
099eb9ca78 | ||
|
ad3966dd85 | ||
|
9a6798f9f4 | ||
|
2cc74ea613 | ||
|
cebf0f473b | ||
|
9da1a910b3 | ||
|
38cf36b46c | ||
|
68be2f3a4a | ||
|
51093e0c3d | ||
|
d53128e5cb | ||
|
82ccc54436 | ||
|
f6ab146fd7 | ||
|
80f8f10bf9 | ||
|
dcc8728171 | ||
|
b48dc5dcac | ||
|
618f3eaaba | ||
|
08242760bb | ||
|
a88dc5d13c | ||
|
586875e32b | ||
|
43a2ad25bc | ||
|
6cac47763c | ||
|
5921549a9c | ||
|
07acb6bd02 | ||
|
dd0432f718 | ||
|
1bb061b563 | ||
|
d8d74de22b | ||
|
8df13fe283 | ||
|
724a0dbc9c | ||
|
1f873e2057 | ||
|
ea66b57979 | ||
|
cbb98f7c60 | ||
|
ed511bec01 | ||
|
204429b8d5 | ||
|
120c48f702 | ||
|
e4992870fe | ||
|
be4309ecd6 | ||
|
a07bc144d8 | ||
|
3442f018e9 | ||
8aec1aa74f | |||
|
a3cb818db4 | ||
|
15240fca49 | ||
|
32a74e293a | ||
|
b1c2219b4d | ||
|
7215cbd7e6 | ||
|
06119a52cd | ||
|
755adb2ad9 | ||
|
1a99d53c5d | ||
|
28dac50d84 | ||
|
255c6d650e | ||
|
8ac5c2dee9 | ||
|
0393bf274f |
61
.github/ISSUE_TEMPLATE.md
vendored
|
@ -1,53 +1,24 @@
|
|||
#### General information
|
||||
|
||||
* **App version:**
|
||||
* **System:**
|
||||
* **Pod:**
|
||||
|
||||
* **Android Device:**
|
||||
* **Android API Version:**
|
||||
* **App version:**
|
||||
* **App source:**
|
||||
* **Pod version:**
|
||||
#### Description
|
||||
|
||||
|
||||
#### Log
|
||||
|
||||
<!--
|
||||
I have:
|
||||
Look for already reported issues before posting!
|
||||
Also take a look at documentation and wiki, or write in the project chat.
|
||||
|
||||
- At least version 0.2.0 installed, see About-> Debug. If it is not visible you have an very old version, and
|
||||
your issue will be closed.
|
||||
- searched open and closed issues for duplicates
|
||||
- read <https://github.com/Diaspora-for-Android/dandelion/blob/master/CONTRIBUTING.md>
|
||||
- not submitted translations - see [Crowdin](https://crowdin.com/project/diaspora-for-android/invite)
|
||||
App version: The version of the app installed and the installation source. Example: v0.3.5 F-Droid
|
||||
Please keep in mind that only the latest downloadable version is supported and that there are no backports to older versions.
|
||||
System: Information about where the app is running. Give all details you know, but at least the Android OS version.
|
||||
Example: Android 8.0.1, Nexus 5, LineageOS
|
||||
|
||||
Description:
|
||||
What this is about, what happens and what is expected to happen. What needs to be done for it to happen.
|
||||
If a crash is happening a log is needed. Screenshots or demonstration videos are always helpful too.
|
||||
-->
|
||||
|
||||
#### Steps to reproduce
|
||||
|
||||
1. …
|
||||
2. …
|
||||
3. …
|
||||
|
||||
|
||||
#### Expected result
|
||||
**What is the expected output?**
|
||||
|
||||
**What do you see instead?**
|
||||
|
||||
|
||||
Upload screenshots via drag&drop if needed and apply resizing:
|
||||
`<img width="30%" height="30%" src="https://cloud.githubusercontent.com/assets/67..b55.jpg">`
|
||||
|
||||
|
||||
#### Debug output
|
||||
|
||||
Please post the output of adb logcat. The log should begin with the start of dandelion* and include all the steps it takes to reproduce the problem.
|
||||
IMPORTANT: In-App debug log must be activated and the app restarted first!
|
||||
|
||||
````
|
||||
adb logcat -s com.github.dfa.diaspora_android
|
||||
````
|
||||
|
||||
#### In-App debug log
|
||||
|
||||
This is useful for visual bugs without application crashes
|
||||
|
||||
````
|
||||
You can long click on the in-app debug log in order to copy it to the clipboard
|
||||
````
|
||||
|
|
27
.github/PULL_REQUEST_TEMPLATE
vendored
Normal file
|
@ -0,0 +1,27 @@
|
|||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<!--
|
||||
Hello, and thanks for contributing!
|
||||
|
||||
Please always do auto-reformat on code before creating a PR.
|
||||
In Android-Studio do a right-click on java->Reformat and check the first two options.
|
||||
|
||||
After creating the PR please wait patiently till somebody from the team has time to give a review.
|
||||
The top-priority requirement for this to get merged is, that building/tests don't fail.
|
||||
If theres an continious integration system integrated in this project, you should see a colored checkmark in the PR window which tells the status.
|
||||
|
||||
## Contributors document
|
||||
Add yourself! When adding your information to the `CONTRIBUTORS.md` file, please use the following format:
|
||||
|
||||
Schema: **[Name](Reference)**<br/>~° Text
|
||||
Where:
|
||||
* Name: username, first/lastname
|
||||
* Reference: E-Mail, Webpage
|
||||
* Text: Information about / kind of contribution
|
||||
Example:
|
||||
* **[Nice Guy](http://niceguy.web)**<br/>~° German localization
|
||||
-->
|
68
.github/workflows/build-android-project.yml
vendored
Normal file
|
@ -0,0 +1,68 @@
|
|||
##############################################################################################################################
|
||||
# # Cleanup:
|
||||
#const sleep = ms => () => new Promise((resolve, reject) => window.setTimeout(resolve, ms));
|
||||
#Promise.resolve()
|
||||
#.then(() => { document.getElementsByClassName("details-overlay details-reset position-relative d-inline-block ")[3].children[0].click(); })
|
||||
#.then(sleep(500))
|
||||
#.then(() => { document.getElementsByClassName("dropdown-item btn-link menu-item-danger")[0].click(); })
|
||||
#.then(sleep(1000))
|
||||
#.then(() => { document.getElementsByClassName("btn btn-block btn-danger")[0].click();});
|
||||
#
|
||||
# while [ 1 ] ; do sleep 4; xdotool key Up; sleep 0.1; xdotool key Return; done
|
||||
##############################################################################################################################
|
||||
|
||||
name: "CI"
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
if: "!contains(github.event.head_commit.message, 'ci skip') && (!contains(github.event_name, 'pull_request') || (contains(github.event_name, 'pull_request') && github.event.pull_request.head.repo.full_name != github.repository))"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
|
||||
- name: "Checkout: Code"
|
||||
uses: actions/checkout@v2
|
||||
|
||||
|
||||
- name: "Checkout: Code (PR)"
|
||||
uses: actions/checkout@v2
|
||||
if: "contains(github.event_name, 'pull_request')"
|
||||
with:
|
||||
ref: ${{github.event.pull_request.head.ref}}
|
||||
repository: ${{github.event.pull_request.head.repo.full_name}}
|
||||
|
||||
- name: "Setup: Java"
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 1.8
|
||||
|
||||
- name: "Cache: Gradle"
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
~/.gradle
|
||||
.gradle
|
||||
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('gradle/wrapper/gradle-wrapper.*') }}
|
||||
|
||||
- name: "Build: Project with make"
|
||||
run: make clean all
|
||||
|
||||
- name: "Build: List dist files"
|
||||
if: always()
|
||||
run: find dist -type f -maxdepth 2
|
||||
|
||||
- name: "Artifacts: All"
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v2.2.1
|
||||
with:
|
||||
name: "all"
|
||||
path: dist
|
||||
retention-days: 5
|
||||
|
||||
- name: "Artifacts: Android APK"
|
||||
uses: actions/upload-artifact@v2.2.1
|
||||
with:
|
||||
name: "android-apk"
|
||||
path: |
|
||||
dist/*.apk
|
2
.gitignore
vendored
|
@ -36,6 +36,7 @@ tmp/
|
|||
### Gradle ###
|
||||
.gradle
|
||||
build/
|
||||
dist/
|
||||
gradle-app.setting
|
||||
|
||||
# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
|
||||
|
@ -96,6 +97,7 @@ app/src/main/res/raw/changelog.*
|
|||
app/src/main/res/raw/license.*
|
||||
app/src/main/res/raw/readme.*
|
||||
app/src/main/res/raw/contributors.*
|
||||
app/flavor*
|
||||
|
||||
# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
|
||||
!gradle-wrapper.jar
|
||||
|
|
48
.travis.yml
|
@ -1,48 +0,0 @@
|
|||
language: android
|
||||
jdk: oraclejdk8
|
||||
|
||||
before_cache:
|
||||
# Do not cache a few Gradle files/directories (see https://docs.travis-ci.com/user/languages/java/#Caching)
|
||||
- rm -f $HOME/.gradle/caches/modules-2/modules-2.lock
|
||||
- rm -fr $HOME/.gradle/caches/*/plugin-resolution/
|
||||
|
||||
cache:
|
||||
directories:
|
||||
# Android SDK
|
||||
- $HOME/android-sdk-dl
|
||||
- $HOME/android-sdk
|
||||
|
||||
# Gradle dependencies
|
||||
- $HOME/.gradle/caches/
|
||||
- $HOME/.gradle/wrapper/
|
||||
|
||||
# Android build cache (see http://tools.android.com/tech-docs/build-cache)
|
||||
- $HOME/.android/build-cache
|
||||
|
||||
install:
|
||||
# Download and unzip the Android SDK tools (if not already there thanks to the cache mechanism)
|
||||
# Latest version available here: https://developer.android.com/studio/index.html#downloads
|
||||
- if test ! -e $HOME/android-sdk-dl/sdk-tools.zip ; then curl https://dl.google.com/android/repository/sdk-tools-linux-3859397.zip > $HOME/android-sdk-dl/sdk-tools.zip ; fi
|
||||
- unzip -qq -n $HOME/android-sdk-dl/sdk-tools.zip -d $HOME/android-sdk
|
||||
|
||||
# Install or update Android SDK components (will not do anything if already up to date thanks to the cache mechanism)
|
||||
- echo y | $HOME/android-sdk/tools/bin/sdkmanager 'tools' > /dev/null
|
||||
- echo y | $HOME/android-sdk/tools/bin/sdkmanager 'platform-tools' > /dev/null
|
||||
- echo y | $HOME/android-sdk/tools/bin/sdkmanager 'build-tools;26.0.2' > /dev/null
|
||||
- echo y | $HOME/android-sdk/tools/bin/sdkmanager 'platforms;android-27' > /dev/null
|
||||
- echo y | $HOME/android-sdk/tools/bin/sdkmanager 'extras;google;m2repository' > /dev/null
|
||||
|
||||
branches:
|
||||
except:
|
||||
- gh-pages
|
||||
- l10n_master
|
||||
- crowdin
|
||||
|
||||
env:
|
||||
global:
|
||||
- ANDROID_HOME=$HOME/android-sdk
|
||||
matrix:
|
||||
- TASK="clean lintFlavorDefaultDebug --stacktrace"
|
||||
- TASK="clean build check -x lint --stacktrace"
|
||||
|
||||
script: "./gradlew --no-daemon --parallel $TASK"
|
49
CHANGELOG.md
|
@ -1,3 +1,52 @@
|
|||
### Recent changes
|
||||
- See [Discussions](https://github.com/gsantner/dandelion/discussions), [Issues](https://github.com/gsantner/dandelion/issues) and [Project page](https://github.com/gsantner/dandelion#readme) to see what is going on.
|
||||
|
||||
### v1.4.0
|
||||
- Add seconds to 'save picture' date format
|
||||
- Updated translations
|
||||
- Added german F-Droid description translation
|
||||
- Update to Android SDK 29
|
||||
|
||||
### v1.3.0
|
||||
- Add option to open youtube links external/in YouTube app (optional)
|
||||
- Pull to refresh
|
||||
|
||||
### v1.2.3
|
||||
**Improved:**
|
||||
- More supported languages, more complete translations!
|
||||
|
||||
### v1.2.1
|
||||
**App release: dandelior**
|
||||
- Added an (rebranded) flavor of dandelion: dandelior
|
||||
- Only differenties in use are other (black) icon and AMOLED colors by default enabled
|
||||
- Already available on F-Droid
|
||||
|
||||
**New features:**
|
||||
- All new Aspects and Tags, using a searchable dialog
|
||||
|
||||
**Fixed:**
|
||||
- Sometimes the Stream went white, which is due an still (>3 years) unfixed Android Support library bug. It should not occur very often anymore due less use of fragments.
|
||||
|
||||
**Improved:**
|
||||
- Various small tweaks
|
||||
- Updated translation files
|
||||
|
||||
### v1.1.3
|
||||
- Improve sharing *a lot*, add support for multiple filetypes
|
||||
- Support for downloading GIFs ;)
|
||||
- Rework screenshot saving and sharing; add new share options:
|
||||
- Merge license and changelog dialog on first start
|
||||
|
||||
### v1.1.2
|
||||
- Fix: loading non-pod links outside customtab/external browser
|
||||
- Fix: webview-js dialog not dismissing correctly
|
||||
|
||||
### v1.1.0
|
||||
- Added: App shortcuts (Android 7+)
|
||||
- Updated: podlist
|
||||
- More supported languages
|
||||
- File sharing fixes
|
||||
|
||||
### v1.0.8
|
||||
- Modified: Navigation - Merge bottom toolbar into top
|
||||
- Updated: Build for Android O/27
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
<!--
|
||||
This file contains references to people who contributed to the app.
|
||||
If you helped by translating the app, please send a message on Crowdin.
|
||||
You can also send a mail to [gsantner AT mailbox DOT org](https://gsantner.github.io#contact) to get included.
|
||||
|
||||
Schema: **[Name](Reference)**<br/>~° Text
|
||||
|
||||
|
@ -10,11 +8,11 @@ Where:
|
|||
* Reference: E-Mail, Webpage
|
||||
* Text: Information about / kind of contribution
|
||||
|
||||
|
||||
|
||||
|
||||
## LIST OF CONTRIBUTORS
|
||||
-->
|
||||
* **[Gregor Santner](https://gsantner.github.io)**<br/>~° Current developer of dandelion
|
||||
* **[Gregor Santner](http://github.com/gsantner)**<br/>~° Development of dandelion
|
||||
* **[Paul Schaub](https://github.com/vanitasvitae)**<br/>~° Development of dandelion
|
||||
* **[Martín Vukovic](martinvukovic AT protonmail DOT com)**<br/>~° Diaspora Native WebApp
|
||||
* **[Gaukler Faun](https://github.com/scoute-dich)**<br/>~° Diaspora Native WebApp additions
|
||||
|
@ -29,4 +27,6 @@ Where:
|
|||
* **[Âng Iōngchun](https://pubpod.alqualonde.org/u/iongchun)**<br/>~° Chinese traditional translation
|
||||
* **[Mikkel Kirkgaard Nielsen](http://www.mikini.dk)**<br/>~° Danish translation
|
||||
* **[Jean Lucas](jean AT 4ray DOT co)**<br/>~° Spanish translation
|
||||
* **[asereze](https://github.com/asereze)**<br/>~° Sardinian translation
|
||||
* **[asereze](https://github.com/asereze)**<br/>~° Sardinian translation
|
||||
* **[Xosé M. Lamas](http://xmgz.eu)**<br />~° Galician translation
|
||||
* **[massimiliano](https://framagit.org/massimiliano)**<br />~° Contributor
|
||||
|
|
99
Makefile
Normal file
|
@ -0,0 +1,99 @@
|
|||
# License of Makefile: Public Domain / CC0
|
||||
.PHONY: $(shell sed -n -e '/^$$/ { n ; /^[^ .\#][^ ]*:/ { s/:.*$$// ; p ; } ; }' $(MAKEFILE_LIST))
|
||||
.NOTPARALLEL: clean
|
||||
.DEFAULT_GOAL := all
|
||||
|
||||
env-%:
|
||||
@: $(if ${${*}},,$(error Environment variable $* not set))
|
||||
####################################################################################
|
||||
|
||||
DIST_DIR = dist
|
||||
MOVE = mv
|
||||
|
||||
all: $(DIST_DIR) spellcheck lint deptree test build aapt_dump_badging
|
||||
|
||||
####################################################################################
|
||||
|
||||
$(DIST_DIR):
|
||||
mkdir -p ${DIST_DIR}
|
||||
|
||||
ANDROID_BUILD_TOOLS := $(shell test -n "$ANDROID_SDK_ROOT" && find "${ANDROID_SDK_ROOT}/build-tools" -iname "aapt" | sort -r | head -n1 | xargs dirname)
|
||||
TOOL_SPELLCHECKING_ISPELL := $(shell command -v ispell 2> /dev/null)
|
||||
|
||||
FLAVOR := $(or ${FLAVOR},${FLAVOR},Atest)
|
||||
|
||||
.NOTPARALLEL: gradle gradle-analyze-log
|
||||
gradle: env-ANDROID_SDK_ROOT
|
||||
mkdir -p $(DIST_DIR)/log/
|
||||
chmod +x gradlew
|
||||
./gradlew --no-daemon --parallel --stacktrace $A 2>&1 | tee "$(DIST_DIR)/log/gradle.log"
|
||||
@echo "-----------------------------------------------------------------------------------"
|
||||
|
||||
gradle-analyze-log:
|
||||
mv "$(DIST_DIR)/log/gradle.log" "$(DIST_DIR)/log/gradle$A.log"
|
||||
cat "$(DIST_DIR)/log/gradle$A.log" | grep "BUILD " | tail -n1 | grep -q "BUILD SUCCESSFUL in"
|
||||
|
||||
adb: env-ANDROID_SDK_ROOT
|
||||
"${ANDROID_SDK_ROOT}/platform-tools/adb" $A 2>&1 | tee "$(DIST_DIR)/log/adb-$L.log"
|
||||
|
||||
aapt: env-ANDROID_SDK_ROOT
|
||||
"${ANDROID_BUILD_TOOLS}/aapt" $A 2>&1 | grep -v 'application-label-' | tee "$(DIST_DIR)/log/aapt$L.log"
|
||||
|
||||
build:
|
||||
rm -f $(DIST_DIR)/*.apk
|
||||
$(MAKE) A="clean assembleFlavor$(FLAVOR) -x lint" gradle
|
||||
find app -type f -newermt '-300 seconds' -iname '*.apk' -not -iname '*unsigned.apk' | xargs cp -R -t $(DIST_DIR)/
|
||||
$(MAKE) A="-build" gradle-analyze-log
|
||||
|
||||
lint:
|
||||
rm -Rf $(DIST_DIR)/lint
|
||||
mkdir -p $(DIST_DIR)/lint/
|
||||
$(MAKE) A="lintFlavorDefaultDebug" gradle
|
||||
find app -type f -iname 'lint-results-*' | grep -v 'intermediates' | xargs cp -R -t $(DIST_DIR)/lint
|
||||
$(MAKE) A="-lint" gradle-analyze-log
|
||||
|
||||
test:
|
||||
rm -Rf $(DIST_DIR)/tests
|
||||
$(MAKE) A="testFlavorDefaultDebugUnitTest -x lint" gradle
|
||||
mkdir -p app/build/test-results/testFlavorDefaultDebugUnitTest && echo 'PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHRlc3RzdWl0ZSBuYW1lPSJkdW1teSIgdGVzdHM9IjEiIHNraXBwZWQ9IjAiIGZhaWx1cmVzPSIwIiBlcnJvcnM9IjAiIHRpbWVzdGFtcD0iMjAyMC0xMi0wOFQwMDowMDowMCIgaG9zdG5hbWU9ImxvY2FsaG9zdCIgdGltZT0iMC4wMSI+CiAgPHByb3BlcnRpZXMvPgogIDx0ZXN0Y2FzZSBuYW1lPSJkdW1teSIgY2xhc3NuYW1lPSJkdW1teSIgdGltZT0iMC4wMSIvPgogIDxzeXN0ZW0tb3V0PjwhW0NEQVRBW11dPjwvc3lzdGVtLW91dD4KICA8c3lzdGVtLWVycj48IVtDREFUQVtdXT48L3N5c3RlbS1lcnI+CjwvdGVzdHN1aXRlPgo=' | base64 -d > 'app/build/test-results/testFlavorDefaultDebugUnitTest/TEST-dummy.xml'
|
||||
find app -type d -iname 'testFlavorDefaultDebugUnitTest' | xargs cp -R -t $(DIST_DIR)/
|
||||
mv ${DIST_DIR}/testFlavorDefaultDebugUnitTest $(DIST_DIR)/tests
|
||||
$(MAKE) A="-test" gradle-analyze-log
|
||||
|
||||
deptree:
|
||||
$(MAKE) A="app:dependencies --configuration flavor$(FLAVOR)DebugRuntimeClasspath" gradle
|
||||
$(MAKE) A="-dependency-tree" gradle-analyze-log
|
||||
|
||||
clean:
|
||||
$(MAKE) A="clean" gradle
|
||||
rm -Rf $(DIST_DIR) app/build app/flavor* .idea dist
|
||||
find . -type f -iname "*.iml" -delete
|
||||
$(MAKE) $(DIST_DIR)
|
||||
@echo "-----------------------------------------------------------------------------------"
|
||||
|
||||
install:
|
||||
$(MAKE) A="install -r $(DIST_DIR)/*.apk" L="install" adb
|
||||
|
||||
run:
|
||||
$(MAKE) A="shell monkey -p $$(aapt dump badging $(DIST_DIR)/*.apk | grep package: | sed 's@.* name=@@' | sed 's@ .*@@' | xargs | head -n1) -c android.intent.category.LAUNCHER 1" L="run" adb
|
||||
|
||||
aapt_dump_badging:
|
||||
$(MAKE) A="dump badging $(DIST_DIR)/*.apk" aapt
|
||||
@echo "-----------------------------------------------------------------------------------"
|
||||
|
||||
spellcheck:
|
||||
mkdir -p "$(DIST_DIR)/lint/"
|
||||
ifndef TOOL_SPELLCHECKING_ISPELL
|
||||
@echo "Tool ispell (spellcheck) not found in PATH. Spellcheck skipped." > "$(DIST_DIR)/lint/stringsxml-spellcheck.txt"
|
||||
else
|
||||
@echo "Use ispell for spellchecking the original values/strings.xml"
|
||||
find . -iname "strings.xml" -path "*/main*/values/*" | head -n1 | xargs cat \
|
||||
| grep "<string name=" | sed 's@.*">@@' | sed 's@</string>@@' | sed 's@\\n@ @g' | sed 's@\\@@g' \
|
||||
| ispell -W3 -a | grep ^\& | sed 's@[0-9]@@g' | sort | uniq | cut -d, -f1-4 \
|
||||
| sed 's@^..@- @' | column -t -s: \
|
||||
> "$(DIST_DIR)/lint/stringsxml-spellcheck.txt"
|
||||
@echo "\nPotential words with bad spelling:"
|
||||
endif
|
||||
@cat "$(DIST_DIR)/lint/stringsxml-spellcheck.txt"
|
||||
@echo "-----------------------------------------------------------------------------------"
|
||||
|
96
NEWS.md
Normal file
|
@ -0,0 +1,96 @@
|
|||
# dandelion - News
|
||||
|
||||
## General
|
||||
|
||||
### Installation
|
||||
You can install and update from [F-Droid](https://f-droid.org/repository/browse/?fdid=com.github.dfa.diaspora_android) or [GitHub](https://github.com/gsantner/dandelion/releases/latest).
|
||||
|
||||
F-Droid is a store for free & open source apps.
|
||||
The *.apk's available for download are signed by the F-Droid team and guaranteed to correspond to the (open source) source code of dandelion.
|
||||
Generally this is the recommended way to install dandelion & keep it updated.
|
||||
|
||||
|
||||
### Get informed
|
||||
* Check the [project readme](https://github.com/gsantner/dandelion/tree/news#readme) for general project information.
|
||||
* Check the [project news](https://github.com/gsantner/dandelion/blob/master/NEWS.md#readme) for more details on what is going on.
|
||||
* Check the [project git history](https://github.com/gsantner/dandelion/commits/master) for most recent code changes.
|
||||
|
||||
### The right place to ask
|
||||
If you have questions or found an issue please head to the [dandelion project](https://github.com/gsantner/dandelion/issues/new/choose) and ask there.
|
||||
[Search](https://github.com/gsantner/dandelion/issues?q=#js-issues-search) for same/similar and related issues/questions before, it might be already answered or resolved.
|
||||
|
||||
|
||||
### Navigation
|
||||
* [dandelion v1.2 - Add dandelior - Searchable Tags and Aspects](#dandelion-v12---add-dandelior---searchable-tags-and-aspects)
|
||||
* [dandelion v0.1.2 - Aspekte, Pod wechseln](#dandelion-v012---aspekte-pod-wechseln)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
# dandelion\* v1.2 - Add dandelior\* - Searchable Tags and Aspects
|
||||
_12. August 2018_
|
||||
|
||||
## dandelior\* is a rebranded version of dandelion\*
|
||||
dandelior\* is based 100% on the same code and resources as dandelion\*. Its from the same code repository, just a different build flavor.
|
||||
The main purpose of dandelior\* is the most requested feature till date - to support multiple accounts / another account at dandelion\*.
|
||||
|
||||
- Added an (rebranded) flavor of dandelion: dandelior
|
||||
- Only differenties in use are other (black) icon and AMOLED colors by default enabled
|
||||
- Already available on F-Droid
|
||||
|
||||
**New features:**
|
||||
- All new Aspects and Tags, using a searchable dialog
|
||||
|
||||
**Fixed:**
|
||||
- Sometimes the Stream went white, which is due an still (3+ years) unfixed Android Support library bug. It should not occur very often anymore due less use of fragments.
|
||||
|
||||
**Improved:**
|
||||
- Various small tweaks
|
||||
- Updated translation files
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
# dandelion v0.1.2 - Aspekte, Pod wechseln
|
||||
_05. Juni 2016_
|
||||
|
||||
In den letzten Tagen hat @gsantner viel Zeit in die inoffizielle diaspora\* Android App ([dandelion\*](https://github.com/gsantner/dandelion)) investiert.
|
||||
|
||||
Dabei wurden unter anderem folgende Änderungen beigesteuert:
|
||||
|
||||
- Allgemeines zur Usability
|
||||
- Animationen für den Activity-Wechsel und Startup, WebView-Scroll-Top
|
||||
- Podliste caching
|
||||
- Aspekt-Liste und Aspekte hinzugefügt
|
||||
- Verbessertes Sharing aus der App
|
||||
- Material Progressbar
|
||||
- Suche verbessert
|
||||
- Collapsing top menu
|
||||
- toolbar/actions/menu geändert, fab entfernt
|
||||
- Refactoring layout & menu files, dialogs
|
||||
- Überarbeitete Main,Splash,PodSelectionActivity
|
||||
- Pod wechseln
|
44
README.md
|
@ -1,10 +1,9 @@
|
|||
[](https://github.com/diaspora-for-android/dandelion/releases)
|
||||
[](https://travis-ci.org/Diaspora-for-Android/dandelion)
|
||||
[](https://crowdin.com/project/diaspora-for-android)
|
||||
[](https://matrix.to/#/#dandelion:matrix.org) [](https://kiwiirc.com/client/irc.freenode.net/?nick=dandelion-anon|?##dandelion)
|
||||
[](https://gsantner.github.io/#donate)
|
||||
[](https://liberapay.com/gsantner/donate)
|
||||
[](https://gratipay.com/dandelion/)
|
||||
[](https://github.com/gsantner/dandelion/releases)
|
||||
[](https://github.com/gsantner/dandelion/releases)
|
||||
[](https://crowdin.com/project/diaspora-for-android/invite)
|
||||
[](https://matrix.to/#/#dandelion:matrix.org)
|
||||
[](https://github.com/gsantner/dandelion/actions)
|
||||
[](https://www.codacy.com/app/gsantner/dandelion)
|
||||
|
||||
# dandelion\*
|
||||
<img src="/app/src/main/ic_launcher-web.png" align="left" width="100" hspace="10" vspace="10">
|
||||
|
@ -42,44 +41,41 @@ dandelion\* requires access to the Internet and to external storage to be able t
|
|||
The project is always open for contributions and accepts pull requests.
|
||||
The project uses [AOSP Java Code Style](https://source.android.com/source/code-style#follow-field-naming-conventions), with one exception: private members are `_camelCase` instead of `mBigCamel`. You may use Android Studios _auto reformat feature_ before sending a PR.
|
||||
|
||||
We use Crowdin to translate dandelion\*. Join our project here: <https://crowdin.com/project/diaspora-for-android/invite>. If your desired language is not listed please contact the maintainers/owner.
|
||||
|
||||
Join our IRC or Matrix channel (bridged) and say hello! Don't be afraid to start talking. [](https://matrix.to/#/#dandelion:matrix.org) [](https://kiwiirc.com/client/irc.freenode.net/?nick=dandelion-anon|?##dandelion)
|
||||
Translations can be contributed on GitHub. You can use Stringlate ([](https://lonamiwebs.github.io/stringlate/translate?git=https%3A%2F%2Fgithub.com%2Fgsantner%2Fdandelion.git)) to translate the project directly on your Android phone. It allows you to export as E-Mail attachement and to post on GitHub.
|
||||
|
||||
Join our Matrix channel and say hello! Don't be afraid to start talking. [](https://matrix.to/#/#dandelion:matrix.org)
|
||||
Note that the main project members are working on this project for free during leisure time, are mostly busy with their job/university/school, and may not react or start coding immediately.
|
||||
|
||||
|
||||
#### Resources
|
||||
* Project: [Changelog](/CHANGELOG.md) | [Issues level/beginner](https://github.com/diaspora-for-android/dandelion/issues?q=is%3Aissue+is%3Aopen+label%3Alevel%2Fbeginner) | [License](/LICENSE.txt) | [CoC](/CODE_OF_CONDUCT.md)
|
||||
* Project: [Changelog](/CHANGELOG.md) | [Issues level/beginner](https://github.com/gsantner/dandelion/issues?q=is%3Aissue+is%3Aopen+label%3Alevel%2Fbeginner) | [License](/LICENSE.txt) | [CoC](/CODE_OF_CONDUCT.md)
|
||||
* Project diaspora\* account: [dandelion00@diasp.org](https://diasp.org/people/48b78420923501341ef3782bcb452bd5)
|
||||
* diaspora\*: [GitHub](https://github.com/diaspora/diaspora) | [Web](https://diasporafoundation.org) | [d\* HQ account](https://pod.diaspora.software/people/7bca7c80311b01332d046c626dd55703)
|
||||
* App on F-Droid: [Metadata](https://gitlab.com/fdroid/fdroiddata/blob/master/metadata/com.github.dfa.diaspora_android.txt) | [Page](https://f-droid.org/packages/com.github.dfa.diaspora_android/) | [Wiki](https://f-droid.org/wiki/page/com.github.dfa.diaspora_android) | [Build log](https://f-droid.org/wiki/page/com.github.dfa.diaspora_android/lastbuild)
|
||||
|
||||
|
||||
## Licensing
|
||||
dandelion\* is released under GNU GENERAL PUBLIC LICENSE (see [LICENCE](https://github.com/Diaspora-for-Android/dandelion/blob/master/LICENSE.md)).
|
||||
dandelion\* is released under GNU GENERAL PUBLIC LICENSE (see [LICENCE](https://github.com/gsantner/dandelion/blob/master/LICENSE.md)).
|
||||
The app is licensed GPL v3. Localization files and resources (strings\*.xml) are licensed CC0 1.0.
|
||||
For more licensing informations, see [`3rd party licenses`](/app/src/main/res/raw/licenses_3rd_party.md).
|
||||
|
||||
## Screenshots
|
||||
<div style="display:flex;" >
|
||||
<img src="https://raw.githubusercontent.com/diaspora-for-android/dandelion-metadata-latest/master/en-US/phoneScreenshots/01.png" width="19%" >
|
||||
<img src="https://raw.githubusercontent.com/diaspora-for-android/dandelion-metadata-latest/master/en-US/phoneScreenshots/02.png" width="19%" style="margin-left:10px;" >
|
||||
<img src="https://raw.githubusercontent.com/diaspora-for-android/dandelion-metadata-latest/master/en-US/phoneScreenshots/03.png" width="19%" style="margin-left:10px;" >
|
||||
<img src="https://raw.githubusercontent.com/diaspora-for-android/dandelion-metadata-latest/master/en-US/phoneScreenshots/04.png" width="19%" style="margin-left:10px;" >
|
||||
<img src="https://raw.githubusercontent.com/diaspora-for-android/dandelion-metadata-latest/master/en-US/phoneScreenshots/05.png" width="19%" style="margin-left:10px;" >
|
||||
<img src="https://raw.githubusercontent.com/gsantner/dandelion/master/metadata/en-US/phoneScreenshots/01.png" width="19%" >
|
||||
<img src="https://raw.githubusercontent.com/gsantner/dandelion/master/metadata/en-US/phoneScreenshots/02.png" width="19%" style="margin-left:10px;" >
|
||||
<img src="https://raw.githubusercontent.com/gsantner/dandelion/master/metadata/en-US/phoneScreenshots/03.png" width="19%" style="margin-left:10px;" >
|
||||
<img src="https://raw.githubusercontent.com/gsantner/dandelion/master/metadata/en-US/phoneScreenshots/04.png" width="19%" style="margin-left:10px;" >
|
||||
<img src="https://raw.githubusercontent.com/gsantner/dandelion/master/metadata/en-US/phoneScreenshots/05.png" width="19%" style="margin-left:10px;" >
|
||||
</div>
|
||||
|
||||
<div style="display:flex;" >
|
||||
<img src="https://raw.githubusercontent.com/diaspora-for-android/dandelion-metadata-latest/master/en-US/phoneScreenshots/06.png" width="19%" >
|
||||
<img src="https://raw.githubusercontent.com/diaspora-for-android/dandelion-metadata-latest/master/en-US/phoneScreenshots/07.png" width="19%" style="margin-left:10px;" >
|
||||
<img src="https://raw.githubusercontent.com/diaspora-for-android/dandelion-metadata-latest/master/en-US/phoneScreenshots/08.png" width="19%" style="margin-left:10px;" >
|
||||
<img src="https://raw.githubusercontent.com/diaspora-for-android/dandelion-metadata-latest/master/en-US/phoneScreenshots/09.png" width="19%" style="margin-left:10px;" >
|
||||
<img src="https://raw.githubusercontent.com/gsantner/dandelion/master/metadata/en-US/phoneScreenshots/06.png" width="19%" >
|
||||
<img src="https://raw.githubusercontent.com/gsantner/dandelion/master/metadata/en-US/phoneScreenshots/07.png" width="19%" style="margin-left:10px;" >
|
||||
<img src="https://raw.githubusercontent.com/gsantner/dandelion/master/metadata/en-US/phoneScreenshots/08.png" width="19%" style="margin-left:10px;" >
|
||||
<img src="https://raw.githubusercontent.com/gsantner/dandelion/master/metadata/en-US/phoneScreenshots/09.png" width="19%" style="margin-left:10px;" >
|
||||
</div>
|
||||
|
||||
### Notice
|
||||
#### Maintainers
|
||||
- gsantner ([GitHub](https://github.com/gsantner), [Web](https://gsantner.github.io), [diaspora*](https://pod.geraspora.de/people/d1cbdd70095301341e834860008dbc6c))
|
||||
- Bitcoin: [1B9ZyYdQoY9BxMe9dRUEKaZbJWsbQqfXU5](https://gsantner.github.io/donate/#donate)
|
||||
- gsantner ([GitHub](https://github.com/gsantner), [diaspora*](https://pod.geraspora.de/people/d1cbdd70095301341e834860008dbc6c))
|
||||
- vanitasvitae ([GitHub](https://github.com/vanitasvitae), [diaspora*](https://pod.geraspora.de/people/bbd7af90fbec013213e34860008dbc6c))
|
||||
- Bitcoin: 1Ao3W6NaQv3xKppviB7RSFKjHo6PGd8RTy
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
## Screenshots
|
||||
|
||||
<div style="display:flex;" >
|
||||
<img src="https://raw.githubusercontent.com/diaspora-for-android/dandelion-metadata-latest/master/en-US/phoneScreenshots/01.png" width="19%" >
|
||||
<img src="https://raw.githubusercontent.com/diaspora-for-android/dandelion-metadata-latest/master/en-US/phoneScreenshots/02.png" width="19%" style="margin-left:10px;" >
|
||||
<img src="https://raw.githubusercontent.com/diaspora-for-android/dandelion-metadata-latest/master/en-US/phoneScreenshots/03.png" width="19%" style="margin-left:10px;" >
|
||||
<img src="https://raw.githubusercontent.com/diaspora-for-android/dandelion-metadata-latest/master/en-US/phoneScreenshots/04.png" width="19%" style="margin-left:10px;" >
|
||||
<img src="https://raw.githubusercontent.com/diaspora-for-android/dandelion-metadata-latest/master/en-US/phoneScreenshots/05.png" width="19%" style="margin-left:10px;" >
|
||||
</div>
|
||||
|
||||
<div style="display:flex;" >
|
||||
<img src="https://raw.githubusercontent.com/diaspora-for-android/dandelion-metadata-latest/master/en-US/phoneScreenshots/06.png" width="19%" >
|
||||
<img src="https://raw.githubusercontent.com/diaspora-for-android/dandelion-metadata-latest/master/en-US/phoneScreenshots/07.png" width="19%" style="margin-left:10px;" >
|
||||
<img src="https://raw.githubusercontent.com/diaspora-for-android/dandelion-metadata-latest/master/en-US/phoneScreenshots/08.png" width="19%" style="margin-left:10px;" >
|
||||
<img src="https://raw.githubusercontent.com/diaspora-for-android/dandelion-metadata-latest/master/en-US/phoneScreenshots/09.png" width="19%" style="margin-left:10px;" >
|
||||
</div>
|
150
app/build.gradle
|
@ -1,43 +1,61 @@
|
|||
apply plugin: 'com.android.application'
|
||||
|
||||
ext {
|
||||
version_sdk = [
|
||||
compileSdk: 27,
|
||||
minSdk : 17,
|
||||
]
|
||||
version_lib = [
|
||||
appcompat : "27.0.0",
|
||||
butterknife: "8.8.1",
|
||||
netcipher : "2.0.0-alpha1",
|
||||
]
|
||||
if (enable_plugin_kotlin) {
|
||||
apply plugin: 'kotlin-android'
|
||||
apply plugin: 'kotlin-android-extensions'
|
||||
apply plugin: 'kotlin-kapt'
|
||||
}
|
||||
|
||||
|
||||
android {
|
||||
compileSdkVersion version_sdk.compileSdk
|
||||
flavorDimensions "default"
|
||||
buildToolsVersion rootProject.ext.version_buildTools
|
||||
compileSdkVersion rootProject.ext.version_compileSdk
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion version_sdk.minSdk
|
||||
targetSdkVersion version_sdk.compileSdk
|
||||
resValue "string", "manifest_package_id", "com.github.dfa.diaspora_android"
|
||||
applicationId "com.github.dfa.diaspora_android"
|
||||
versionName "1.3.5"
|
||||
versionCode 46
|
||||
vectorDrawables.useSupportLibrary = true
|
||||
|
||||
minSdkVersion rootProject.ext.version_minSdk
|
||||
targetSdkVersion rootProject.ext.version_compileSdk
|
||||
buildConfigField "boolean", "IS_TEST_BUILD", "false"
|
||||
buildConfigField "boolean", "IS_GPLAY_BUILD", "false"
|
||||
buildConfigField("String[]", "APPLICATION_LANGUAGES", '{' + getUsedAndroidLanguages().collect {"\"${it}\""}.join(",") + '}')
|
||||
buildConfigField "String[]", "DETECTED_ANDROID_LOCALES", "${findUsedAndroidLocales()}"
|
||||
buildConfigField "String", "BUILD_DATE", "\"${getBuildDate()}\""
|
||||
buildConfigField "String", "GITHASH", "\"${getGitHash()}\""
|
||||
|
||||
applicationId "com.github.dfa.diaspora_android"
|
||||
versionName "1.0.8"
|
||||
versionCode 28
|
||||
|
||||
|
||||
vectorDrawables.useSupportLibrary = true
|
||||
resValue 'string', 'app_name', "dandelion*"
|
||||
manifestPlaceholders = [appIcon: "@drawable/ic_launcher"]
|
||||
setProperty("archivesBaseName", applicationId + "-v" + versionCode + "-" + versionName)
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
flavorDimensions "default"
|
||||
productFlavors {
|
||||
flavorDefault {
|
||||
}
|
||||
|
||||
/*
|
||||
flavorGplay {
|
||||
buildConfigField "boolean", "IS_GPLAY_BUILD", "true"
|
||||
}*/
|
||||
|
||||
flavorDandelior {
|
||||
applicationId "net.gsantner.dandelior"
|
||||
}
|
||||
|
||||
flavorAtest {
|
||||
applicationId "net.gsantner.secondlion"
|
||||
versionCode = Integer.parseInt(new Date().format('yyMMdd'))
|
||||
versionName = new Date().format('yyMMdd')
|
||||
buildConfigField "boolean", "IS_TEST_BUILD", "true"
|
||||
}
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
main { assets.srcDirs = ['src/main/assets'] }
|
||||
if (enable_plugin_kotlin) {
|
||||
main.java.srcDirs += 'src/main/kotlin'
|
||||
}
|
||||
main.java.srcDirs += 'thirdparty/java'
|
||||
main.res.srcDirs += 'thirdparty/res'
|
||||
main.assets.srcDirs += 'thirdparty/assets'
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
|
@ -47,25 +65,33 @@ android {
|
|||
}
|
||||
}
|
||||
|
||||
productFlavors {
|
||||
flavorDefault {
|
||||
}
|
||||
|
||||
/*
|
||||
flavorGplay {
|
||||
buildConfigField "boolean", "IS_GPLAY_BUILD", "true"
|
||||
}*/
|
||||
flavorTest {
|
||||
applicationId "com.github.dfa.secondlion"
|
||||
resValue 'string', 'app_name', "secondlion*"
|
||||
manifestPlaceholders = [appIcon: "@drawable/ic_launcher_test"]
|
||||
versionCode = Integer.parseInt(new Date().format('yyMMdd'))
|
||||
versionName = new Date().format('yyMMdd')
|
||||
buildConfigField "boolean", "IS_TEST_BUILD", "true"
|
||||
configurations.all {
|
||||
resolutionStrategy {
|
||||
eachDependency { details ->
|
||||
if (details.requested.group == 'com.android.support') {
|
||||
if (details.requested.name != 'multidex' && details.requested.name != 'multidex-instrumentation') {
|
||||
details.useVersion "${rootProject.ext.version_library_appcompat}"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
packagingOptions {
|
||||
exclude 'META-INF/LICENSE-LGPL-2.1.txt'
|
||||
exclude 'META-INF/LICENSE-LGPL-3.txt'
|
||||
exclude 'META-INF/LICENSE-W3C-TEST'
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
encoding = 'UTF-8'
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
lintOptions {
|
||||
disable 'MissingTranslation'
|
||||
disable 'MissingTranslation', 'InvalidPackage', 'ObsoleteLintCustomCheck', 'DefaultLocale', 'UnusedAttribute', 'VectorRaster', 'InflateParams', 'IconLocation', 'UnusedResources', 'TypographyEllipsis'
|
||||
abortOnError false
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -75,22 +101,36 @@ dependencies {
|
|||
|
||||
// Jars
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||
testImplementation 'junit:junit:4.12'
|
||||
testImplementation 'junit:junit:4.13'
|
||||
|
||||
// Android standard libs
|
||||
implementation "com.android.support:appcompat-v7:${version_lib.appcompat}"
|
||||
implementation "com.android.support:design:${version_lib.appcompat}"
|
||||
implementation "com.android.support:support-v4:${version_lib.appcompat}"
|
||||
implementation "com.android.support:customtabs:${version_lib.appcompat}"
|
||||
implementation "com.android.support:cardview-v7:${version_lib.appcompat}"
|
||||
implementation "com.android.support:appcompat-v7:${version_library_appcompat}"
|
||||
implementation "com.android.support:design:${version_library_appcompat}"
|
||||
implementation "com.android.support:support-v4:${version_library_appcompat}"
|
||||
implementation "com.android.support:customtabs:${version_library_appcompat}"
|
||||
implementation "com.android.support:cardview-v7:${version_library_appcompat}"
|
||||
implementation "com.android.support:preference-v7:${version_library_appcompat}"
|
||||
|
||||
// UI libraries
|
||||
implementation "com.github.DASAR:ShiftColorPicker:v0.5"
|
||||
|
||||
// Tool libraries
|
||||
implementation "com.jakewharton:butterknife:${version_lib.butterknife}"
|
||||
implementation "info.guardianproject.netcipher:netcipher:${version_lib.netcipher}"
|
||||
implementation "info.guardianproject.netcipher:netcipher-webkit:${version_lib.netcipher}"
|
||||
implementation 'commons-io:commons-io:2.6'
|
||||
implementation "info.guardianproject.netcipher:netcipher:${version_library_netcipher}"
|
||||
implementation "info.guardianproject.netcipher:netcipher-webkit:${version_library_netcipher}"
|
||||
//noinspection AnnotationProcessorOnCompilePath
|
||||
implementation "com.jakewharton:butterknife:${version_library_butterknife}"
|
||||
if (enable_plugin_kotlin) {
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:${version_plugin_kotlin}"
|
||||
}
|
||||
|
||||
annotationProcessor "com.jakewharton:butterknife-compiler:${version_lib.butterknife}"
|
||||
// Processors
|
||||
def anpros = ["com.jakewharton:butterknife-compiler:${version_library_butterknife}"]
|
||||
for (anpro in anpros) {
|
||||
if (enable_plugin_kotlin) {
|
||||
kapt anpro
|
||||
} else {
|
||||
annotationProcessor anpro
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/ic_launcher_background"/>
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/ic_launcher_background"/>
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
BIN
app/src/flavorAtest/res/drawable-hdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 2.9 KiB |
BIN
app/src/flavorAtest/res/drawable-hdpi/ic_launcher_round.png
Normal file
After Width: | Height: | Size: 2.9 KiB |
BIN
app/src/flavorAtest/res/drawable-ldpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
app/src/flavorAtest/res/drawable-mdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
BIN
app/src/flavorAtest/res/drawable-mdpi/ic_launcher_round.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
BIN
app/src/flavorAtest/res/drawable-xhdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 4.1 KiB |
BIN
app/src/flavorAtest/res/drawable-xhdpi/ic_launcher_round.png
Normal file
After Width: | Height: | Size: 4.1 KiB |
BIN
app/src/flavorAtest/res/drawable-xxhdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 6.3 KiB |
BIN
app/src/flavorAtest/res/drawable-xxhdpi/ic_launcher_round.png
Normal file
After Width: | Height: | Size: 6.3 KiB |
BIN
app/src/flavorAtest/res/drawable-xxxhdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 9.1 KiB |
BIN
app/src/flavorAtest/res/drawable-xxxhdpi/ic_launcher_round.png
Normal file
After Width: | Height: | Size: 9.1 KiB |
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="20"
|
||||
android:viewportHeight="20">
|
||||
<path
|
||||
android:pathData="M0.025,-0.07h19.95v20.14H0.025z"
|
||||
android:fillColor="#492600"/>
|
||||
</vector>
|
12
app/src/flavorAtest/res/drawable/ic_launcher_foreground.xml
Normal file
|
@ -0,0 +1,12 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="36.363636"
|
||||
android:viewportHeight="36.363636">
|
||||
<group android:translateX="8.181818"
|
||||
android:translateY="8.181818">
|
||||
<path
|
||||
android:pathData="M11.337,14.123l-0.963,-1.345c-0.257,-0.36 -0.466,-0.64 -0.477,-0.64 -0.012,0 -0.416,0.544 -0.958,1.287a83.9,83.9 0,0 1,-0.947 1.287c-0.015,0 -1.86,-1.3 -1.865,-1.313 -0.002,-0.007 0.415,-0.62 0.927,-1.361 0.512,-0.742 0.931,-1.36 0.931,-1.375 0,-0.023 -0.166,-0.081 -1.468,-0.515l-1.485,-0.496c-0.013,-0.005 0.063,-0.263 0.327,-1.094 0.19,-0.599 0.349,-1.093 0.354,-1.099 0.005,-0.006 0.707,0.219 1.56,0.5 0.852,0.28 1.556,0.509 1.565,0.509 0.008,0 0.018,-0.013 0.022,-0.03 0.003,-0.015 0.01,-0.74 0.016,-1.612 0.006,-0.87 0.015,-1.59 0.02,-1.6 0.009,-0.012 0.248,-0.015 1.127,-0.015 0.614,0 1.123,0.004 1.13,0.01 0.01,0.006 0.027,0.485 0.056,1.56 0.046,1.766 0.047,1.79 0.075,1.79 0.01,0 0.686,-0.226 1.501,-0.503a50.795,50.795 0,0 1,1.49 -0.492c0.016,0.019 0.685,2.194 0.676,2.202 -0.004,0.005 -0.684,0.237 -1.51,0.517 -1.137,0.386 -1.504,0.515 -1.507,0.531 -0.003,0.012 0.388,0.597 0.886,1.324 0.49,0.716 0.888,1.308 0.886,1.314a96.945,96.945 0,0 1,-1.852 1.364c-0.006,0 -0.239,-0.317 -0.517,-0.705z"
|
||||
android:fillColor="#fafafa"/>
|
||||
</group>
|
||||
</vector>
|
BIN
app/src/flavorAtest/res/ic_launcher-web.png
Normal file
After Width: | Height: | Size: 27 KiB |
4
app/src/flavorAtest/res/values/strings-flavor.xml
Normal file
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_name" translatable="false">secondlion*</string>
|
||||
</resources>
|
BIN
app/src/flavorDandelior/ic_launcher-web.png
Normal file
After Width: | Height: | Size: 26 KiB |
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/ic_launcher_background"/>
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/ic_launcher_background"/>
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
BIN
app/src/flavorDandelior/res/drawable-hdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 7.6 KiB |
BIN
app/src/flavorDandelior/res/drawable-hdpi/ic_launcher_round.png
Normal file
After Width: | Height: | Size: 7.6 KiB |
BIN
app/src/flavorDandelior/res/drawable-ldpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 6 KiB |
BIN
app/src/flavorDandelior/res/drawable-ldpi/ic_launcher_round.png
Normal file
After Width: | Height: | Size: 6 KiB |
BIN
app/src/flavorDandelior/res/drawable-mdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 6.6 KiB |
BIN
app/src/flavorDandelior/res/drawable-mdpi/ic_launcher_round.png
Normal file
After Width: | Height: | Size: 6.6 KiB |
BIN
app/src/flavorDandelior/res/drawable-xhdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 8.7 KiB |
BIN
app/src/flavorDandelior/res/drawable-xhdpi/ic_launcher_round.png
Normal file
After Width: | Height: | Size: 8.7 KiB |
BIN
app/src/flavorDandelior/res/drawable-xxhdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 11 KiB |
After Width: | Height: | Size: 11 KiB |
BIN
app/src/flavorDandelior/res/drawable-xxxhdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 14 KiB |
After Width: | Height: | Size: 14 KiB |
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="20"
|
||||
android:viewportHeight="20">
|
||||
<path
|
||||
android:pathData="M0.025,-0.07h19.95v20.14H0.025z"
|
||||
android:fillColor="#000000"/>
|
||||
</vector>
|
|
@ -0,0 +1,12 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="36.363636"
|
||||
android:viewportHeight="36.363636">
|
||||
<group android:translateX="8.181818"
|
||||
android:translateY="8.181818">
|
||||
<path
|
||||
android:pathData="M11.337,14.123l-0.963,-1.345c-0.257,-0.36 -0.466,-0.64 -0.477,-0.64 -0.012,0 -0.416,0.544 -0.958,1.287a83.9,83.9 0,0 1,-0.947 1.287c-0.015,0 -1.86,-1.3 -1.865,-1.313 -0.002,-0.007 0.415,-0.62 0.927,-1.361 0.512,-0.742 0.931,-1.36 0.931,-1.375 0,-0.023 -0.166,-0.081 -1.468,-0.515l-1.485,-0.496c-0.013,-0.005 0.063,-0.263 0.327,-1.094 0.19,-0.599 0.349,-1.093 0.354,-1.099 0.005,-0.006 0.707,0.219 1.56,0.5 0.852,0.28 1.556,0.509 1.565,0.509 0.008,0 0.018,-0.013 0.022,-0.03 0.003,-0.015 0.01,-0.74 0.016,-1.612 0.006,-0.87 0.015,-1.59 0.02,-1.6 0.009,-0.012 0.248,-0.015 1.127,-0.015 0.614,0 1.123,0.004 1.13,0.01 0.01,0.006 0.027,0.485 0.056,1.56 0.046,1.766 0.047,1.79 0.075,1.79 0.01,0 0.686,-0.226 1.501,-0.503a50.795,50.795 0,0 1,1.49 -0.492c0.016,0.019 0.685,2.194 0.676,2.202 -0.004,0.005 -0.684,0.237 -1.51,0.517 -1.137,0.386 -1.504,0.515 -1.507,0.531 -0.003,0.012 0.388,0.597 0.886,1.324 0.49,0.716 0.888,1.308 0.886,1.314a96.945,96.945 0,0 1,-1.852 1.364c-0.006,0 -0.239,-0.317 -0.517,-0.705z"
|
||||
android:fillColor="#fafafa"/>
|
||||
</group>
|
||||
</vector>
|
4
app/src/flavorDandelior/res/values/strings-flavor.xml
Normal file
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_name" translatable="false">dandelior*</string>
|
||||
</resources>
|
|
@ -7,12 +7,15 @@
|
|||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.INSTALL_SHORTCUT" />
|
||||
<uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT" />
|
||||
|
||||
<application
|
||||
android:name="com.github.dfa.diaspora_android.App"
|
||||
android:allowBackup="false"
|
||||
android:icon="${appIcon}"
|
||||
android:icon="@drawable/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:requestLegacyExternalStorage="true"
|
||||
android:theme="@style/DiasporaLight">
|
||||
|
||||
<provider
|
||||
|
@ -20,6 +23,16 @@
|
|||
android:authorities="${applicationId}.mainactivity"
|
||||
android:exported="false" />
|
||||
|
||||
<!--File provider for sharing files-->
|
||||
<provider
|
||||
android:name="android.support.v4.content.FileProvider"
|
||||
android:authorities="${applicationId}"
|
||||
android:exported="false"
|
||||
android:grantUriPermissions="true">
|
||||
<meta-data
|
||||
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||
android:resource="@xml/file_provider_paths" />
|
||||
</provider>
|
||||
<service
|
||||
android:name="com.github.dfa.diaspora_android.service.FetchPodsService"
|
||||
android:enabled="true"
|
||||
|
@ -35,20 +48,29 @@
|
|||
<activity
|
||||
android:name="com.github.dfa.diaspora_android.activity.AboutActivity"
|
||||
android:configChanges="locale"
|
||||
android:label="@string/about_activity__title_about_app"
|
||||
android:label="@string/about"
|
||||
android:theme="@style/DiasporaLight.NoActionBar" />
|
||||
|
||||
<activity
|
||||
android:name="com.github.dfa.diaspora_android.activity.MainActivity"
|
||||
android:configChanges="keyboardHidden|locale|orientation|screenSize"
|
||||
android:icon="${appIcon}"
|
||||
android:icon="@drawable/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:launchMode="singleTop"
|
||||
android:theme="@style/DiasporaLight.NoActionBar"
|
||||
android:windowSoftInputMode="adjustResize">
|
||||
|
||||
<meta-data
|
||||
android:name="android.app.shortcuts"
|
||||
android:resource="@xml/shortcuts" />
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="sc_new_post" />
|
||||
<action android:name="sc_nav_followed_tags" />
|
||||
<action android:name="sc_aspects" />
|
||||
<action android:name="sc_activities" />
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
|
||||
|
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 29 KiB |
Before Width: | Height: | Size: 17 KiB |
|
@ -35,6 +35,8 @@ import com.github.dfa.diaspora_android.util.AppSettings;
|
|||
import com.github.dfa.diaspora_android.util.DiasporaUrlHelper;
|
||||
|
||||
import net.gsantner.opoc.util.AdBlock;
|
||||
import net.gsantner.opoc.util.ContextUtils;
|
||||
import net.gsantner.opoc.util.ShareUtil;
|
||||
|
||||
public class App extends Application {
|
||||
private volatile static App app;
|
||||
|
@ -50,10 +52,18 @@ public class App extends Application {
|
|||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
ShareUtil.setFileProviderAuthority(BuildConfig.APPLICATION_ID);
|
||||
app = this;
|
||||
final Context c = getApplicationContext();
|
||||
appSettings = AppSettings.get();
|
||||
|
||||
String a = new ContextUtils(this).bcstr("FLAVOR", "");
|
||||
a += "__";
|
||||
|
||||
if (appSettings.isAppFirstStart() && "flavorDandelior".equals(new ContextUtils(this).bcstr("FLAVOR", ""))) {
|
||||
appSettings.setAmoledColorMode(true);
|
||||
}
|
||||
|
||||
// Init app log
|
||||
AppLog.setLoggingEnabled(appSettings.isLoggingEnabled());
|
||||
AppLog.setLoggingSpamEnabled(appSettings.isLoggingSpamEnabled());
|
||||
|
|
|
@ -20,12 +20,13 @@ package com.github.dfa.diaspora_android.activity;
|
|||
|
||||
import android.content.ClipData;
|
||||
import android.content.ClipboardManager;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.design.widget.AppBarLayout;
|
||||
import android.support.design.widget.TabLayout;
|
||||
import android.support.v4.app.Fragment;
|
||||
|
@ -157,20 +158,23 @@ public class AboutActivity extends ThemedActivity
|
|||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
View rootView = inflater.inflate(R.layout.about__fragment_about, container, false);
|
||||
ButterKnife.bind(this, rootView);
|
||||
protected int getLayoutResId() {
|
||||
return R.layout.about__fragment_about;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
ButterKnife.bind(this, view);
|
||||
if (isAdded()) {
|
||||
try {
|
||||
PackageInfo pInfo = getActivity().getPackageManager().getPackageInfo(getActivity().getPackageName(), 0);
|
||||
appVersion.setText(getString(R.string.fragment_debug__app_version, pInfo.versionName + " (" + pInfo.versionCode + ")"));
|
||||
appVersion.setText(getString(R.string.app_version_with_arg, pInfo.versionName + " (" + pInfo.versionCode + ")"));
|
||||
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
return rootView;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -209,8 +213,8 @@ public class AboutActivity extends ThemedActivity
|
|||
Intent sharingIntent = new Intent(android.content.Intent.ACTION_SEND);
|
||||
sharingIntent.setType("text/plain");
|
||||
sharingIntent.putExtra(android.content.Intent.EXTRA_SUBJECT, getString(R.string.app_name));
|
||||
sharingIntent.putExtra(android.content.Intent.EXTRA_TEXT, getString(R.string.fragment_about__spread_the_word_share_text, getString(R.string.fragment_about__fdroid_link)));
|
||||
startActivity(Intent.createChooser(sharingIntent, getResources().getString(R.string.action_share_dotdotdot)));
|
||||
sharingIntent.putExtra(android.content.Intent.EXTRA_TEXT, getString(R.string.hey_checkout_dandelion_tag__appspecific, getString(R.string.fragment_about__fdroid_link)));
|
||||
startActivity(Intent.createChooser(sharingIntent, getResources().getString(R.string.share_dotdotdot)));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -244,19 +248,22 @@ public class AboutActivity extends ThemedActivity
|
|||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
View rootView = inflater.inflate(R.layout.about__fragment_license, container, false);
|
||||
ButterKnife.bind(this, rootView);
|
||||
final Context context = rootView.getContext();
|
||||
protected int getLayoutResId() {
|
||||
return R.layout.about__fragment_license;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
ButterKnife.bind(this, view);
|
||||
accentColor = ContextUtils.get().colorToHexString(ThemeHelper.getAccentColor());
|
||||
|
||||
maintainers.setTextFormatted(getString(R.string.fragment_license__maintainers_text,
|
||||
maintainers.setTextFormatted(getString(R.string.this_app_is_currently_developed_and_maintained_by_witharg,
|
||||
ContextUtils.get().loadMarkdownForTextViewFromRaw(R.raw.maintainers, "")));
|
||||
contributors.setTextFormatted(getString(R.string.fragment_license__contributors_thank_you,
|
||||
contributors.setTextFormatted(getString(R.string.thank_you_witharg,
|
||||
ContextUtils.get().loadMarkdownForTextViewFromRaw(R.raw.contributors, "")));
|
||||
thirdPartyLibs.setTextFormatted(
|
||||
ContextUtils.get().loadMarkdownForTextViewFromRaw(R.raw.license_third_party, ""));
|
||||
return rootView;
|
||||
ContextUtils.get().loadMarkdownForTextViewFromRaw(R.raw.licenses_3rd_party, ""));
|
||||
}
|
||||
|
||||
@OnClick({R.id.fragment_license__leafpic_button, R.id.fragment_license__license_button})
|
||||
|
@ -334,7 +341,7 @@ public class AboutActivity extends ThemedActivity
|
|||
ClipboardManager clipboard = (ClipboardManager) getActivity().getSystemService(CLIPBOARD_SERVICE);
|
||||
ClipData clip = ClipData.newPlainText("DEBUG_LOG", AppLog.Log.getLogBuffer());
|
||||
clipboard.setPrimaryClip(clip);
|
||||
Toast.makeText(DebugFragment.this.getActivity(), R.string.fragment_debug__toast_log_copied, Toast.LENGTH_SHORT).show();
|
||||
Toast.makeText(DebugFragment.this.getActivity(), R.string.debug_log_copied_to_clipboard, Toast.LENGTH_SHORT).show();
|
||||
} else {
|
||||
AppLog.d(this, "Not Added!");
|
||||
}
|
||||
|
@ -349,13 +356,13 @@ public class AboutActivity extends ThemedActivity
|
|||
PackageInfo pInfo = getActivity().getPackageManager().getPackageInfo(getActivity().getPackageName(), 0);
|
||||
AppSettings appSettings = ((App) getActivity().getApplication()).getSettings();
|
||||
packageName.setText(pInfo.packageName);
|
||||
appVersion.setText(getString(R.string.fragment_debug__app_version, pInfo.versionName + " (" + pInfo.versionCode + ")"));
|
||||
appVersion.setText(getString(R.string.app_version_with_arg, pInfo.versionName + " (" + pInfo.versionCode + ")"));
|
||||
|
||||
osVersion.setText(getString(R.string.fragment_debug__android_version, Build.VERSION.RELEASE));
|
||||
deviceName.setText(getString(R.string.fragment_debug__device_name, Build.MANUFACTURER + " " + Build.MODEL));
|
||||
osVersion.setText(getString(R.string.android_version_witharg, Build.VERSION.RELEASE));
|
||||
deviceName.setText(getString(R.string.device_name_witharg, Build.MANUFACTURER + " " + Build.MODEL));
|
||||
if (app.getSettings().getPod() != null) {
|
||||
podDomain.setText(getString(R.string.fragment_debug__pod_profile_url, app.getSettings().getPod().getPodUrl()));
|
||||
podName.setText(getString(R.string.fragment_debug__pod_profile_name, app.getSettings().getPod().getName()));
|
||||
podDomain.setText(getString(R.string.pod_domain_witharg__appspecific, app.getSettings().getPod().getPodUrl()));
|
||||
podName.setText(getString(R.string.pod_profile_name_witharg__appspecific, app.getSettings().getPod().getName()));
|
||||
}
|
||||
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
|
@ -413,11 +420,11 @@ public class AboutActivity extends ThemedActivity
|
|||
public CharSequence getPageTitle(int position) {
|
||||
switch (position) {
|
||||
case 0:
|
||||
return getString(R.string.about_activity__title_about_app);
|
||||
return getString(R.string.about);
|
||||
case 1:
|
||||
return getString(R.string.about_activity__title_about_license);
|
||||
return getString(R.string.license);
|
||||
case 2:
|
||||
return getString(R.string.about_activity__title_debug_info);
|
||||
return getString(R.string.debugging);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -1,209 +0,0 @@
|
|||
/*
|
||||
This file is part of the dandelion*.
|
||||
|
||||
dandelion* is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
dandelion* is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with the dandelion*.
|
||||
|
||||
If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.github.dfa.diaspora_android.activity;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.os.Bundle;
|
||||
import android.support.v7.widget.AppCompatImageView;
|
||||
import android.support.v7.widget.LinearLayoutManager;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.github.dfa.diaspora_android.App;
|
||||
import com.github.dfa.diaspora_android.R;
|
||||
import com.github.dfa.diaspora_android.data.DiasporaAspect;
|
||||
import com.github.dfa.diaspora_android.listener.OnSomethingClickListener;
|
||||
import com.github.dfa.diaspora_android.ui.theme.ThemedFragment;
|
||||
import com.github.dfa.diaspora_android.util.AppLog;
|
||||
import com.github.dfa.diaspora_android.util.AppSettings;
|
||||
import com.github.dfa.diaspora_android.util.ContextUtils;
|
||||
import com.github.dfa.diaspora_android.util.DiasporaUrlHelper;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import butterknife.BindView;
|
||||
import butterknife.ButterKnife;
|
||||
|
||||
/**
|
||||
* Fragment that shows a list of the Aspects
|
||||
*/
|
||||
public class AspectListFragment extends ThemedFragment implements OnSomethingClickListener<Object> {
|
||||
|
||||
public static final String TAG = "com.github.dfa.diaspora_android.AspectListFragment";
|
||||
|
||||
@BindView(R.id.fragment_list__recycler_view)
|
||||
public RecyclerView aspectsRecyclerView;
|
||||
|
||||
@BindView(R.id.fragment_list__spacer)
|
||||
public View space;
|
||||
|
||||
@BindView(R.id.fragment_list__root)
|
||||
public RelativeLayout rootView;
|
||||
|
||||
protected App app;
|
||||
protected DiasporaUrlHelper urls;
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
AppLog.d(this, "onCreateView()");
|
||||
return inflater.inflate(R.layout.recycler_list__fragment, container, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(View view, Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
ButterKnife.bind(this, view);
|
||||
app = (App) getActivity().getApplication();
|
||||
AppSettings appSettings = app.getSettings();
|
||||
urls = new DiasporaUrlHelper(appSettings);
|
||||
|
||||
aspectsRecyclerView.setHasFixedSize(true);
|
||||
aspectsRecyclerView.setNestedScrollingEnabled(false);
|
||||
|
||||
RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(getContext());
|
||||
aspectsRecyclerView.setLayoutManager(layoutManager);
|
||||
|
||||
final AspectAdapter adapter = new AspectAdapter(appSettings, this);
|
||||
aspectsRecyclerView.setAdapter(adapter);
|
||||
|
||||
//Set window title
|
||||
getActivity().setTitle(R.string.nav_aspects);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFragmentTag() {
|
||||
return TAG;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onBackPressed() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSomethingClicked(Object null1, Integer null2, String aspectId) {
|
||||
((MainActivity) getActivity()).openDiasporaUrl(urls.getAspectUrl(aspectId));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void applyColorToViews() {
|
||||
aspectsRecyclerView.invalidate();
|
||||
if (getAppSettings().isAmoledColorMode()) {
|
||||
rootView.setBackgroundColor(Color.BLACK);
|
||||
space.setBackgroundColor(Color.BLACK);
|
||||
}
|
||||
}
|
||||
|
||||
public static class AspectAdapter extends RecyclerView.Adapter<AspectAdapter.ViewHolder> {
|
||||
private boolean isAmoledColorMode;
|
||||
private final AppSettings appSettings;
|
||||
private final DiasporaAspect[] aspectList;
|
||||
private final List<String> aspectFavsList;
|
||||
private final OnSomethingClickListener<Object> aspectClickedListener;
|
||||
|
||||
static class ViewHolder extends RecyclerView.ViewHolder {
|
||||
@BindView(R.id.recycler_view__list_item__text)
|
||||
public TextView title;
|
||||
@BindView(R.id.recycler_view__list_item__favourite)
|
||||
AppCompatImageView favouriteImage;
|
||||
@BindView(R.id.recycler_view__list_item__root)
|
||||
RelativeLayout root;
|
||||
|
||||
ViewHolder(View v) {
|
||||
super(v);
|
||||
ButterKnife.bind(this, v);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
AspectAdapter(AppSettings appSettings, OnSomethingClickListener<Object> aspectClickedListener) {
|
||||
this.appSettings = appSettings;
|
||||
this.aspectList = appSettings.getAspects();
|
||||
this.aspectFavsList = new ArrayList<>(Arrays.asList(appSettings.getAspectFavs()));
|
||||
this.aspectClickedListener = aspectClickedListener;
|
||||
this.isAmoledColorMode = appSettings.isAmoledColorMode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return aspectList.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AspectAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||
View v = LayoutInflater.from(parent.getContext())
|
||||
.inflate(R.layout.recycler_list__list_item_with_fav, parent, false);
|
||||
return new ViewHolder(v);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(final ViewHolder holder, int position) {
|
||||
// Alternating colors
|
||||
final Context c = holder.root.getContext();
|
||||
final DiasporaAspect aspect = aspectList[position];
|
||||
holder.title.setText(aspect.name);
|
||||
if (position % 2 == 1) {
|
||||
holder.root.setBackgroundColor(isAmoledColorMode ? Color.BLACK : ContextUtils.get().color(R.color.alternate_row_color));
|
||||
holder.title.setTextColor(isAmoledColorMode ? Color.GRAY : Color.BLACK);
|
||||
} else {
|
||||
holder.root.setBackgroundColor(isAmoledColorMode ? Color.BLACK : Color.WHITE);
|
||||
holder.title.setTextColor(isAmoledColorMode ? Color.GRAY : Color.BLACK);
|
||||
}
|
||||
|
||||
// Favourite (Star) Image
|
||||
applyFavouriteImage(holder.favouriteImage, isAspectFaved(aspect.name));
|
||||
|
||||
// Click on fav button
|
||||
holder.favouriteImage.setOnClickListener(new View.OnClickListener() {
|
||||
public void onClick(View v) {
|
||||
if (isAspectFaved(aspect.name)) {
|
||||
aspectFavsList.remove(aspectFavsList.indexOf(aspect.name));
|
||||
} else {
|
||||
aspectFavsList.add(aspect.name);
|
||||
}
|
||||
appSettings.setAspectFavs(aspectFavsList);
|
||||
applyFavouriteImage(holder.favouriteImage, isAspectFaved(aspect.name));
|
||||
}
|
||||
});
|
||||
|
||||
holder.root.setOnClickListener(new View.OnClickListener() {
|
||||
public void onClick(View v) {
|
||||
aspectClickedListener.onSomethingClicked(null, null, aspect.id + "");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private boolean isAspectFaved(String tag) {
|
||||
return aspectFavsList.contains(tag);
|
||||
}
|
||||
|
||||
private void applyFavouriteImage(AppCompatImageView imageView, boolean isFaved) {
|
||||
imageView.setImageResource(isFaved ? R.drawable.ic_star_filled_48px : R.drawable.ic_star_border_black_48px);
|
||||
imageView.setColorFilter(isFaved ? appSettings.getAccentColor() : (isAmoledColorMode ? Color.GRAY : 0), PorterDuff.Mode.SRC_ATOP);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -25,6 +25,7 @@ import android.app.Activity;
|
|||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.graphics.Bitmap;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
|
@ -39,8 +40,10 @@ import android.webkit.JavascriptInterface;
|
|||
import android.webkit.ValueCallback;
|
||||
import android.webkit.WebChromeClient;
|
||||
import android.webkit.WebView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.github.dfa.diaspora_android.App;
|
||||
import com.github.dfa.diaspora_android.BuildConfig;
|
||||
import com.github.dfa.diaspora_android.R;
|
||||
import com.github.dfa.diaspora_android.data.DiasporaUserProfile;
|
||||
import com.github.dfa.diaspora_android.ui.theme.ThemedAlertDialogBuilder;
|
||||
|
@ -53,10 +56,14 @@ import com.github.dfa.diaspora_android.web.DiasporaStreamWebChromeClient;
|
|||
import com.github.dfa.diaspora_android.web.FileUploadWebChromeClient;
|
||||
import com.github.dfa.diaspora_android.web.WebHelper;
|
||||
|
||||
import net.gsantner.opoc.util.PermissionChecker;
|
||||
import net.gsantner.opoc.util.ShareUtil;
|
||||
|
||||
import org.json.JSONException;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* Fragment that displays the Stream of the diaspora* user
|
||||
|
@ -97,6 +104,9 @@ public class DiasporaStreamFragment extends BrowserFragment {
|
|||
super.onCreateOptionsMenu(menu, inflater);
|
||||
inflater.inflate(R.menu.stream__menu_top, menu);
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||
menu.findItem(R.id.action_share_pdf).setVisible(true);
|
||||
}
|
||||
|
||||
final boolean darkBg = ContextUtils.get().shouldColorOnTopBeLight(AppSettings.get().getPrimaryColor());
|
||||
ContextUtils.get().tintMenuItems(menu, true, ContextCompat.getColor(getActivity(), darkBg ? R.color.white : R.color.black));
|
||||
|
@ -118,6 +128,8 @@ public class DiasporaStreamFragment extends BrowserFragment {
|
|||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
AppLog.d(this, "StreamFragment.onOptionsItemSelected()");
|
||||
ShareUtil shu = new ShareUtil(getContext());
|
||||
PermissionChecker permc = new PermissionChecker(getActivity());
|
||||
switch (item.getItemId()) {
|
||||
case R.id.action_reload: {
|
||||
if (WebHelper.isOnline(getContext())) {
|
||||
|
@ -140,17 +152,51 @@ public class DiasporaStreamFragment extends BrowserFragment {
|
|||
sharingIntent.setType("text/plain");
|
||||
sharingIntent.putExtra(Intent.EXTRA_SUBJECT, webView.getTitle());
|
||||
sharingIntent.putExtra(Intent.EXTRA_TEXT, webView.getUrl());
|
||||
startActivity(Intent.createChooser(sharingIntent, getResources().getString(R.string.action_share_dotdotdot)));
|
||||
startActivity(Intent.createChooser(sharingIntent, getResources().getString(R.string.share_dotdotdot)));
|
||||
return true;
|
||||
}
|
||||
|
||||
case R.id.action_share_pdf: {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||
shu.createPdf(webView, "dandelion-" + ShareUtil.SDF_SHORT.format(new Date()));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
case R.id.action_share_link_to_clipboard: {
|
||||
shu.setClipboard(webView.getUrl());
|
||||
Toast.makeText(getContext(), R.string.link_adress_copied, Toast.LENGTH_SHORT).show();
|
||||
return true;
|
||||
}
|
||||
|
||||
case R.id.action_create_launcher_shortcut: {
|
||||
if (webView.getUrl() != null) {
|
||||
Intent intent = new Intent(getContext(), MainActivity.class);
|
||||
intent.setAction(Intent.ACTION_VIEW);
|
||||
intent.setData(Uri.parse(webView.getUrl()));
|
||||
shu.createLauncherDesktopShortcut(intent, R.drawable.ic_launcher, webView.getTitle());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
case R.id.action_take_screenshot: {
|
||||
makeScreenshotOfWebView(false);
|
||||
if (permc.doIfExtStoragePermissionGranted(getString(R.string.screenshot_permission__appspecific))) {
|
||||
File fileSaveDirectory = appSettings.getAppSaveDirectory();
|
||||
if (permc.mkdirIfStoragePermissionGranted(fileSaveDirectory)) {
|
||||
Bitmap bmp = ShareUtil.getBitmapFromWebView(webView);
|
||||
String filename = "dandelion-" + ShareUtil.SDF_SHORT.format(new Date()) + ".jpg";
|
||||
_cu.writeImageToFile(new File(fileSaveDirectory, filename), bmp);
|
||||
Snackbar.make(webView, getString(R.string.saving_screenshot_as)
|
||||
+ " " + filename, Snackbar.LENGTH_LONG).show();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
case R.id.action_share_screenshot: {
|
||||
makeScreenshotOfWebView(true);
|
||||
if (permc.doIfExtStoragePermissionGranted(getString(R.string.screenshot_permission__appspecific))) {
|
||||
shu.shareImage(ShareUtil.getBitmapFromWebView(webView));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -247,7 +293,7 @@ public class DiasporaStreamFragment extends BrowserFragment {
|
|||
if (hasWRITE_EXTERNAL_STORAGE != PackageManager.PERMISSION_GRANTED) {
|
||||
if (!shouldShowRequestPermissionRationale(Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
|
||||
new ThemedAlertDialogBuilder(getContext(), appSettings)
|
||||
.setMessage(R.string.permissions_image)
|
||||
.setMessage(R.string.image_permission_description__appspecific)
|
||||
.setNegativeButton(android.R.string.no, null)
|
||||
.setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
|
@ -325,13 +371,30 @@ public class DiasporaStreamFragment extends BrowserFragment {
|
|||
@SuppressWarnings("unused")
|
||||
@JavascriptInterface
|
||||
public void setUserProfile(final String webMessage) throws JSONException {
|
||||
final DiasporaUserProfile pup = ((App) getActivity().getApplication()).getDiasporaUserProfile();
|
||||
if (pup.isRefreshNeeded()) {
|
||||
AppLog.v(this, "DiasporaUserProfile needs refresh; Try to parse JSON");
|
||||
pup.parseJson(webMessage);
|
||||
getActivity().runOnUiThread(new Runnable() {
|
||||
final Activity activity = getActivity();
|
||||
if (activity != null) {
|
||||
activity.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
pup.analyzeUrl(webView.getUrl());
|
||||
App app = ((App) activity.getApplication());
|
||||
final DiasporaUserProfile pup = app.getDiasporaUserProfile();
|
||||
if (pup.isRefreshNeeded()) {
|
||||
try {
|
||||
// Try to very fail-safe check if user information gets really loaded from correct pod
|
||||
if (!webView.getUrl().startsWith(app.getSettings().getPod().getPodUrl().getBaseUrl())) {
|
||||
return;
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
return;
|
||||
}
|
||||
AppLog.v(this, "DiasporaUserProfile needs refresh; Try to parse JSON");
|
||||
pup.parseJson(webMessage);
|
||||
getActivity().runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
pup.analyzeUrl(webView.getUrl());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -36,6 +36,7 @@ import android.support.customtabs.CustomTabsSession;
|
|||
import android.support.design.widget.AppBarLayout;
|
||||
import android.support.design.widget.NavigationView;
|
||||
import android.support.design.widget.Snackbar;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v4.app.FragmentManager;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
import android.support.v4.content.LocalBroadcastManager;
|
||||
|
@ -61,8 +62,8 @@ import android.widget.TextView;
|
|||
import android.widget.Toast;
|
||||
|
||||
import com.github.dfa.diaspora_android.App;
|
||||
import com.github.dfa.diaspora_android.BuildConfig;
|
||||
import com.github.dfa.diaspora_android.R;
|
||||
import com.github.dfa.diaspora_android.data.DiasporaAspect;
|
||||
import com.github.dfa.diaspora_android.data.DiasporaPodList;
|
||||
import com.github.dfa.diaspora_android.data.DiasporaUserProfile;
|
||||
import com.github.dfa.diaspora_android.listener.DiasporaUserProfileChangedListener;
|
||||
|
@ -71,10 +72,11 @@ import com.github.dfa.diaspora_android.receiver.OpenExternalLinkReceiver;
|
|||
import com.github.dfa.diaspora_android.receiver.UpdateTitleReceiver;
|
||||
import com.github.dfa.diaspora_android.ui.BadgeDrawable;
|
||||
import com.github.dfa.diaspora_android.ui.PodSelectionDialog;
|
||||
import com.github.dfa.diaspora_android.ui.theme.CustomFragment;
|
||||
import com.github.dfa.diaspora_android.ui.SearchOrCustomTextDialogCreator;
|
||||
import com.github.dfa.diaspora_android.ui.theme.ThemeHelper;
|
||||
import com.github.dfa.diaspora_android.ui.theme.ThemedActivity;
|
||||
import com.github.dfa.diaspora_android.ui.theme.ThemedAlertDialogBuilder;
|
||||
import com.github.dfa.diaspora_android.ui.theme.ThemedFragment;
|
||||
import com.github.dfa.diaspora_android.util.ActivityUtils;
|
||||
import com.github.dfa.diaspora_android.util.AndroidBug5497Workaround;
|
||||
import com.github.dfa.diaspora_android.util.AppLog;
|
||||
|
@ -87,7 +89,7 @@ import com.github.dfa.diaspora_android.web.ProxyHandler;
|
|||
import com.github.dfa.diaspora_android.web.WebHelper;
|
||||
import com.github.dfa.diaspora_android.web.custom_tab.CustomTabActivityHelper;
|
||||
|
||||
import net.gsantner.opoc.util.SimpleMarkdownParser;
|
||||
import net.gsantner.opoc.format.markdown.SimpleMarkdownParser;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
|
@ -199,7 +201,7 @@ public class MainActivity extends ThemedActivity
|
|||
brOpenExternalLink = new OpenExternalLinkReceiver(this);
|
||||
brSetTitle = new UpdateTitleReceiver(app, urls, new UpdateTitleReceiver.TitleCallback() {
|
||||
public void setTitle(String url, int resId) {
|
||||
CustomFragment top = getTopFragment();
|
||||
ThemedFragment top = getTopFragment();
|
||||
if (top != null && top.getFragmentTag().equals(DiasporaStreamFragment.TAG)) {
|
||||
MainActivity.this.setTitle(resId);
|
||||
showLastVisitedTimestampMessageIfNeeded(url);
|
||||
|
@ -207,7 +209,7 @@ public class MainActivity extends ThemedActivity
|
|||
}
|
||||
|
||||
public void setTitle(String url, String title) {
|
||||
CustomFragment top = getTopFragment();
|
||||
ThemedFragment top = getTopFragment();
|
||||
if (top != null && top.getFragmentTag().equals(DiasporaStreamFragment.TAG)) {
|
||||
MainActivity.this.setTitle(title);
|
||||
}
|
||||
|
@ -229,23 +231,16 @@ public class MainActivity extends ThemedActivity
|
|||
}
|
||||
}
|
||||
|
||||
// Show first start dialog
|
||||
// Show first start / update dialog
|
||||
try {
|
||||
SimpleMarkdownParser mdParser = SimpleMarkdownParser.get().setDefaultSmpFilter(SimpleMarkdownParser.FILTER_ANDROID_TEXTVIEW);
|
||||
if (_appSettings.isAppFirstStart()) {
|
||||
mdParser.parse(
|
||||
getResources().openRawResource(R.raw.license), "");
|
||||
String html = mdParser.getHtml()
|
||||
+ "<br/><br/><br/>"
|
||||
+ "<h1>" + getString(R.string.fragment_license__thirdparty_libs) + "</h1>"
|
||||
+ mdParser.parse(getResources().openRawResource(R.raw.license_third_party), "");
|
||||
html = mdParser.setHtml(html).removeMultiNewlines().getHtml();
|
||||
ActivityUtils.get(this).showDialogWithHtmlTextView(R.string.about_activity__title_about_license, html);
|
||||
_appSettings.isAppCurrentVersionFirstStart();
|
||||
} else if (_appSettings.isAppCurrentVersionFirstStart()) {
|
||||
SimpleMarkdownParser smp = new SimpleMarkdownParser().parse(
|
||||
getResources().openRawResource(R.raw.changelog), "");
|
||||
ActivityUtils.get(this).showDialogWithHtmlTextView(R.string.changelog, smp.getHtml());
|
||||
if (_appSettings.isAppCurrentVersionFirstStart(true)) {
|
||||
SimpleMarkdownParser smp = SimpleMarkdownParser.get().setDefaultSmpFilter(SimpleMarkdownParser.FILTER_ANDROID_TEXTVIEW);
|
||||
String html = "";
|
||||
html += smp.parse(getString(R.string.copyright_license_text_official).replace("\n", " \n"), "").getHtml();
|
||||
html += "<br/><br/><br/><big><big>" + getString(R.string.changelog) + "</big></big><br/>" + smp.parse(getResources().openRawResource(R.raw.changelog), "", SimpleMarkdownParser.FILTER_ANDROID_TEXTVIEW, SimpleMarkdownParser.FILTER_CHANGELOG).getHtml();
|
||||
html += "<br/><br/><br/><big><big>" + getString(R.string.licenses) + "</big></big><br/>" + smp.parse(getResources().openRawResource(R.raw.licenses_3rd_party), "").getHtml();
|
||||
ActivityUtils _au = new ActivityUtils(this);
|
||||
_au.showDialogWithHtmlTextView(R.string.licenses, html);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
|
@ -269,7 +264,7 @@ public class MainActivity extends ThemedActivity
|
|||
|
||||
//Setup snackbar
|
||||
snackbarExitApp = Snackbar
|
||||
.make(fragmentContainer, R.string.confirm_exit, Snackbar.LENGTH_LONG)
|
||||
.make(fragmentContainer, R.string.do_you_want_to_exit, Snackbar.LENGTH_LONG)
|
||||
.setAction(android.R.string.yes, new View.OnClickListener() {
|
||||
public void onClick(View view) {
|
||||
finish();
|
||||
|
@ -278,13 +273,13 @@ public class MainActivity extends ThemedActivity
|
|||
});
|
||||
snackbarLastVisitedTimestampInStream =
|
||||
Snackbar.make(fragmentContainer,
|
||||
R.string.jump_to_last_visited_timestamp_in_stream, Snackbar.LENGTH_LONG)
|
||||
R.string.jump_to_last_visited_page_in_stream__appspecific, Snackbar.LENGTH_LONG)
|
||||
.setAction(android.R.string.yes, new View.OnClickListener() {
|
||||
public void onClick(View view) {
|
||||
openDiasporaUrl(urls.getStreamWithTimestampUrl(diasporaUserProfile.getLastVisitedPositionInStream()));
|
||||
}
|
||||
});
|
||||
snackbarNoInternet = Snackbar.make(fragmentContainer, R.string.no_internet, Snackbar.LENGTH_LONG);
|
||||
snackbarNoInternet = Snackbar.make(fragmentContainer, R.string.sorry_need_to_be_connected_to_internet, Snackbar.LENGTH_LONG);
|
||||
|
||||
// Load app settings
|
||||
setupNavigationSlider();
|
||||
|
@ -292,15 +287,15 @@ public class MainActivity extends ThemedActivity
|
|||
}
|
||||
|
||||
/**
|
||||
* Get an instance of the CustomFragment with the tag fragmentTag.
|
||||
* Get an instance of the ThemedFragment with the tag fragmentTag.
|
||||
* If there was no instance so far, create a new one and add it to the FragmentManagers pool.
|
||||
* If there is no Fragment with the corresponding Tag, return the top fragment.
|
||||
*
|
||||
* @param fragmentTag tag
|
||||
* @return corresponding Fragment
|
||||
*/
|
||||
protected CustomFragment getFragment(String fragmentTag) {
|
||||
CustomFragment fragment = (CustomFragment) fm.findFragmentByTag(fragmentTag);
|
||||
protected ThemedFragment getFragment(String fragmentTag) {
|
||||
ThemedFragment fragment = (ThemedFragment) fm.findFragmentByTag(fragmentTag);
|
||||
if (fragment != null) {
|
||||
return fragment;
|
||||
} else {
|
||||
|
@ -313,14 +308,6 @@ public class MainActivity extends ThemedActivity
|
|||
BrowserFragment bf = new BrowserFragment();
|
||||
fm.beginTransaction().add(bf, fragmentTag).commit();
|
||||
return bf;
|
||||
case TagListFragment.TAG:
|
||||
TagListFragment hlf = new TagListFragment();
|
||||
fm.beginTransaction().add(hlf, fragmentTag).commit();
|
||||
return hlf;
|
||||
case AspectListFragment.TAG:
|
||||
AspectListFragment alf = new AspectListFragment();
|
||||
fm.beginTransaction().add(alf, fragmentTag).commit();
|
||||
return alf;
|
||||
case PodSelectionFragment.TAG:
|
||||
PodSelectionFragment psf = new PodSelectionFragment();
|
||||
fm.beginTransaction().add(psf, fragmentTag).commit();
|
||||
|
@ -338,12 +325,30 @@ public class MainActivity extends ThemedActivity
|
|||
*
|
||||
* @param url URL to load in the DiasporaStreamFragment
|
||||
*/
|
||||
public void openDiasporaUrl(String url) {
|
||||
public void openDiasporaUrl(final String url) {
|
||||
AppLog.v(this, "openDiasporaUrl()");
|
||||
DiasporaStreamFragment streamFragment = (DiasporaStreamFragment) getFragment(DiasporaStreamFragment.TAG);
|
||||
showFragment(streamFragment);
|
||||
showLastVisitedTimestampMessageIfNeeded(url);
|
||||
streamFragment.loadUrl(url);
|
||||
if (url != null && url.startsWith("http://127.0.0.1")) {
|
||||
// This URL seems to be called somehow, but it doesn't make sense ;)
|
||||
toolbarTop.postDelayed(() -> {
|
||||
Intent i = new Intent(ACTION_OPEN_EXTERNAL_URL);
|
||||
i.putExtra(EXTRA_URL, "https://github.com/gsantner/dandelion/blob/master/README.md");
|
||||
LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(i);
|
||||
}, 1000);
|
||||
return;
|
||||
}
|
||||
if (_appSettings.getPod() != null && _appSettings.getPod().getPodUrl() != null && _appSettings.getPod().getPodUrl().getBaseUrl() != null
|
||||
&& url.startsWith(_appSettings.getPod().getPodUrl().getBaseUrl()) && !url.startsWith("https://dia.so/")) {
|
||||
DiasporaStreamFragment streamFragment = (DiasporaStreamFragment) getFragment(DiasporaStreamFragment.TAG);
|
||||
showFragment(streamFragment);
|
||||
showLastVisitedTimestampMessageIfNeeded(url);
|
||||
streamFragment.loadUrl(url);
|
||||
} else {
|
||||
toolbarTop.postDelayed(() -> {
|
||||
Intent i = new Intent(ACTION_OPEN_EXTERNAL_URL);
|
||||
i.putExtra(EXTRA_URL, url);
|
||||
LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(i);
|
||||
}, 1000);
|
||||
}
|
||||
}
|
||||
|
||||
public void showLastVisitedTimestampMessageIfNeeded(String url) {
|
||||
|
@ -358,9 +363,16 @@ public class MainActivity extends ThemedActivity
|
|||
*
|
||||
* @param fragment Fragment to show
|
||||
*/
|
||||
protected void showFragment(CustomFragment fragment) {
|
||||
protected void showFragment(ThemedFragment fragment) {
|
||||
if (PodSelectionFragment.TAG.equals(fragment.getTag())) {
|
||||
Fragment fragment1 = fm.findFragmentByTag(DiasporaStreamFragment.TAG);
|
||||
if (fragment1 != null) {
|
||||
new net.gsantner.opoc.util.ContextUtils(this).restartApp(MainActivity.class);
|
||||
}
|
||||
}
|
||||
|
||||
AppLog.v(this, "showFragment()");
|
||||
CustomFragment currentTop = (CustomFragment) fm.findFragmentById(R.id.fragment_container);
|
||||
ThemedFragment currentTop = (ThemedFragment) fm.findFragmentById(R.id.fragment_container);
|
||||
if (currentTop == null || !currentTop.getFragmentTag().equals(fragment.getFragmentTag())) {
|
||||
AppLog.v(this, "Fragment was not visible. Replace it.");
|
||||
fm.beginTransaction().addToBackStack(null).replace(R.id.fragment_container, fragment, fragment.getFragmentTag()).commit();
|
||||
|
@ -376,7 +388,7 @@ public class MainActivity extends ThemedActivity
|
|||
*/
|
||||
private void setupNavigationSlider() {
|
||||
ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
|
||||
this, navDrawer, toolbarTop, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
|
||||
this, navDrawer, toolbarTop, R.string.open_navdrawer, R.string.close_navdrawer);
|
||||
navDrawer.addDrawerListener(toggle);
|
||||
toggle.syncState();
|
||||
|
||||
|
@ -420,8 +432,6 @@ public class MainActivity extends ThemedActivity
|
|||
app.getAvatarImageLoader().startImageDownload(navheaderImage, avatarUrl);
|
||||
}
|
||||
}
|
||||
} else if (BuildConfig.IS_TEST_BUILD) {
|
||||
navheaderImage.setImageResource(R.drawable.ic_launcher_test);
|
||||
}
|
||||
updateNavigationViewEntryVisibilities();
|
||||
}
|
||||
|
@ -448,7 +458,7 @@ public class MainActivity extends ThemedActivity
|
|||
navMenu.findItem(R.id.nav_statistics).setVisible(_appSettings.isVisibleInNavStatistics());
|
||||
navMenu.findItem(R.id.nav_reports).setVisible(_appSettings.isVisibleInNavReports());
|
||||
navMenu.findItem(R.id.nav_toggle_desktop_page).setVisible(_appSettings.isVisibleInNavToggleMobileDesktop());
|
||||
navMenu.findItem(R.id.nav_dandelion).setVisible(_appSettings.isVisibleInNavDandelionAccount());
|
||||
navMenu.findItem(R.id.nav_product_support).setVisible(_appSettings.isVisibleInNavGsantnerAccount());
|
||||
|
||||
|
||||
// Hide whole group (for logged in use) if no pod was selected
|
||||
|
@ -549,6 +559,18 @@ public class MainActivity extends ThemedActivity
|
|||
} else if (Intent.ACTION_SEND_MULTIPLE.equals(action) && type != null) {
|
||||
/* TODO: Implement and add filter to manifest */
|
||||
return;
|
||||
} else if ("sc_new_post".equals(action)) {
|
||||
openDiasporaUrl(urls.getNewPostUrl());
|
||||
return;
|
||||
} else if ("sc_activities".equals(action)) {
|
||||
openDiasporaUrl(urls.getActivityUrl());
|
||||
return;
|
||||
} else if ("sc_contacts".equals(action)) {
|
||||
onNavigationItemSelected(navView.getMenu().findItem(R.id.nav_aspects));
|
||||
return;
|
||||
} else if ("sc_tags".equals(action)) {
|
||||
onNavigationItemSelected(navView.getMenu().findItem(R.id.nav_followed_tags));
|
||||
return;
|
||||
}
|
||||
//Catch split screen recreation
|
||||
if (action != null && action.equals(Intent.ACTION_MAIN) && getTopFragment() != null) {
|
||||
|
@ -579,8 +601,8 @@ public class MainActivity extends ThemedActivity
|
|||
*
|
||||
* @return top fragment or null if there is none displayed
|
||||
*/
|
||||
private CustomFragment getTopFragment() {
|
||||
return (CustomFragment) fm.findFragmentById(R.id.fragment_container);
|
||||
private ThemedFragment getTopFragment() {
|
||||
return (ThemedFragment) fm.findFragmentById(R.id.fragment_container);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -593,7 +615,7 @@ public class MainActivity extends ThemedActivity
|
|||
navDrawer.closeDrawer(navView);
|
||||
return;
|
||||
}
|
||||
CustomFragment top = getTopFragment();
|
||||
ThemedFragment top = getTopFragment();
|
||||
if (top != null) {
|
||||
AppLog.v(this, "Top Fragment is not null");
|
||||
if (!top.onBackPressed()) {
|
||||
|
@ -671,7 +693,7 @@ public class MainActivity extends ThemedActivity
|
|||
//Clear the menus
|
||||
menu.clear();
|
||||
|
||||
CustomFragment top = getTopFragment();
|
||||
ThemedFragment top = getTopFragment();
|
||||
if (top != null) {
|
||||
if (!top.getFragmentTag().equals(PodSelectionFragment.TAG)) {
|
||||
cache = _appSettings.isExtendedNotificationsActivated();
|
||||
|
@ -681,10 +703,10 @@ public class MainActivity extends ThemedActivity
|
|||
}
|
||||
}
|
||||
|
||||
final boolean darkBg = ContextUtils.get().shouldColorOnTopBeLight(AppSettings.get().getPrimaryColor());
|
||||
ContextUtils.get()
|
||||
.tintMenuItems(menu, true, ContextCompat.getColor(this, darkBg ? R.color.white : R.color.black))
|
||||
.setSubMenuIconsVisiblity(menu, true);
|
||||
ContextUtils cu = ContextUtils.get();
|
||||
final boolean darkBg = cu.get().shouldColorOnTopBeLight(AppSettings.get().getPrimaryColor());
|
||||
cu.tintMenuItems(menu, true, ContextCompat.getColor(this, darkBg ? R.color.white : R.color.black));
|
||||
cu.setSubMenuIconsVisiblity(menu, true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -823,7 +845,7 @@ public class MainActivity extends ThemedActivity
|
|||
final InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
|
||||
@SuppressLint("InflateParams") View layout = getLayoutInflater().inflate(R.layout.ui__dialog_search__people_tags, null, false);
|
||||
final EditText input = (EditText) layout.findViewById(R.id.dialog_search__input);
|
||||
final EditText input = layout.findViewById(R.id.dialog_search__input);
|
||||
input.setMaxLines(1);
|
||||
input.setSingleLine(true);
|
||||
ThemeHelper.updateEditTextColor(input);
|
||||
|
@ -832,7 +854,7 @@ public class MainActivity extends ThemedActivity
|
|||
public void onClick(DialogInterface dialogInterface, int which) {
|
||||
String query = input.getText().toString().trim().replaceAll((which == DialogInterface.BUTTON_NEGATIVE ? "\\*" : "\\#"), "");
|
||||
if (query.equals("")) {
|
||||
Snackbar.make(fragmentContainer, R.string.search_alert_bypeople_validate_needsomedata, Snackbar.LENGTH_LONG).show();
|
||||
Snackbar.make(fragmentContainer, R.string.please_add_a_name, Snackbar.LENGTH_LONG).show();
|
||||
} else {
|
||||
openDiasporaUrl(which == DialogInterface.BUTTON_NEGATIVE ? urls.getSearchPeopleUrl(query) : urls.getSearchTagsUrl(query));
|
||||
}
|
||||
|
@ -844,8 +866,8 @@ public class MainActivity extends ThemedActivity
|
|||
final AlertDialog dialog = new ThemedAlertDialogBuilder(this, _appSettings)
|
||||
.setView(layout).setTitle(R.string.search_alert_title)
|
||||
.setCancelable(true)
|
||||
.setPositiveButton(R.string.search_alert_tag, clickListener)
|
||||
.setNegativeButton(R.string.search_alert_people, clickListener)
|
||||
.setPositiveButton(R.string.by_tags, clickListener)
|
||||
.setNegativeButton(R.string.by_people, clickListener)
|
||||
.create();
|
||||
|
||||
input.setOnEditorActionListener(new TextView.OnEditorActionListener() {
|
||||
|
@ -1041,12 +1063,39 @@ public class MainActivity extends ThemedActivity
|
|||
break;
|
||||
|
||||
case R.id.nav_followed_tags: {
|
||||
showFragment(getFragment(TagListFragment.TAG));
|
||||
SearchOrCustomTextDialogCreator.showDiasporaTagsDialog(this, arg -> {
|
||||
if (arg.startsWith(SearchOrCustomTextDialogCreator.SPECIAL_PREFIX)) {
|
||||
arg = arg.replace(SearchOrCustomTextDialogCreator.SPECIAL_PREFIX, "").trim();
|
||||
if (arg.equals(getString(R.string.manage_hashtags))) {
|
||||
openDiasporaUrl(urls.getManageTagsUrl());
|
||||
} else {
|
||||
openDiasporaUrl(urls.getAllFollowedTagsUrl());
|
||||
}
|
||||
} else {
|
||||
openDiasporaUrl(urls.getSearchTagsUrl(arg));
|
||||
}
|
||||
});
|
||||
}
|
||||
break;
|
||||
|
||||
case R.id.nav_aspects: {
|
||||
showFragment(getFragment(AspectListFragment.TAG));
|
||||
SearchOrCustomTextDialogCreator.showDiasporaAspectsDialog(this, arg -> {
|
||||
if (arg.startsWith(SearchOrCustomTextDialogCreator.SPECIAL_PREFIX)) {
|
||||
arg = arg.replace(SearchOrCustomTextDialogCreator.SPECIAL_PREFIX, "").trim();
|
||||
if (arg.equals(getString(R.string.manage_your_contact_list))) {
|
||||
openDiasporaUrl(urls.getContactsUrl());
|
||||
} else if (arg.equals(getString(R.string.nav_profile))) {
|
||||
openDiasporaUrl(urls.getProfileUrl());
|
||||
}
|
||||
} else {
|
||||
for (DiasporaAspect daspect : _appSettings.getAspects()) {
|
||||
if (arg.equals(daspect.name)) {
|
||||
openDiasporaUrl(urls.getAspectUrl(Long.toString(daspect.id)));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -1127,8 +1176,8 @@ public class MainActivity extends ThemedActivity
|
|||
}
|
||||
break;
|
||||
|
||||
case R.id.nav_dandelion: {
|
||||
openDiasporaUrl(urls.getProfileUrl("48b78420923501341ef3782bcb452bd5"));
|
||||
case R.id.nav_product_support: {
|
||||
openDiasporaUrl(urls.getProfileUrl("d1cbdd70095301341e834860008dbc6c"));
|
||||
}
|
||||
break;
|
||||
|
||||
|
|
|
@ -32,7 +32,6 @@ import android.support.v4.content.LocalBroadcastManager;
|
|||
import android.support.v4.view.MenuItemCompat;
|
||||
import android.support.v7.widget.AppCompatButton;
|
||||
import android.support.v7.widget.SearchView;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
|
@ -53,7 +52,6 @@ import com.github.dfa.diaspora_android.service.FetchPodsService;
|
|||
import com.github.dfa.diaspora_android.ui.PodSelectionDialog;
|
||||
import com.github.dfa.diaspora_android.ui.theme.ThemedFragment;
|
||||
import com.github.dfa.diaspora_android.util.ActivityUtils;
|
||||
import com.github.dfa.diaspora_android.util.AppLog;
|
||||
import com.github.dfa.diaspora_android.util.AppSettings;
|
||||
import com.github.dfa.diaspora_android.util.ContextUtils;
|
||||
import com.github.dfa.diaspora_android.util.DiasporaUrlHelper;
|
||||
|
@ -92,16 +90,14 @@ public class PodSelectionFragment extends ThemedFragment implements SearchView.O
|
|||
private String filterString = "";
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
AppLog.d(this, "onCreateView()");
|
||||
View view = inflater.inflate(R.layout.podselection__fragment, container, false);
|
||||
ButterKnife.bind(this, view);
|
||||
return view;
|
||||
protected int getLayoutResId() {
|
||||
return R.layout.podselection__fragment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(View view, Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
ButterKnife.bind(this, view);
|
||||
app = (App) getActivity().getApplication();
|
||||
appSettings = app.getSettings();
|
||||
|
||||
|
@ -165,7 +161,7 @@ public class PodSelectionFragment extends ThemedFragment implements SearchView.O
|
|||
} catch (JSONException ignored) {
|
||||
}
|
||||
} else {
|
||||
Snackbar.make(listViewPod, R.string.podlist_error, Snackbar.LENGTH_SHORT).show();
|
||||
Snackbar.make(listViewPod, R.string.could_not_retrieve_list_of_pods__appspecific, Snackbar.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -177,11 +173,10 @@ public class PodSelectionFragment extends ThemedFragment implements SearchView.O
|
|||
rootView.setBackgroundColor(appSettings.isAmoledColorMode() ? Color.BLACK : Color.WHITE);
|
||||
listViewPod.setDivider(new ColorDrawable(Color.GRAY));
|
||||
listViewPod.setDividerHeight(dividerHeight);
|
||||
if (appSettings.isAmoledColorMode()) {
|
||||
buttonUseCustomPod.setTextColor(Color.WHITE);
|
||||
} else {
|
||||
buttonUseCustomPod.setTextColor(ContextUtils.get().shouldColorOnTopBeLight(appSettings.getAccentColor()) ? Color.WHITE : Color.BLACK);
|
||||
}
|
||||
int bgcolor = appSettings.isAmoledColorMode() ? Color.DKGRAY : appSettings.getAccentColor();
|
||||
buttonUseCustomPod.setBackgroundColor(bgcolor);
|
||||
buttonUseCustomPod.setTextColor(_cu.shouldColorOnTopBeLight(bgcolor) ? Color.WHITE : Color.BLACK);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -204,7 +199,7 @@ public class PodSelectionFragment extends ThemedFragment implements SearchView.O
|
|||
@Override
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
View view = super.getView(position, convertView, parent);
|
||||
TextView textView = (TextView) view.findViewById(android.R.id.text1);
|
||||
TextView textView = view.findViewById(android.R.id.text1);
|
||||
textView.setTextColor(appSettings.isAmoledColorMode() ? Color.GRAY : Color.BLACK);
|
||||
return view;
|
||||
}
|
||||
|
|
|
@ -131,7 +131,7 @@ public class SettingsActivity extends ThemedActivity implements SharedPreference
|
|||
if (top != null && top.getFragmentTag().equals(SettingsFragmentProxy.TAG)) {
|
||||
ProxyHandler.ProxySettings newProxySettings = getAppSettings().getProxySettings();
|
||||
if (oldProxySettings.isEnabled() && !newProxySettings.isEnabled()) {
|
||||
Toast.makeText(this, R.string.toast__proxy_disabled__restart_required, Toast.LENGTH_LONG).show();
|
||||
Toast.makeText(this, R.string.app_needs_restart_to_disable_proxy_usage, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
}
|
||||
super.onBackPressed();
|
||||
|
@ -226,7 +226,7 @@ public class SettingsActivity extends ThemedActivity implements SharedPreference
|
|||
} else if (settings.isKeyEqual(key, R.string.pref_key__change_account)) {
|
||||
new ThemedAlertDialogBuilder(getActivity(), AppSettings.get())
|
||||
.setTitle(getString(R.string.confirmation))
|
||||
.setMessage(getString(R.string.pref_warning__change_account))
|
||||
.setMessage(getString(R.string.logout_warning_description))
|
||||
.setNegativeButton(android.R.string.no, null)
|
||||
.setPositiveButton(android.R.string.yes,
|
||||
new DialogInterface.OnClickListener() {
|
||||
|
@ -271,6 +271,8 @@ public class SettingsActivity extends ThemedActivity implements SharedPreference
|
|||
|
||||
@Override
|
||||
public boolean onPreferenceTreeClick(PreferenceScreen screen, Preference preference) {
|
||||
AppSettings settings = ((App) getActivity().getApplication()).getSettings();
|
||||
DiasporaUrlHelper diasporaUrlHelper = new DiasporaUrlHelper(settings);
|
||||
if (isAdded() && preference.hasKey()) {
|
||||
String key = preference.getKey();
|
||||
if (key.equals(getString(R.string.pref_key__primary_color__preference_click))) {
|
||||
|
@ -279,6 +281,13 @@ public class SettingsActivity extends ThemedActivity implements SharedPreference
|
|||
} else if (key.equals(getString(R.string.pref_key__accent_color__preference_click))) {
|
||||
showColorPickerDialog(2);
|
||||
return true;
|
||||
} else if (key.equals(getString(R.string.pref_key__manage_theme))) {
|
||||
Intent intent = new Intent(getActivity(), MainActivity.class);
|
||||
intent.setAction(MainActivity.ACTION_OPEN_URL);
|
||||
intent.putExtra(MainActivity.URL_MESSAGE, diasporaUrlHelper.getThemeUrl());
|
||||
startActivity(intent);
|
||||
getActivity().finish();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return super.onPreferenceTreeClick(screen, preference);
|
||||
|
@ -305,12 +314,12 @@ public class SettingsActivity extends ThemedActivity implements SharedPreference
|
|||
final ThemedAlertDialogBuilder builder = new ThemedAlertDialogBuilder(context, appSettings);
|
||||
builder.setView(dialogLayout);
|
||||
|
||||
final FrameLayout titleBackground = (FrameLayout) dialogLayout.findViewById(R.id.color_picker_dialog__title_background);
|
||||
final TextView title = (TextView) dialogLayout.findViewById(R.id.color_picker_dialog__title);
|
||||
final LineColorPicker base = (LineColorPicker) dialogLayout.findViewById(R.id.color_picker_dialog__base_picker);
|
||||
final LineColorPicker shade = (LineColorPicker) dialogLayout.findViewById(R.id.color_picker_dialog__shade_picker);
|
||||
final FrameLayout titleBackground = dialogLayout.findViewById(R.id.color_picker_dialog__title_background);
|
||||
final TextView title = dialogLayout.findViewById(R.id.color_picker_dialog__title);
|
||||
final LineColorPicker base = dialogLayout.findViewById(R.id.color_picker_dialog__base_picker);
|
||||
final LineColorPicker shade = dialogLayout.findViewById(R.id.color_picker_dialog__shade_picker);
|
||||
|
||||
title.setText(type == 1 ? R.string.pref_title__primary_color : R.string.pref_title__accent_color);
|
||||
title.setText(type == 1 ? R.string.primary_colors : R.string.accent_color);
|
||||
title.setTextColor(getResources().getColor(R.color.white));
|
||||
final int[] current = (type == 1 ? appSettings.getPrimaryColorSettings() : appSettings.getAccentColorSettings());
|
||||
base.setColors((type == 1 ? ColorPalette.getBaseColors(context) : ColorPalette.getAccentColors(context)));
|
||||
|
@ -412,7 +421,7 @@ public class SettingsActivity extends ThemedActivity implements SharedPreference
|
|||
if (appSettings.isKeyEqual(key, R.string.pref_key__http_proxy_load_tor_preset)) {
|
||||
appSettings.setProxyHttpHost("127.0.0.1");
|
||||
appSettings.setProxyHttpPort(8118);
|
||||
Toast.makeText(screen.getContext(), R.string.toast__proxy_orbot_preset_loaded, Toast.LENGTH_SHORT).show();
|
||||
Toast.makeText(screen.getContext(), R.string.orbot_proxy_preset_loaded, Toast.LENGTH_SHORT).show();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -477,17 +486,13 @@ public class SettingsActivity extends ThemedActivity implements SharedPreference
|
|||
|
||||
ThemedAlertDialogBuilder builder = new ThemedAlertDialogBuilder(getActivity(), appSettings);
|
||||
builder.setTitle(R.string.confirmation)
|
||||
.setMessage(R.string.dialog_content__wipe_settings)
|
||||
.setMessage(R.string.wipe_settings_warning__appspecific)
|
||||
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialogInterface, int i) {
|
||||
appSettings.resetAppSettings();
|
||||
appSettings.resetPodSettings();
|
||||
Intent restartActivity = new Intent(getActivity(), MainActivity.class);
|
||||
PendingIntent pendingIntent = PendingIntent.getActivity(getActivity(), 12374, restartActivity, PendingIntent.FLAG_CANCEL_CURRENT);
|
||||
AlarmManager mgr = (AlarmManager) getActivity().getSystemService(Context.ALARM_SERVICE);
|
||||
mgr.set(AlarmManager.RTC, System.currentTimeMillis() + 100, pendingIntent);
|
||||
System.exit(0);
|
||||
new net.gsantner.opoc.util.ContextUtils(appSettings.getContext()).restartApp(MainActivity.class);
|
||||
}
|
||||
}).setNegativeButton(android.R.string.cancel, null)
|
||||
.create().show();
|
||||
|
|
|
@ -1,208 +0,0 @@
|
|||
/*
|
||||
This file is part of the dandelion*.
|
||||
|
||||
dandelion* is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
dandelion* is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with the dandelion*.
|
||||
|
||||
If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.github.dfa.diaspora_android.activity;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.os.Bundle;
|
||||
import android.support.v7.widget.AppCompatImageView;
|
||||
import android.support.v7.widget.LinearLayoutManager;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.github.dfa.diaspora_android.App;
|
||||
import com.github.dfa.diaspora_android.R;
|
||||
import com.github.dfa.diaspora_android.listener.OnSomethingClickListener;
|
||||
import com.github.dfa.diaspora_android.ui.theme.ThemedFragment;
|
||||
import com.github.dfa.diaspora_android.util.AppLog;
|
||||
import com.github.dfa.diaspora_android.util.AppSettings;
|
||||
import com.github.dfa.diaspora_android.util.ContextUtils;
|
||||
import com.github.dfa.diaspora_android.util.DiasporaUrlHelper;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import butterknife.BindView;
|
||||
import butterknife.ButterKnife;
|
||||
|
||||
/**
|
||||
* Fragment that shows a list of the HashTags the user follows
|
||||
*/
|
||||
public class TagListFragment extends ThemedFragment implements OnSomethingClickListener<Object> {
|
||||
|
||||
public static final String TAG = "com.github.dfa.diaspora_android.TagListFragment";
|
||||
|
||||
@BindView(R.id.fragment_list__recycler_view)
|
||||
public RecyclerView followedTagsRecyclerView;
|
||||
|
||||
@BindView(R.id.fragment_list__spacer)
|
||||
public View space;
|
||||
|
||||
@BindView(R.id.fragment_list__root)
|
||||
public RelativeLayout rootView;
|
||||
|
||||
protected App app;
|
||||
protected DiasporaUrlHelper urls;
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
AppLog.d(this, "onCreateView()");
|
||||
return inflater.inflate(R.layout.recycler_list__fragment, container, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(View view, Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
ButterKnife.bind(this, view);
|
||||
app = (App) getActivity().getApplication();
|
||||
AppSettings appSettings = app.getSettings();
|
||||
urls = new DiasporaUrlHelper(appSettings);
|
||||
|
||||
followedTagsRecyclerView.setHasFixedSize(true);
|
||||
followedTagsRecyclerView.setNestedScrollingEnabled(false);
|
||||
|
||||
RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(getContext());
|
||||
followedTagsRecyclerView.setLayoutManager(layoutManager);
|
||||
|
||||
final FollowedTagsAdapter adapter = new FollowedTagsAdapter(appSettings, this);
|
||||
followedTagsRecyclerView.setAdapter(adapter);
|
||||
|
||||
//Set window title
|
||||
getActivity().setTitle(R.string.nav_followed_tags);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFragmentTag() {
|
||||
return TAG;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onBackPressed() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSomethingClicked(Object null1, Integer null2, String tag) {
|
||||
((MainActivity) getActivity()).openDiasporaUrl(urls.getSearchTagsUrl(tag));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void applyColorToViews() {
|
||||
followedTagsRecyclerView.invalidate();
|
||||
if (getAppSettings().isAmoledColorMode()) {
|
||||
rootView.setBackgroundColor(Color.BLACK);
|
||||
space.setBackgroundColor(Color.BLACK);
|
||||
}
|
||||
}
|
||||
|
||||
public static class FollowedTagsAdapter extends RecyclerView.Adapter<FollowedTagsAdapter.ViewHolder> {
|
||||
private boolean isAmoledColorMode;
|
||||
private AppSettings appSettings;
|
||||
private String[] followedTagsList;
|
||||
private List<String> followedTagsFavsList;
|
||||
private OnSomethingClickListener<Object> tagClickedListener;
|
||||
|
||||
static class ViewHolder extends RecyclerView.ViewHolder {
|
||||
@BindView(R.id.recycler_view__list_item__text)
|
||||
public TextView title;
|
||||
@BindView(R.id.recycler_view__list_item__favourite)
|
||||
AppCompatImageView favouriteImage;
|
||||
@BindView(R.id.recycler_view__list_item__root)
|
||||
RelativeLayout root;
|
||||
|
||||
ViewHolder(View v) {
|
||||
super(v);
|
||||
ButterKnife.bind(this, v);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
FollowedTagsAdapter(AppSettings appSettings, OnSomethingClickListener<Object> tagClickedListener) {
|
||||
this.appSettings = appSettings;
|
||||
this.followedTagsList = appSettings.getFollowedTags();
|
||||
this.followedTagsFavsList = new ArrayList<>(Arrays.asList(appSettings.getFollowedTagsFavs()));
|
||||
this.tagClickedListener = tagClickedListener;
|
||||
this.isAmoledColorMode = appSettings.isAmoledColorMode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return followedTagsList.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FollowedTagsAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||
View v = LayoutInflater.from(parent.getContext())
|
||||
.inflate(R.layout.recycler_list__list_item_with_fav, parent, false);
|
||||
return new ViewHolder(v);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(final ViewHolder holder, final int position) {
|
||||
// Alternating colors
|
||||
final Context c = holder.root.getContext();
|
||||
final String tag = followedTagsList[position];
|
||||
holder.title.setText(tag);
|
||||
if (position % 2 == 1) {
|
||||
holder.root.setBackgroundColor(isAmoledColorMode ? Color.BLACK : ContextUtils.get().color(R.color.alternate_row_color));
|
||||
holder.title.setTextColor(isAmoledColorMode ? Color.GRAY : Color.BLACK);
|
||||
} else {
|
||||
holder.root.setBackgroundColor(isAmoledColorMode ? Color.BLACK : Color.WHITE);
|
||||
holder.title.setTextColor(isAmoledColorMode ? Color.GRAY : Color.BLACK);
|
||||
}
|
||||
|
||||
// Favourite (Star) Image
|
||||
applyFavouriteImage(holder.favouriteImage, isFollowedTagFaved(tag));
|
||||
|
||||
// Click on fav button
|
||||
holder.favouriteImage.setOnClickListener(new View.OnClickListener() {
|
||||
public void onClick(View v) {
|
||||
if (isFollowedTagFaved(tag)) {
|
||||
followedTagsFavsList.remove(followedTagsFavsList.indexOf(tag));
|
||||
} else {
|
||||
followedTagsFavsList.add(tag);
|
||||
}
|
||||
appSettings.setFollowedTagsFavs(followedTagsFavsList);
|
||||
applyFavouriteImage(holder.favouriteImage, isFollowedTagFaved(tag));
|
||||
}
|
||||
});
|
||||
|
||||
holder.root.setOnClickListener(new View.OnClickListener() {
|
||||
public void onClick(View v) {
|
||||
tagClickedListener.onSomethingClicked(null, null, tag);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private boolean isFollowedTagFaved(String tag) {
|
||||
return followedTagsFavsList.contains(tag);
|
||||
}
|
||||
|
||||
private void applyFavouriteImage(AppCompatImageView imageView, boolean isFaved) {
|
||||
imageView.setImageResource(isFaved ? R.drawable.ic_star_filled_48px : R.drawable.ic_star_border_black_48px);
|
||||
imageView.setColorFilter(isFaved ? appSettings.getAccentColor() : (isAmoledColorMode ? Color.GRAY : 0), PorterDuff.Mode.SRC_ATOP);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -13,7 +13,7 @@ import java.util.List;
|
|||
|
||||
|
||||
/**
|
||||
* Created by gsantner (https://gsantner.github.io/ on 30.09.16.
|
||||
* Created by gsantner (gsantner AT mailbox DOT org on 30.09.16.
|
||||
* DiasporaPodList - List container for DiasporaPod's, with methods to merge with other DiasporaPodLists
|
||||
* DiasporaPod - Data container for a Pod, can include N DiasporaPodUrl's
|
||||
* DiasporaPodUrl - A Url of an DiasporaPod
|
||||
|
@ -315,8 +315,8 @@ public class DiasporaPodList implements Iterable<DiasporaPodList.DiasporaPod>, S
|
|||
}
|
||||
|
||||
/*
|
||||
* Getter & Setter
|
||||
*/
|
||||
* Getter & Setter
|
||||
*/
|
||||
public List<DiasporaPodUrl> getPodUrls() {
|
||||
return _podUrls;
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@ import org.json.JSONObject;
|
|||
|
||||
/**
|
||||
* User profile
|
||||
* Created by gsantner (https://gsantner.github.io/) on 24.03.16. Part of dandelion*.
|
||||
* Created by gsantner (gsantner AT mailbox DOT org) on 24.03.16. Part of dandelion*.
|
||||
*/
|
||||
public class DiasporaUserProfile {
|
||||
private static final int MINIMUM_USERPROFILE_LOAD_TIMEDIFF = 5000;
|
||||
|
|
|
@ -21,7 +21,7 @@ package com.github.dfa.diaspora_android.listener;
|
|||
import com.github.dfa.diaspora_android.data.DiasporaUserProfile;
|
||||
|
||||
/**
|
||||
* Created by gsantner (https://gsantner.github.io/) on 26.03.16.
|
||||
* Created by gsantner (gsantner AT mailbox DOT org) on 26.03.16.
|
||||
* Interface that needs to be implemented by classes that listen for Profile related changes
|
||||
*/
|
||||
public interface DiasporaUserProfileChangedListener {
|
||||
|
|
|
@ -24,6 +24,8 @@ import android.graphics.Bitmap;
|
|||
import android.graphics.BitmapFactory;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import net.gsantner.opoc.util.DownloadTask;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public class AvatarImageLoader {
|
||||
|
@ -52,7 +54,9 @@ public class AvatarImageLoader {
|
|||
|
||||
public void startImageDownload(ImageView imageView, String avatarUrl) {
|
||||
if (!avatarUrl.equals("")) {
|
||||
new ImageDownloadTask(imageView, avatarFile.getAbsolutePath()).execute(avatarUrl);
|
||||
new DownloadTask(new File(avatarFile.getAbsolutePath()), (ok, file) -> {
|
||||
loadToImageView(imageView);
|
||||
}).execute(avatarUrl);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -59,7 +59,7 @@ public class FetchPodsService extends Service {
|
|||
}
|
||||
|
||||
class GetPodsTask extends AsyncTask<Void, Void, DiasporaPodList> {
|
||||
private static final String PODDY_PODLIST_URL = "https://raw.githubusercontent.com/Diaspora-for-Android/dandelion/master/app/src/main/res/raw/podlist.json";
|
||||
private static final String PODDY_PODLIST_URL = "https://raw.githubusercontent.com/gsantner/dandelion/master/app/src/main/res/raw/podlist.json";
|
||||
|
||||
private final Service service;
|
||||
|
||||
|
|
|
@ -1,98 +0,0 @@
|
|||
/*
|
||||
This file is part of the dandelion*.
|
||||
|
||||
dandelion* is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
dandelion* is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with the dandelion*.
|
||||
|
||||
If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.github.dfa.diaspora_android.service;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.os.AsyncTask;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import com.github.dfa.diaspora_android.util.AppLog;
|
||||
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
|
||||
import info.guardianproject.netcipher.NetCipher;
|
||||
|
||||
/**
|
||||
* Task that can be used to download images from URLs and store them in storage
|
||||
* Created by gsantner (https://gsantner.github.io/) on 24.03.16.
|
||||
*/
|
||||
public class ImageDownloadTask extends AsyncTask<String, Void, Bitmap> {
|
||||
private final ImageView imageView;
|
||||
private String savePath;
|
||||
|
||||
/**
|
||||
* Download image from URL
|
||||
*
|
||||
* @param imageView ImageView to set image to (null = don't set)
|
||||
* @param savePath Save image to file (null = don't save)
|
||||
*/
|
||||
public ImageDownloadTask(@Nullable ImageView imageView, @Nullable String savePath) {
|
||||
this.imageView = imageView;
|
||||
this.savePath = savePath;
|
||||
}
|
||||
|
||||
protected Bitmap doInBackground(String... urls) {
|
||||
String url = urls[0];
|
||||
Bitmap bitmap = null;
|
||||
FileOutputStream out = null;
|
||||
InputStream inStream;
|
||||
HttpsURLConnection connection;
|
||||
try {
|
||||
connection = NetCipher.getHttpsURLConnection(url);
|
||||
inStream = connection.getInputStream();
|
||||
bitmap = BitmapFactory.decodeStream(inStream);
|
||||
|
||||
// Save to file if not null
|
||||
if (savePath != null) {
|
||||
out = new FileOutputStream(savePath);
|
||||
bitmap.compress(Bitmap.CompressFormat.PNG, 100, out);
|
||||
}
|
||||
|
||||
try {
|
||||
inStream.close();
|
||||
} catch (IOException e) {/*Nothing*/}
|
||||
|
||||
connection.disconnect();
|
||||
|
||||
} catch (Exception e) {
|
||||
AppLog.e(this, e.getMessage());
|
||||
} finally {
|
||||
try {
|
||||
if (out != null) {
|
||||
out.close();
|
||||
}
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
}
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
protected void onPostExecute(Bitmap result) {
|
||||
// Display on imageview if not null
|
||||
if (imageView != null) {
|
||||
imageView.setImageBitmap(result);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -71,7 +71,7 @@ public class BadgeDrawable extends Drawable {
|
|||
Rect bounds = getBounds();
|
||||
float width = bounds.right - bounds.left;
|
||||
float height = bounds.bottom - bounds.top;
|
||||
float oneDp = ContextUtils.get().dp2px(1);
|
||||
float oneDp = ContextUtils.get().convertDpToPx(1);
|
||||
|
||||
// Position the badge in the top-right quadrant of the icon.
|
||||
float radius = ((Math.max(width, height) / 2)) / 2;
|
||||
|
|
|
@ -37,7 +37,7 @@ import butterknife.OnItemSelected;
|
|||
|
||||
/**
|
||||
* Dialog that helps the user configure a pod
|
||||
* Created by gsantner (https://gsantner.github.io) on 06.10.16.
|
||||
* Created by gsantner on 06.10.16.
|
||||
*/
|
||||
public class PodSelectionDialog extends ThemedAppCompatDialogFragment {
|
||||
public static final String TAG = "com.github.dfa.diaspora_android.ui.PodSelectionDialog";
|
||||
|
|
|
@ -0,0 +1,85 @@
|
|||
package com.github.dfa.diaspora_android.ui;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
|
||||
import com.github.dfa.diaspora_android.R;
|
||||
import com.github.dfa.diaspora_android.data.DiasporaAspect;
|
||||
import com.github.dfa.diaspora_android.util.AppSettings;
|
||||
|
||||
import net.gsantner.opoc.ui.SearchOrCustomTextDialog;
|
||||
import net.gsantner.opoc.util.Callback;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
||||
public class SearchOrCustomTextDialogCreator {
|
||||
public static final String SPECIAL_PREFIX = "\uD83D\uDCA0";
|
||||
|
||||
public static void showDiasporaTagsDialog(final Activity activity, final Callback.a1<String> callback) {
|
||||
SearchOrCustomTextDialog.DialogOptions dopt = new SearchOrCustomTextDialog.DialogOptions();
|
||||
baseConf(activity, dopt);
|
||||
dopt.callback = callback;
|
||||
dopt.isSearchEnabled = true;
|
||||
dopt.searchHintText = R.string.search;
|
||||
dopt.titleText = R.string.tags;
|
||||
|
||||
new Thread(() -> {
|
||||
AppSettings appSettings = AppSettings.get();
|
||||
ArrayList<String> hl = new ArrayList<>();
|
||||
ArrayList<String> data = new ArrayList<>(Arrays.asList(appSettings.getFollowedTags()));
|
||||
if (data.size() > 0) {
|
||||
String highlighted = surroundString(data.remove(0));
|
||||
data.add(0, highlighted);
|
||||
hl.add(highlighted);
|
||||
}
|
||||
|
||||
for (int strid : new int[]{R.string.manage_hashtags}) {
|
||||
String special = surroundString(appSettings.rstr(strid));
|
||||
data.add(0, special);
|
||||
hl.add(special);
|
||||
}
|
||||
dopt.data = data;
|
||||
dopt.highlightData = hl;
|
||||
activity.runOnUiThread(() -> SearchOrCustomTextDialog.showMultiChoiceDialogWithSearchFilterUI(activity, dopt));
|
||||
}).start();
|
||||
}
|
||||
|
||||
private static String surroundString(String text) {
|
||||
return SPECIAL_PREFIX + " " + text + " ";
|
||||
}
|
||||
|
||||
|
||||
public static void showDiasporaAspectsDialog(final Activity activity, final Callback.a1<String> callback) {
|
||||
SearchOrCustomTextDialog.DialogOptions dopt = new SearchOrCustomTextDialog.DialogOptions();
|
||||
baseConf(activity, dopt);
|
||||
dopt.callback = callback;
|
||||
dopt.isSearchEnabled = false;
|
||||
dopt.titleText = R.string.contacts;
|
||||
|
||||
new Thread(() -> {
|
||||
AppSettings appSettings = AppSettings.get();
|
||||
ArrayList<String> hl = new ArrayList<>();
|
||||
ArrayList<String> data = new ArrayList<>();
|
||||
for (DiasporaAspect aspect : AppSettings.get().getAspects()) {
|
||||
data.add(aspect.name);
|
||||
}
|
||||
for (int strid : new int[]{R.string.nav_profile, R.string.manage_your_contact_list}) {
|
||||
String special = surroundString(appSettings.rstr(strid));
|
||||
data.add(0, special);
|
||||
hl.add(special);
|
||||
}
|
||||
dopt.data = data;
|
||||
dopt.highlightData = hl;
|
||||
activity.runOnUiThread(() -> SearchOrCustomTextDialog.showMultiChoiceDialogWithSearchFilterUI(activity, dopt));
|
||||
}).start();
|
||||
}
|
||||
|
||||
|
||||
private static void baseConf(Activity activity, SearchOrCustomTextDialog.DialogOptions dopt) {
|
||||
AppSettings as = new AppSettings(activity);
|
||||
dopt.isDarkDialog = as.isAmoledColorMode();
|
||||
dopt.textColor = ContextCompat.getColor(activity, dopt.isDarkDialog ? R.color.white : R.color.primary_text);
|
||||
dopt.highlightColor = as.getAccentColor();
|
||||
}
|
||||
}
|
|
@ -1,63 +0,0 @@
|
|||
/*
|
||||
This file is part of the dandelion*.
|
||||
|
||||
dandelion* is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
dandelion* is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with the dandelion*.
|
||||
|
||||
If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.github.dfa.diaspora_android.ui.theme;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.app.Fragment;
|
||||
|
||||
/**
|
||||
* Customized abstract Fragment class with some useful methods
|
||||
* Created by vanitas on 21.09.16.
|
||||
*/
|
||||
|
||||
public abstract class CustomFragment extends Fragment {
|
||||
|
||||
public static final String TAG = "com.github.dfa.diaspora_android.ui.theme.CustomFragment";
|
||||
|
||||
/**
|
||||
* We have an optionsMenu
|
||||
*
|
||||
* @param savedInstanceState state
|
||||
*/
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setHasOptionsMenu(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the tag used to identify the Fragment.
|
||||
*
|
||||
* @return tag
|
||||
*/
|
||||
public abstract String getFragmentTag();
|
||||
|
||||
/**
|
||||
* Return true if the fragment reacted to a back button press, false else.
|
||||
* In case the fragment returned false, the parent activity should handle the backPress.
|
||||
*
|
||||
* @return did we react to the back press?
|
||||
*/
|
||||
public abstract boolean onBackPressed();
|
||||
|
||||
public boolean isAllowedIntellihide() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -19,9 +19,7 @@
|
|||
package com.github.dfa.diaspora_android.ui.theme;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.ActivityManager;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.os.Build;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
|
||||
|
@ -69,15 +67,7 @@ public abstract class ThemedActivity extends AppCompatActivity {
|
|||
*/
|
||||
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
||||
private void updateRecentAppColor() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
BitmapDrawable drawable = ((BitmapDrawable) getDrawable(R.drawable.ic_launcher));
|
||||
if (drawable != null) {
|
||||
setTaskDescription(new ActivityManager.TaskDescription(
|
||||
getResources().getString(R.string.app_name),
|
||||
drawable.getBitmap(),
|
||||
getAppSettings().getPrimaryColor()));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected void updateScreenRotation() {
|
||||
|
|
|
@ -41,7 +41,7 @@ public class ThemedCheckBoxPreference extends CheckBoxPreference implements Them
|
|||
|
||||
@Override
|
||||
public void setColors() {
|
||||
CheckBox checkBox = (CheckBox) rootLayout.findViewById(android.R.id.checkbox);
|
||||
CheckBox checkBox = rootLayout.findViewById(android.R.id.checkbox);
|
||||
ThemeHelper.getInstance(AppSettings.get());
|
||||
ThemeHelper.updateCheckBoxColor(checkBox);
|
||||
}
|
||||
|
|
|
@ -38,7 +38,7 @@ public class ThemedColorPickerPreference extends Preference implements Themeable
|
|||
@Override
|
||||
protected void onBindView(View view) {
|
||||
super.onBindView(view);
|
||||
colorPreview = (ImageView) view.findViewById(android.R.id.icon);
|
||||
colorPreview = view.findViewById(android.R.id.icon);
|
||||
setColors();
|
||||
}
|
||||
|
||||
|
@ -50,7 +50,7 @@ public class ThemedColorPickerPreference extends Preference implements Themeable
|
|||
AppSettings appSettings = AppSettings.get();
|
||||
String key = getKey();
|
||||
|
||||
int color = ContextUtils.get().color(R.color.primary);
|
||||
int color = ContextUtils.get().rcolor(R.color.primary);
|
||||
if ((appSettings.isKeyEqual(key, R.string.pref_key__primary_color_shade))) {
|
||||
color = appSettings.getPrimaryColor();
|
||||
} else if ((appSettings.isKeyEqual(key, R.string.pref_key__accent_color_shade))) {
|
||||
|
|
|
@ -21,12 +21,14 @@ package com.github.dfa.diaspora_android.ui.theme;
|
|||
import com.github.dfa.diaspora_android.App;
|
||||
import com.github.dfa.diaspora_android.util.AppSettings;
|
||||
|
||||
import net.gsantner.opoc.activity.GsFragmentBase;
|
||||
|
||||
/**
|
||||
* Fragment that supports color schemes
|
||||
* Created by vanitas on 06.10.16.
|
||||
*/
|
||||
|
||||
public abstract class ThemedFragment extends CustomFragment {
|
||||
public abstract class ThemedFragment extends GsFragmentBase {
|
||||
protected AppSettings getAppSettings() {
|
||||
return ((App) getActivity().getApplication()).getSettings();
|
||||
}
|
||||
|
@ -39,4 +41,9 @@ public abstract class ThemedFragment extends CustomFragment {
|
|||
ThemeHelper.getInstance(getAppSettings());
|
||||
applyColorToViews();
|
||||
}
|
||||
|
||||
|
||||
public boolean isAllowedIntellihide() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,7 +36,7 @@ public class ThemedPreferenceCategory extends PreferenceCategory implements Them
|
|||
@Override
|
||||
protected View onCreateView(ViewGroup parent) {
|
||||
View rootLayout = super.onCreateView(parent);
|
||||
this.titleTextView = (TextView) rootLayout.findViewById(android.R.id.title);
|
||||
this.titleTextView = rootLayout.findViewById(android.R.id.title);
|
||||
setColors();
|
||||
return rootLayout;
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@ public class ThemedVisibilityPreference extends ThemedCheckBoxPreference {
|
|||
|
||||
@Override
|
||||
public void setColors() {
|
||||
CheckBox checkBox = (CheckBox) rootLayout.findViewById(android.R.id.checkbox);
|
||||
CheckBox checkBox = rootLayout.findViewById(android.R.id.checkbox);
|
||||
checkBox.setButtonDrawable(R.drawable.ic_visibility_selector);
|
||||
ThemeHelper.getInstance(AppSettings.get());
|
||||
ThemeHelper.updateCheckBoxColor(checkBox);
|
||||
|
|
|
@ -1,10 +1,14 @@
|
|||
package com.github.dfa.diaspora_android.util;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.Environment;
|
||||
import android.support.v4.content.FileProvider;
|
||||
import android.view.View;
|
||||
|
||||
import com.github.dfa.diaspora_android.BuildConfig;
|
||||
import com.github.dfa.diaspora_android.R;
|
||||
import com.github.dfa.diaspora_android.web.WebHelper;
|
||||
|
||||
|
@ -17,7 +21,7 @@ import java.util.Locale;
|
|||
|
||||
@SuppressWarnings({"WeakerAccess", "unused", "SameParameterValue"})
|
||||
public class ActivityUtils extends net.gsantner.opoc.util.ActivityUtils {
|
||||
protected ActivityUtils(Activity activity) {
|
||||
public ActivityUtils(Activity activity) {
|
||||
super(activity);
|
||||
}
|
||||
|
||||
|
@ -48,7 +52,7 @@ public class ActivityUtils extends net.gsantner.opoc.util.ActivityUtils {
|
|||
public boolean showInfoIfUserNotConnectedToInternet(View anchor) {
|
||||
boolean isOnline = WebHelper.isOnline(_context);
|
||||
if (!isOnline) {
|
||||
showSnackBar(R.string.no_internet, true);
|
||||
showSnackBar(R.string.sorry_need_to_be_connected_to_internet, true);
|
||||
}
|
||||
return !isOnline;
|
||||
}
|
||||
|
@ -70,4 +74,13 @@ public class ActivityUtils extends net.gsantner.opoc.util.ActivityUtils {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method creates file sharing uri by using FileProvider
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static Uri getFileSharingUri(Context context, File file) {
|
||||
return FileProvider.getUriForFile(context, BuildConfig.APPLICATION_ID, file);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@ public class AndroidBug5497Workaround {
|
|||
private FrameLayout.LayoutParams frameLayoutParams;
|
||||
|
||||
private AndroidBug5497Workaround(Activity activity) {
|
||||
FrameLayout content = (FrameLayout) activity.findViewById(android.R.id.content);
|
||||
FrameLayout content = activity.findViewById(android.R.id.content);
|
||||
mChildOfContent = content.getChildAt(0);
|
||||
mChildOfContent.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
|
||||
public void onGlobalLayout() {
|
||||
|
|
|
@ -18,6 +18,7 @@ import android.annotation.SuppressLint;
|
|||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.graphics.Color;
|
||||
import android.os.Environment;
|
||||
|
||||
import com.github.dfa.diaspora_android.App;
|
||||
import com.github.dfa.diaspora_android.BuildConfig;
|
||||
|
@ -26,19 +27,20 @@ import com.github.dfa.diaspora_android.data.DiasporaAspect;
|
|||
import com.github.dfa.diaspora_android.data.DiasporaPodList.DiasporaPod;
|
||||
import com.github.dfa.diaspora_android.web.ProxyHandler;
|
||||
|
||||
import net.gsantner.opoc.util.AppSettingsBase;
|
||||
import net.gsantner.opoc.preference.SharedPreferencesPropertyBackend;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Settings
|
||||
* Created by gsantner (https://gsantner.github.io/) on 20.03.16. Part of dandelion*.
|
||||
* Created by gsantner (gsantner AT mailbox DOT org) on 20.03.16. Part of dandelion*.
|
||||
*/
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
public class AppSettings extends AppSettingsBase {
|
||||
public class AppSettings extends SharedPreferencesPropertyBackend {
|
||||
private final SharedPreferences _prefPod;
|
||||
private DiasporaPod currentPod0Cached;
|
||||
|
||||
|
@ -46,7 +48,7 @@ public class AppSettings extends AppSettingsBase {
|
|||
return new AppSettings(App.get());
|
||||
}
|
||||
|
||||
private AppSettings(Context context) {
|
||||
public AppSettings(Context context) {
|
||||
super(context);
|
||||
_prefPod = _context.getSharedPreferences("pod0", Context.MODE_PRIVATE);
|
||||
}
|
||||
|
@ -145,7 +147,11 @@ public class AppSettings extends AppSettingsBase {
|
|||
}
|
||||
|
||||
public void setPodAspects(DiasporaAspect[] aspects) {
|
||||
setStringArray(R.string.pref_key__podprofile_aspects, aspects, _prefPod);
|
||||
String[] strs = new String[aspects.length];
|
||||
for (int i = 0; i < strs.length; i++) {
|
||||
strs[i] = aspects[i].toShareAbleText();
|
||||
}
|
||||
setStringArray(R.string.pref_key__podprofile_aspects, strs, _prefPod);
|
||||
}
|
||||
|
||||
public DiasporaAspect[] getAspects() {
|
||||
|
@ -341,8 +347,8 @@ public class AppSettings extends AppSettingsBase {
|
|||
return getBool(R.string.pref_key__visibility_nav__reports, false);
|
||||
}
|
||||
|
||||
public boolean isVisibleInNavDandelionAccount() {
|
||||
return getBool(R.string.pref_key__visibility_nav__dandelion_account, false);
|
||||
public boolean isVisibleInNavGsantnerAccount() {
|
||||
return getBool(R.string.pref_key__visibility_nav__gsantner_account, false);
|
||||
}
|
||||
|
||||
public boolean isVisibleInNavToggleMobileDesktop() {
|
||||
|
@ -353,6 +359,14 @@ public class AppSettings extends AppSettingsBase {
|
|||
return getBool(R.string.pref_key__topbar_stream_shortcut, false);
|
||||
}
|
||||
|
||||
public boolean isOpenYoutubeExternalEnabled() {
|
||||
return getBool(R.string.pref_key__open_youtube_external_enabled, true);
|
||||
}
|
||||
|
||||
public boolean isSwipeRefreshEnabled() {
|
||||
return getBool(R.string.pref_key__swipe_refresh_enabled, true);
|
||||
}
|
||||
|
||||
public String getScreenRotation() {
|
||||
return getString(R.string.pref_key__screen_rotation, R.string.rotation_val_system);
|
||||
}
|
||||
|
@ -363,12 +377,19 @@ public class AppSettings extends AppSettingsBase {
|
|||
return value;
|
||||
}
|
||||
|
||||
public boolean isAppCurrentVersionFirstStart() {
|
||||
public boolean isAppCurrentVersionFirstStart(boolean doSet) {
|
||||
int value = getInt(R.string.pref_key__app_first_start_current_version, -1);
|
||||
setInt(R.string.pref_key__app_first_start_current_version, BuildConfig.VERSION_CODE);
|
||||
if (doSet) {
|
||||
setInt(R.string.pref_key__app_first_start_current_version, BuildConfig.VERSION_CODE);
|
||||
}
|
||||
return value != BuildConfig.VERSION_CODE && !BuildConfig.IS_TEST_BUILD;
|
||||
}
|
||||
|
||||
public File getAppSaveDirectory() {
|
||||
return new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES) + "/dandelion");
|
||||
|
||||
}
|
||||
|
||||
public long getLastVisitedPositionInStream() {
|
||||
return getLong(R.string.pref_key__podprofile_last_stream_position, -1, _prefPod);
|
||||
}
|
||||
|
@ -431,6 +452,10 @@ public class AppSettings extends AppSettingsBase {
|
|||
return getBool(R.string.pref_key__primary_color__amoled_mode, false);
|
||||
}
|
||||
|
||||
public void setAmoledColorMode(boolean enable) {
|
||||
setBool(R.string.pref_key__primary_color__amoled_mode, enable);
|
||||
}
|
||||
|
||||
public boolean isAdBlockEnabled() {
|
||||
return getBool(R.string.pref_key__adblock_enable, true);
|
||||
}
|
||||
|
|
|
@ -62,6 +62,7 @@ public class DiasporaUrlHelper {
|
|||
public static final String SUBURL_NOTIFICATIONS_MENTIONED = "/notifications?type=mentioned";
|
||||
public static final String SUBURL_NOTIFICATIONS_RESHARED = "/notifications?type=reshared";
|
||||
public static final String SUBURL_NOTIFICATIONS_STARTED_SHARING = "/notifications?type=started_sharing";
|
||||
public static final String SUBURL_THEME = "/user/edit";
|
||||
|
||||
public DiasporaUrlHelper(AppSettings settings) {
|
||||
this.settings = settings;
|
||||
|
@ -229,6 +230,15 @@ public class DiasporaUrlHelper {
|
|||
return getPodUrl() + SUBURL_SEARCH_TAGS + query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a url that queries posts for the given hashtag query
|
||||
*
|
||||
* @return https://(pod-domain.tld)/followed_tags
|
||||
*/
|
||||
public String getAllFollowedTagsUrl() {
|
||||
return getPodUrl() + SUBURL_FOLOWED_TAGS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a url that queries user accounts for query
|
||||
*
|
||||
|
@ -344,4 +354,13 @@ public class DiasporaUrlHelper {
|
|||
}
|
||||
return app.getString(R.string.aspects);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a url that points to the settings of the pod.
|
||||
*
|
||||
* @return https://(pod-domain.tld)/user/edit
|
||||
*/
|
||||
public String getThemeUrl() {
|
||||
return getPodUrl() + SUBURL_THEME;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,19 +18,10 @@
|
|||
*/
|
||||
package com.github.dfa.diaspora_android.web;
|
||||
|
||||
import android.Manifest;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.MutableContextWrapper;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.graphics.Bitmap;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.Environment;
|
||||
import android.support.design.widget.Snackbar;
|
||||
import android.support.v7.app.AlertDialog;
|
||||
import android.view.LayoutInflater;
|
||||
import android.support.v4.widget.SwipeRefreshLayout;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.webkit.WebSettings;
|
||||
|
@ -39,21 +30,11 @@ import android.widget.ProgressBar;
|
|||
|
||||
import com.github.dfa.diaspora_android.App;
|
||||
import com.github.dfa.diaspora_android.R;
|
||||
import com.github.dfa.diaspora_android.activity.MainActivity;
|
||||
import com.github.dfa.diaspora_android.ui.theme.ThemeHelper;
|
||||
import com.github.dfa.diaspora_android.ui.theme.ThemedFragment;
|
||||
import com.github.dfa.diaspora_android.util.AppLog;
|
||||
import com.github.dfa.diaspora_android.util.AppSettings;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Fragment with a webView and a ProgressBar.
|
||||
* This Fragment retains its instance.
|
||||
|
@ -63,7 +44,6 @@ import java.util.Locale;
|
|||
public class BrowserFragment extends ThemedFragment {
|
||||
public static final String TAG = "com.github.dfa.diaspora_android.BrowserFragment";
|
||||
|
||||
protected View rootLayout;
|
||||
protected ContextMenuWebView webView;
|
||||
protected ProgressBar progressBar;
|
||||
protected AppSettings appSettings;
|
||||
|
@ -71,20 +51,14 @@ public class BrowserFragment extends ThemedFragment {
|
|||
protected WebSettings webSettings;
|
||||
|
||||
protected String pendingUrl;
|
||||
protected SwipeRefreshLayout swipe;//pull to refresh
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
AppLog.d(this, "onCreateView()");
|
||||
if (rootLayout == null) {
|
||||
LayoutInflater inflater1 = inflater.cloneInContext(new MutableContextWrapper(getContext()));
|
||||
rootLayout = inflater1.inflate(R.layout.browser__fragment, container, false);
|
||||
} else {
|
||||
MutableContextWrapper context = (MutableContextWrapper) rootLayout.getContext();
|
||||
context.setBaseContext(getContext());
|
||||
}
|
||||
return rootLayout;
|
||||
protected int getLayoutResId() {
|
||||
return R.layout.browser__fragment;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onViewCreated(View view, Bundle savedInstanceState) {
|
||||
AppLog.d(this, "onViewCreated()");
|
||||
|
@ -95,7 +69,7 @@ public class BrowserFragment extends ThemedFragment {
|
|||
}
|
||||
|
||||
if (this.webView == null) {
|
||||
this.webView = (ContextMenuWebView) view.findViewById(R.id.webView);
|
||||
this.webView = view.findViewById(R.id.webView);
|
||||
getActivity().runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
|
@ -107,7 +81,7 @@ public class BrowserFragment extends ThemedFragment {
|
|||
}
|
||||
|
||||
if (this.progressBar == null) {
|
||||
this.progressBar = (ProgressBar) view.findViewById(R.id.progressBar);
|
||||
this.progressBar = view.findViewById(R.id.progressBar);
|
||||
}
|
||||
|
||||
if (pendingUrl != null) {
|
||||
|
@ -118,14 +92,26 @@ public class BrowserFragment extends ThemedFragment {
|
|||
webView.setParentActivity(getActivity());
|
||||
|
||||
this.setRetainInstance(true);
|
||||
|
||||
//pull to refresh
|
||||
swipe = view.findViewById(R.id.swipe);
|
||||
swipe.setDistanceToTriggerSync(2000);
|
||||
swipe.setOnRefreshListener(() -> reloadUrl());
|
||||
if (appSettings.isSwipeRefreshEnabled()) {
|
||||
swipe.setEnabled(true);
|
||||
} else {
|
||||
swipe.setRefreshing(false);
|
||||
swipe.setEnabled(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
|
||||
if (getRetainInstance() && rootLayout.getParent() instanceof ViewGroup) {
|
||||
((ViewGroup) rootLayout.getParent()).removeView(rootLayout);
|
||||
if (getRetainInstance() && getView() != null && getView().getParent() instanceof ViewGroup) {
|
||||
((ViewGroup) getView().getParent()).removeView(getView());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -163,88 +149,6 @@ public class BrowserFragment extends ThemedFragment {
|
|||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("ResultOfMethodCallIgnored")
|
||||
protected boolean makeScreenshotOfWebView(boolean hasToShareScreenshot) {
|
||||
AppLog.i(this, "StreamFragment.makeScreenshotOfWebView()");
|
||||
if (android.os.Build.VERSION.SDK_INT >= 23) {
|
||||
int hasWRITE_EXTERNAL_STORAGE = getActivity().checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE);
|
||||
if (hasWRITE_EXTERNAL_STORAGE != PackageManager.PERMISSION_GRANTED) {
|
||||
if (!shouldShowRequestPermissionRationale(Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
|
||||
new AlertDialog.Builder(getContext())
|
||||
.setMessage(R.string.permissions_screenshot)
|
||||
.setNegativeButton(android.R.string.no, null)
|
||||
.setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
if (android.os.Build.VERSION.SDK_INT >= 23)
|
||||
requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
|
||||
MainActivity.REQUEST_CODE_ASK_PERMISSIONS);
|
||||
}
|
||||
})
|
||||
.show();
|
||||
return false;
|
||||
}
|
||||
requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
|
||||
MainActivity.REQUEST_CODE_ASK_PERMISSIONS);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Date dateNow = new Date();
|
||||
DateFormat dateFormat = new SimpleDateFormat("yy_MM_dd--HH_mm_ss", Locale.getDefault());
|
||||
File fileSaveDirectory = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES) + "/Diaspora");
|
||||
|
||||
String fileSaveName = hasToShareScreenshot ? ".DfA_share.jpg" : String.format("DfA_%s.jpg", dateFormat.format(dateNow));
|
||||
if (!fileSaveDirectory.exists()) {
|
||||
if (!fileSaveDirectory.mkdirs()) {
|
||||
AppLog.w(this, "Could not mkdir " + fileSaveDirectory.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasToShareScreenshot) {
|
||||
Snackbar.make(webView, getString(R.string.share__toast_screenshot) + " " + fileSaveName, Snackbar.LENGTH_LONG).show();
|
||||
}
|
||||
|
||||
Bitmap bitmap;
|
||||
webView.setDrawingCacheEnabled(true);
|
||||
bitmap = Bitmap.createBitmap(webView.getDrawingCache());
|
||||
webView.setDrawingCacheEnabled(false);
|
||||
|
||||
OutputStream bitmapWriter = null;
|
||||
try {
|
||||
bitmapWriter = new FileOutputStream(new File(fileSaveDirectory, fileSaveName));
|
||||
bitmap.compress(Bitmap.CompressFormat.JPEG, 85, bitmapWriter);
|
||||
bitmapWriter.flush();
|
||||
bitmap.recycle();
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
} finally {
|
||||
if (bitmapWriter != null) {
|
||||
try {
|
||||
bitmapWriter.close();
|
||||
} catch (IOException _ignSaveored) {/* Nothing */}
|
||||
}
|
||||
}
|
||||
|
||||
// Only show share intent when Action Share Screenshot was selected
|
||||
if (hasToShareScreenshot) {
|
||||
Intent sharingIntent = new Intent(Intent.ACTION_SEND);
|
||||
sharingIntent.setType("image/jpeg");
|
||||
sharingIntent.putExtra(Intent.EXTRA_SUBJECT, webView.getTitle());
|
||||
sharingIntent.putExtra(Intent.EXTRA_TEXT, webView.getUrl());
|
||||
Uri bmpUri = Uri.fromFile(new File(fileSaveDirectory, fileSaveName));
|
||||
sharingIntent.putExtra(Intent.EXTRA_STREAM, bmpUri);
|
||||
startActivity(Intent.createChooser(sharingIntent, getString(R.string.action_share_dotdotdot)));
|
||||
} else {
|
||||
// Broadcast that this file is indexable
|
||||
File file = new File(fileSaveDirectory, fileSaveName);
|
||||
Uri uri = Uri.fromFile(file);
|
||||
Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, uri);
|
||||
getActivity().sendBroadcast(intent);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFragmentTag() {
|
||||
return TAG;
|
||||
|
@ -302,6 +206,7 @@ public class BrowserFragment extends ThemedFragment {
|
|||
@Override
|
||||
public void run() {
|
||||
getWebView().reload();
|
||||
swipe.setRefreshing(false);//pull to refresh
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -18,29 +18,27 @@
|
|||
*/
|
||||
package com.github.dfa.diaspora_android.web;
|
||||
|
||||
import android.Manifest;
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.DownloadManager;
|
||||
import android.content.ClipData;
|
||||
import android.content.ClipboardManager;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.graphics.Bitmap;
|
||||
import android.net.Uri;
|
||||
import android.os.Environment;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.ContextMenu;
|
||||
import android.view.MenuItem;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.github.dfa.diaspora_android.BuildConfig;
|
||||
import com.github.dfa.diaspora_android.R;
|
||||
import com.github.dfa.diaspora_android.activity.MainActivity;
|
||||
import com.github.dfa.diaspora_android.service.ImageDownloadTask;
|
||||
import com.github.dfa.diaspora_android.util.AppSettings;
|
||||
|
||||
import net.gsantner.opoc.util.DownloadTask;
|
||||
import net.gsantner.opoc.util.PermissionChecker;
|
||||
import net.gsantner.opoc.util.ShareUtil;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* Subclass of WebView which adds a context menu for long clicks on images or links to share, save
|
||||
|
@ -80,106 +78,46 @@ public class ContextMenuWebView extends NestedWebView {
|
|||
public boolean onMenuItemClick(MenuItem item) {
|
||||
HitTestResult result = getHitTestResult();
|
||||
String url = result.getExtra();
|
||||
final ShareUtil shu = new ShareUtil(context);
|
||||
final PermissionChecker permc = new PermissionChecker(parentActivity);
|
||||
final AppSettings appSettings = new AppSettings(context);
|
||||
|
||||
switch (item.getItemId()) {
|
||||
//Save image to external memory
|
||||
case ID_SAVE_IMAGE: {
|
||||
boolean writeToStoragePermitted = true;
|
||||
if (android.os.Build.VERSION.SDK_INT >= 23) {
|
||||
int hasWRITE_EXTERNAL_STORAGE = parentActivity.checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE);
|
||||
if (hasWRITE_EXTERNAL_STORAGE != PackageManager.PERMISSION_GRANTED) {
|
||||
writeToStoragePermitted = false;
|
||||
if (!parentActivity.shouldShowRequestPermissionRationale(Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
|
||||
new AlertDialog.Builder(parentActivity)
|
||||
.setMessage(R.string.permissions_image)
|
||||
.setPositiveButton(context.getText(android.R.string.yes), new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
if (android.os.Build.VERSION.SDK_INT >= 23)
|
||||
parentActivity.requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
|
||||
MainActivity.REQUEST_CODE__ACCESS_EXTERNAL_STORAGE);
|
||||
}
|
||||
})
|
||||
.setNegativeButton(context.getText(android.R.string.no), null)
|
||||
.show();
|
||||
}
|
||||
parentActivity.requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
|
||||
MainActivity.REQUEST_CODE__ACCESS_EXTERNAL_STORAGE);
|
||||
}
|
||||
}
|
||||
if (writeToStoragePermitted) {
|
||||
//Make sure, Diaspora Folder exists
|
||||
File destinationFolder = new File(Environment.getExternalStorageDirectory() + "/Pictures/Diaspora");
|
||||
if (!destinationFolder.exists()) {
|
||||
destinationFolder.mkdirs();
|
||||
}
|
||||
|
||||
if (url != null) {
|
||||
Uri source = Uri.parse(url);
|
||||
if (permc.doIfExtStoragePermissionGranted(context.getString(R.string.image_permission_description__appspecific))) {
|
||||
File fileSaveDirectory = appSettings.getAppSaveDirectory();
|
||||
if (permc.mkdirIfStoragePermissionGranted(fileSaveDirectory)) {
|
||||
String filename = "dandelion-" + ShareUtil.SDF_SHORT.format(new Date()) + url.substring(url.lastIndexOf("."));
|
||||
/*Uri source = Uri.parse(url);
|
||||
DownloadManager.Request request = new DownloadManager.Request(source);
|
||||
File destinationFile = new File(Environment.getExternalStorageDirectory() + "/Pictures/Diaspora/" + System.currentTimeMillis() + ".png");
|
||||
|
||||
request.setDestinationUri(Uri.fromFile(destinationFile));
|
||||
((DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE)).enqueue(request);
|
||||
|
||||
Toast.makeText(context, context.getText(R.string.share__toast_saved_image_to_location) + " " +
|
||||
destinationFile.getAbsolutePath(), Toast.LENGTH_LONG).show();
|
||||
request.setDestinationUri(Uri.fromFile(new File(fileSaveDirectory, filename)));
|
||||
((DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE)).enqueue(request);*/
|
||||
new DownloadTask(new File(fileSaveDirectory, filename), (ok, dlfile) -> {
|
||||
if (ok) {
|
||||
Toast.makeText(context, context.getText(R.string.saving_image_to) + " " + dlfile.getName(), Toast.LENGTH_LONG).show();
|
||||
}
|
||||
}).execute(url);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case ID_SHARE_IMAGE:
|
||||
if (url != null) {
|
||||
boolean writeToStoragePermitted = true;
|
||||
if (android.os.Build.VERSION.SDK_INT >= 23) {
|
||||
int hasWRITE_EXTERNAL_STORAGE = parentActivity.checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE);
|
||||
if (hasWRITE_EXTERNAL_STORAGE != PackageManager.PERMISSION_GRANTED) {
|
||||
writeToStoragePermitted = false;
|
||||
if (!parentActivity.shouldShowRequestPermissionRationale(Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
|
||||
new AlertDialog.Builder(parentActivity)
|
||||
.setMessage(R.string.permissions_image)
|
||||
.setPositiveButton(context.getText(android.R.string.yes), new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
if (android.os.Build.VERSION.SDK_INT >= 23)
|
||||
parentActivity.requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
|
||||
MainActivity.REQUEST_CODE__ACCESS_EXTERNAL_STORAGE);
|
||||
}
|
||||
})
|
||||
.setNegativeButton(context.getText(android.R.string.no), null)
|
||||
.show();
|
||||
} else {
|
||||
parentActivity.requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
|
||||
MainActivity.REQUEST_CODE__ACCESS_EXTERNAL_STORAGE);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (writeToStoragePermitted) {
|
||||
//Make sure, Diaspora Folder exists
|
||||
File destinationFolder = new File(Environment.getExternalStorageDirectory() + "/Pictures/Diaspora");
|
||||
if (!destinationFolder.exists()) {
|
||||
destinationFolder.mkdirs();
|
||||
}
|
||||
|
||||
final Uri local = Uri.parse(Environment.getExternalStorageDirectory() + "/Pictures/Diaspora/" + System.currentTimeMillis() + ".png");
|
||||
new ImageDownloadTask(null, local.getPath()) {
|
||||
@Override
|
||||
protected void onPostExecute(Bitmap result) {
|
||||
Uri myUri = Uri.fromFile(new File(local.getPath()));
|
||||
Intent sharingIntent = new Intent();
|
||||
sharingIntent.setAction(Intent.ACTION_SEND);
|
||||
sharingIntent.putExtra(Intent.EXTRA_STREAM, myUri);
|
||||
sharingIntent.setType("image/png");
|
||||
sharingIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||
context.startActivity(Intent.createChooser(sharingIntent, getResources().getString(R.string.action_share_dotdotdot)));
|
||||
}
|
||||
}.execute(url);
|
||||
}
|
||||
} else {
|
||||
Toast.makeText(context, "Cannot share image: url is null", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case ID_SHARE_IMAGE: {
|
||||
if (permc.doIfExtStoragePermissionGranted(context.getString(R.string.image_permission_description__appspecific))) {
|
||||
File fileSaveDirectory = appSettings.getAppSaveDirectory();
|
||||
if (permc.mkdirIfStoragePermissionGranted(fileSaveDirectory)) {
|
||||
String filename = ".dandelion-shared" + url.substring(url.lastIndexOf("."));
|
||||
new DownloadTask(new File(fileSaveDirectory, filename), (ok, dlfile) -> {
|
||||
if (ok) {
|
||||
Toast.makeText(context, context.getText(R.string.saving_image_to) + " " + dlfile.getName(), Toast.LENGTH_LONG).show();
|
||||
shu.shareStream(dlfile, "image/" + dlfile.getAbsolutePath().lastIndexOf(".") + 1);
|
||||
}
|
||||
}).execute(url);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case ID_IMAGE_EXTERNAL_BROWSER:
|
||||
if (url != null) {
|
||||
|
@ -194,7 +132,7 @@ public class ContextMenuWebView extends NestedWebView {
|
|||
if (url != null) {
|
||||
ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
|
||||
clipboard.setPrimaryClip(ClipData.newPlainText("text", url));
|
||||
Toast.makeText(context, R.string.share__toast_link_address_copied, Toast.LENGTH_SHORT).show();
|
||||
Toast.makeText(context, R.string.link_adress_copied, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -206,7 +144,7 @@ public class ContextMenuWebView extends NestedWebView {
|
|||
sendIntent.putExtra(Intent.EXTRA_TEXT, url);
|
||||
sendIntent.setType("text/plain");
|
||||
context.startActivity(Intent.createChooser(sendIntent, getResources()
|
||||
.getText(R.string.context_menu_share_link)));
|
||||
.getText(R.string.share_link_address)));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -219,16 +157,16 @@ public class ContextMenuWebView extends NestedWebView {
|
|||
result.getType() == HitTestResult.SRC_IMAGE_ANCHOR_TYPE) {
|
||||
// Menu options for an image.
|
||||
menu.setHeaderTitle(result.getExtra());
|
||||
menu.add(0, ID_SAVE_IMAGE, 0, context.getString(R.string.context_menu_save_image)).setOnMenuItemClickListener(handler);
|
||||
menu.add(0, ID_IMAGE_EXTERNAL_BROWSER, 0, context.getString(R.string.context_menu_open_external_browser)).setOnMenuItemClickListener(handler);
|
||||
menu.add(0, ID_SHARE_IMAGE, 0, context.getString(R.string.context_menu_share_image)).setOnMenuItemClickListener(handler);
|
||||
menu.add(0, ID_COPY_IMAGE_LINK, 0, context.getString(R.string.context_menu_copy_image_link)).setOnMenuItemClickListener(handler);
|
||||
menu.add(0, ID_SAVE_IMAGE, 0, context.getString(R.string.save_image)).setOnMenuItemClickListener(handler);
|
||||
menu.add(0, ID_IMAGE_EXTERNAL_BROWSER, 0, context.getString(R.string.open_in_external_browser)).setOnMenuItemClickListener(handler);
|
||||
menu.add(0, ID_SHARE_IMAGE, 0, context.getString(R.string.share_image)).setOnMenuItemClickListener(handler);
|
||||
menu.add(0, ID_COPY_IMAGE_LINK, 0, context.getString(R.string.copy_image_address_to_clipboard)).setOnMenuItemClickListener(handler);
|
||||
} else if (result.getType() == HitTestResult.ANCHOR_TYPE ||
|
||||
result.getType() == HitTestResult.SRC_ANCHOR_TYPE) {
|
||||
// Menu options for a hyperlink.
|
||||
menu.setHeaderTitle(result.getExtra());
|
||||
menu.add(0, ID_COPY_LINK, 0, context.getString(R.string.context_menu_copy_link)).setOnMenuItemClickListener(handler);
|
||||
menu.add(0, ID_SHARE_LINK, 0, context.getString(R.string.context_menu_share_link)).setOnMenuItemClickListener(handler);
|
||||
menu.add(0, ID_COPY_LINK, 0, context.getString(R.string.copy_link_adress_to_clipboard)).setOnMenuItemClickListener(handler);
|
||||
menu.add(0, ID_SHARE_LINK, 0, context.getString(R.string.share_link_address)).setOnMenuItemClickListener(handler);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@ package com.github.dfa.diaspora_android.web;
|
|||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.support.v4.content.LocalBroadcastManager;
|
||||
import android.webkit.CookieManager;
|
||||
|
@ -38,6 +39,7 @@ public class CustomWebViewClient extends WebViewClient {
|
|||
private final App app;
|
||||
private String lastLoadUrl = "";
|
||||
private boolean isAdBlockEnabled = false;
|
||||
AppSettings appSettings = AppSettings.get();
|
||||
|
||||
public CustomWebViewClient(App app, WebView webView) {
|
||||
this.app = app;
|
||||
|
@ -56,6 +58,11 @@ public class CustomWebViewClient extends WebViewClient {
|
|||
|| (host != null && (url.startsWith("https://" + host)
|
||||
|| url.startsWith("http://" + host)))) {
|
||||
return false;
|
||||
}//make youtube links open external-->never customtab
|
||||
else if (appSettings.isOpenYoutubeExternalEnabled() && (url.startsWith("https://youtube.com/") || url.startsWith("https://www.youtube.com/") || url.startsWith("https://m.youtube.com/") || url.startsWith("https://youtu.be/"))) {
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
|
||||
view.getContext().startActivity(intent);
|
||||
return true;
|
||||
} else {
|
||||
Intent i = new Intent(MainActivity.ACTION_OPEN_EXTERNAL_URL);
|
||||
i.putExtra(MainActivity.EXTRA_URL, url);
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
*/
|
||||
package com.github.dfa.diaspora_android.web;
|
||||
|
||||
import android.content.DialogInterface;
|
||||
import android.webkit.JsResult;
|
||||
import android.webkit.WebView;
|
||||
import android.widget.ProgressBar;
|
||||
|
@ -65,20 +64,13 @@ public class DiasporaStreamWebChromeClient extends FileUploadWebChromeClient {
|
|||
ThemedAlertDialogBuilder builder = new ThemedAlertDialogBuilder(view.getContext(), AppSettings.get());
|
||||
builder.setTitle(view.getContext().getString(R.string.confirmation))
|
||||
.setMessage(message)
|
||||
.setPositiveButton(android.R.string.ok,
|
||||
new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
result.confirm();
|
||||
}
|
||||
})
|
||||
.setNegativeButton(android.R.string.cancel,
|
||||
new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
result.cancel();
|
||||
}
|
||||
})
|
||||
.create()
|
||||
.show();
|
||||
.setPositiveButton(android.R.string.ok, (dialog, which) -> result.confirm())
|
||||
.setNegativeButton(android.R.string.cancel, (dialog, which) -> result.cancel())
|
||||
.setOnCancelListener(dialog -> {
|
||||
result.cancel();
|
||||
dialog.dismiss();
|
||||
})
|
||||
.create().show();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -32,7 +32,6 @@ import com.github.dfa.diaspora_android.activity.MainActivity;
|
|||
|
||||
/**
|
||||
* Created by Gregor Santner on 07.08.16.
|
||||
* https://gsantner.github.io
|
||||
*/
|
||||
public class WebHelper {
|
||||
|
||||
|
|
|
@ -41,6 +41,9 @@ public class CustomTabsHelper {
|
|||
static final String BETA_PACKAGE = "com.chrome.beta";
|
||||
static final String DEV_PACKAGE = "com.chrome.dev";
|
||||
static final String LOCAL_PACKAGE = "com.google.android.apps.chrome";
|
||||
static final String CHROMIUM = "org.chromium.chrome";
|
||||
static final String FENNEC = "org.mozilla.fennec_fdroid";
|
||||
static final String KLAR = "org.mozilla.klar";
|
||||
private static final String EXTRA_CUSTOM_TABS_KEEP_ALIVE =
|
||||
"android.support.customtabs.extra.KEEP_ALIVE";
|
||||
|
||||
|
@ -101,6 +104,12 @@ public class CustomTabsHelper {
|
|||
sPackageNameToUse = DEV_PACKAGE;
|
||||
} else if (packagesSupportingCustomTabs.contains(LOCAL_PACKAGE)) {
|
||||
sPackageNameToUse = LOCAL_PACKAGE;
|
||||
} else if (packagesSupportingCustomTabs.contains(CHROMIUM)) {
|
||||
sPackageNameToUse = CHROMIUM;
|
||||
} else if (packagesSupportingCustomTabs.contains(FENNEC)) {
|
||||
sPackageNameToUse = FENNEC;
|
||||
} else if (packagesSupportingCustomTabs.contains(KLAR)) {
|
||||
sPackageNameToUse = KLAR;
|
||||
}
|
||||
return sPackageNameToUse;
|
||||
}
|
||||
|
@ -137,6 +146,6 @@ public class CustomTabsHelper {
|
|||
* @return All possible chrome package names that provide custom tabs feature.
|
||||
*/
|
||||
public static String[] getPackages() {
|
||||
return new String[]{"", STABLE_PACKAGE, BETA_PACKAGE, DEV_PACKAGE, LOCAL_PACKAGE};
|
||||
return new String[]{"", STABLE_PACKAGE, BETA_PACKAGE, DEV_PACKAGE, LOCAL_PACKAGE, CHROMIUM, FENNEC, KLAR};
|
||||
}
|
||||
}
|
||||
|
|
160
app/src/main/java/net/gsantner/opoc/activity/GsFragmentBase.java
Normal file
|
@ -0,0 +1,160 @@
|
|||
/*#######################################################
|
||||
*
|
||||
* Maintained 2017-2023 by Gregor Santner <gsantner AT mailbox DOT org>
|
||||
*
|
||||
* License: Apache 2.0
|
||||
* https://github.com/gsantner/opoc/#licensing
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
#########################################################*/
|
||||
package net.gsantner.opoc.activity;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.LayoutRes;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import net.gsantner.opoc.android.dummy.MenuItemDummy;
|
||||
import net.gsantner.opoc.util.ContextUtils;
|
||||
|
||||
import butterknife.ButterKnife;
|
||||
|
||||
/**
|
||||
* A common base fragment to extend from
|
||||
*/
|
||||
public abstract class GsFragmentBase extends Fragment {
|
||||
private boolean _fragmentFirstTimeVisible = true;
|
||||
private final Object _fragmentFirstTimeVisibleSync = new Object();
|
||||
|
||||
protected ContextUtils _cu;
|
||||
protected Bundle _savedInstanceState = null;
|
||||
protected Menu _fragmentMenu = new MenuItemDummy.Menu();
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setHasOptionsMenu(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inflate the fragments layout. Don't override this method, just supply the needed
|
||||
* {@link LayoutRes} via abstract method {@link #getLayoutResId()}, super does the rest
|
||||
*/
|
||||
@Deprecated
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
_cu = new ContextUtils(inflater.getContext());
|
||||
_cu.setAppLanguage(getAppLanguage());
|
||||
_savedInstanceState = savedInstanceState;
|
||||
if (getLayoutResId() == 0) {
|
||||
Log.e(getClass().getCanonicalName(), "Error: GsFragmentbase.onCreateview: Returned 0 for getLayoutResId");
|
||||
}
|
||||
View view = inflater.inflate(getLayoutResId(), container, false);
|
||||
ButterKnife.bind(this, view);
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
view.postDelayed(() -> {
|
||||
synchronized (_fragmentFirstTimeVisibleSync) {
|
||||
if (getUserVisibleHint() && isVisible() && _fragmentFirstTimeVisible) {
|
||||
_fragmentFirstTimeVisible = false;
|
||||
onFragmentFirstTimeVisible();
|
||||
}
|
||||
}
|
||||
}, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a tag from the fragment, allows faster distinction
|
||||
*
|
||||
* @return This fragments tag
|
||||
*/
|
||||
public abstract String getFragmentTag();
|
||||
|
||||
|
||||
/**
|
||||
* Get the layout to be inflated in the fragment
|
||||
*
|
||||
* @return Layout resource id
|
||||
*/
|
||||
@LayoutRes
|
||||
protected abstract int getLayoutResId();
|
||||
|
||||
/**
|
||||
* Event to be called when the back button was pressed
|
||||
* True should be returned when this was handled by the fragment
|
||||
* and no further handling in the view hierarchy is needed
|
||||
*
|
||||
* @return True if back handled by fragment
|
||||
*/
|
||||
public boolean onBackPressed() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the language to be used in this fragment
|
||||
* Defaults to resolve the language from sharedpreferences: pref_key__language
|
||||
*
|
||||
* @return Empty string for system language, or an android locale code
|
||||
*/
|
||||
public String getAppLanguage() {
|
||||
if (getContext() != null) {
|
||||
return getContext().getSharedPreferences("app", Context.MODE_PRIVATE)
|
||||
.getString("pref_key__language", "");
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* This will be called when this fragment gets the first time visible
|
||||
*/
|
||||
public void onFragmentFirstTimeVisible() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUserVisibleHint(boolean isVisibleToUser) {
|
||||
super.setUserVisibleHint(isVisibleToUser);
|
||||
synchronized (_fragmentFirstTimeVisibleSync) {
|
||||
if (isVisibleToUser && _fragmentFirstTimeVisible) {
|
||||
_fragmentFirstTimeVisible = false;
|
||||
onFragmentFirstTimeVisible();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||
super.onCreateOptionsMenu(menu, inflater);
|
||||
_fragmentMenu = menu;
|
||||
}
|
||||
|
||||
public Menu getFragmentMenu() {
|
||||
return _fragmentMenu;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the toolbar from activity
|
||||
* Requires id to be set to @+id/toolbar
|
||||
*/
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
protected Toolbar getToolbar() {
|
||||
try {
|
||||
return (Toolbar) getActivity().findViewById(new ContextUtils(getActivity()).getResId(ContextUtils.ResType.ID, "toolbar"));
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,351 @@
|
|||
/*
|
||||
* Maintained 2017-2023 by Gregor Santner <gsantner AT mailbox DOT org>
|
||||
* License: Creative Commons Zero (CC0 1.0) / Public Domain
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/
|
||||
*
|
||||
* You can do whatever you want with this. If we meet some day, and you think it is worth it,
|
||||
* you can buy me a drink in return. Provided as is without any kind of warranty. Do not blame
|
||||
* or ask for support if something goes wrong. - Gregor Santner
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
package net.gsantner.opoc.android.dummy;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.content.Intent;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.view.ActionProvider;
|
||||
import android.view.ContextMenu;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.MenuItem;
|
||||
import android.view.SubMenu;
|
||||
import android.view.View;
|
||||
|
||||
public class MenuItemDummy implements MenuItem {
|
||||
private final int _itemId;
|
||||
|
||||
public MenuItemDummy(final int itemId) {
|
||||
_itemId = itemId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemId() {
|
||||
return _itemId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getGroupId() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MenuItem setTitle(CharSequence title) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MenuItem setTitle(int title) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence getTitle() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MenuItem setTitleCondensed(CharSequence title) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence getTitleCondensed() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MenuItem setIcon(Drawable icon) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MenuItem setIcon(int iconRes) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Drawable getIcon() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MenuItem setIntent(Intent intent) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Intent getIntent() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MenuItem setShortcut(char numericChar, char alphaChar) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MenuItem setNumericShortcut(char numericChar) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public char getNumericShortcut() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MenuItem setAlphabeticShortcut(char alphaChar) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public char getAlphabeticShortcut() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MenuItem setCheckable(boolean checkable) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCheckable() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MenuItem setChecked(boolean checked) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isChecked() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MenuItem setVisible(boolean visible) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isVisible() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MenuItem setEnabled(boolean enabled) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasSubMenu() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SubMenu getSubMenu() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MenuItem setOnMenuItemClickListener(OnMenuItemClickListener menuItemClickListener) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ContextMenu.ContextMenuInfo getMenuInfo() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setShowAsAction(int actionEnum) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public MenuItem setShowAsActionFlags(int actionEnum) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MenuItem setActionView(View view) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MenuItem setActionView(int resId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getActionView() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MenuItem setActionProvider(ActionProvider actionProvider) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionProvider getActionProvider() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean expandActionView() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean collapseActionView() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isActionViewExpanded() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MenuItem setOnActionExpandListener(OnActionExpandListener listener) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public static class Menu implements android.view.Menu {
|
||||
@Override
|
||||
public MenuItem add(CharSequence title) {
|
||||
return add(0, 0, 0, "");
|
||||
}
|
||||
|
||||
@Override
|
||||
public MenuItem add(int titleRes) {
|
||||
return add(0, 0, 0, "");
|
||||
}
|
||||
|
||||
@Override
|
||||
public MenuItem add(int groupId, int itemId, int order, CharSequence title) {
|
||||
return new MenuItemDummy(itemId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MenuItem add(int groupId, int itemId, int order, int titleRes) {
|
||||
return add(0, 0, 0, "");
|
||||
}
|
||||
|
||||
@Override
|
||||
public SubMenu addSubMenu(CharSequence title) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SubMenu addSubMenu(int titleRes) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SubMenu addSubMenu(int groupId, int itemId, int order, CharSequence title) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SubMenu addSubMenu(int groupId, int itemId, int order, int titleRes) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int addIntentOptions(int groupId, int itemId, int order, ComponentName caller, Intent[] specifics, Intent intent, int flags, MenuItem[] outSpecificItems) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeItem(int id) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeGroup(int groupId) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setGroupCheckable(int group, boolean checkable, boolean exclusive) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setGroupVisible(int group, boolean visible) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setGroupEnabled(int group, boolean enabled) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasVisibleItems() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MenuItem findItem(int id) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MenuItem getItem(int index) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean performShortcut(int keyCode, KeyEvent event, int flags) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isShortcutKey(int keyCode, KeyEvent event) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean performIdentifierAction(int id, int flags) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setQwertyMode(boolean isQwerty) {
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* Maintained 2017-2023 by Gregor Santner <gsantner AT mailbox DOT org>
|
||||
* License: Creative Commons Zero (CC0 1.0) / Public Domain
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/
|
||||
*
|
||||
* You can do whatever you want with this. If we meet some day, and you think it is worth it,
|
||||
* you can buy me a drink in return. Provided as is without any kind of warranty. Do not blame
|
||||
* or ask for support if something goes wrong. - Gregor Santner
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
package net.gsantner.opoc.android.dummy;
|
||||
|
||||
import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
|
||||
import net.gsantner.opoc.util.Callback;
|
||||
|
||||
@SuppressWarnings({"unused", "SpellCheckingInspection"})
|
||||
public class TextWatcherDummy implements TextWatcher {
|
||||
@Override
|
||||
public void beforeTextChanged(final CharSequence s, final int start, final int count, final int after) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(final CharSequence s, final int start, final int before, final int count) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(final Editable s) {
|
||||
}
|
||||
|
||||
public static TextWatcher before(final Callback.a4<CharSequence, Integer, Integer, Integer> impl) {
|
||||
return new TextWatcherDummy() {
|
||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
||||
impl.callback(s, start, count, after);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static TextWatcher on(final Callback.a4<CharSequence, Integer, Integer, Integer> impl) {
|
||||
return new TextWatcherDummy() {
|
||||
public void onTextChanged(final CharSequence s, final int start, final int before, final int count) {
|
||||
impl.callback(s, start, before, count);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static TextWatcher after(final Callback.a1<Editable> impl) {
|
||||
return new TextWatcherDummy() {
|
||||
public void afterTextChanged(final Editable s) {
|
||||
impl.callback(s);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
|
@ -1,20 +1,14 @@
|
|||
/*
|
||||
* ------------------------------------------------------------------------------
|
||||
* Gregor Santner <gsantner.github.io> wrote this. You can do whatever you want
|
||||
* with it. If we meet some day, and you think it is worth it, you can buy me a
|
||||
* coke in return. Provided as is without any kind of warranty. Do not blame or
|
||||
* sue me if something goes wrong. No attribution required. - Gregor Santner
|
||||
/*#######################################################
|
||||
*
|
||||
* License: Creative Commons Zero (CC0 1.0)
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/
|
||||
* ----------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/*
|
||||
* Get updates:
|
||||
* https://github.com/gsantner/onePieceOfCode/blob/master/java/SimpleMarkdownParser.java
|
||||
* Apply to TextView:
|
||||
* See https://github.com/gsantner/onePieceOfCode/blob/master/android/ContextUtils.java
|
||||
* Maintained 2018-2023 by Gregor Santner <gsantner AT mailbox DOT org>
|
||||
*
|
||||
* License: Apache 2.0
|
||||
* https://github.com/gsantner/opoc/#licensing
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
#########################################################*/
|
||||
|
||||
/*
|
||||
* Parses most common markdown tags. Only inline tags are supported, multiline/block syntax
|
||||
* is not supported (citation, multiline code, ..). This is intended to stay as easy as possible.
|
||||
*
|
||||
|
@ -27,7 +21,7 @@
|
|||
* FILTER_WEB is intended to be used at engines understanding most common HTML tags.
|
||||
*/
|
||||
|
||||
package net.gsantner.opoc.util;
|
||||
package net.gsantner.opoc.format.markdown;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.FileInputStream;
|
||||
|
@ -38,7 +32,7 @@ import java.io.InputStreamReader;
|
|||
/**
|
||||
* Simple Markdown Parser
|
||||
*/
|
||||
@SuppressWarnings({"WeakerAccess", "CaughtExceptionImmediatelyRethrown", "SameParameterValue", "unused", "SpellCheckingInspection", "RepeatedSpace", "SingleCharAlternation"})
|
||||
@SuppressWarnings({"WeakerAccess", "CaughtExceptionImmediatelyRethrown", "SameParameterValue", "unused", "SpellCheckingInspection", "RepeatedSpace", "SingleCharAlternation", "Convert2Lambda"})
|
||||
public class SimpleMarkdownParser {
|
||||
//########################
|
||||
//## Statics
|
||||
|
@ -98,8 +92,8 @@ public class SimpleMarkdownParser {
|
|||
.replaceAll("!\\[(.*?)\\]\\((.*?)\\)", "<img src=\\'$2\\' alt='$1' />") // img
|
||||
.replaceAll("<(http|https):\\/\\/(.*)>", "<a href='$1://$2'>$1://$2</a>") // a href (DEP: img)
|
||||
.replaceAll("\\[(.*?)\\]\\((.*?)\\)", "<a href=\\'$2\\'>$1</a>") // a href (DEP: img)
|
||||
.replaceAll("(?m)^([-*] )(.*)$", "<font color='#000001'>•</font> $2 ") // unordered list + end line
|
||||
.replaceAll("(?m)^ (-|\\*) ([^<]*)$", " <font color='#000001'>•</font> $2 ") // unordered list2 + end line
|
||||
.replaceAll("(?m)^[-*] (.*)$", "<font color='#000001'>•</font> $1 ") // unordered list + end line
|
||||
.replaceAll("(?m)^ [-*] (.*)$", " <font color='#000001'>•</font> $1 ") // unordered list2 + end line
|
||||
.replaceAll("`([^<]*)`", "<code>$1</code>") // code
|
||||
.replace("\\*", "●") // temporary replace escaped star symbol
|
||||
.replaceAll("(?m)\\*\\*(.*)\\*\\*", "<b>$1</b>") // bold (DEP: temp star)
|
||||
|
@ -116,6 +110,7 @@ public class SimpleMarkdownParser {
|
|||
public String filter(String text) {
|
||||
text = text
|
||||
.replace("New:", "<font color='#276230'>New:</font>")
|
||||
.replace("New features:", "<font color='#276230'>New:</font>")
|
||||
.replace("Added:", "<font color='#276230'>Added:</font>")
|
||||
.replace("Add:", "<font color='#276230'>Add:</font>")
|
||||
.replace("Fixed:", "<font color='#005688'>Fixed:</font>")
|
||||
|
@ -129,6 +124,26 @@ public class SimpleMarkdownParser {
|
|||
return text;
|
||||
}
|
||||
};
|
||||
public final static SmpFilter FILTER_H_TO_SUP = new SmpFilter() {
|
||||
@Override
|
||||
public String filter(String text) {
|
||||
text = text
|
||||
.replace("<h1>", "<sup><sup><sup>")
|
||||
.replace("</h1>", "</sup></sup></sup>")
|
||||
.replace("<h2>", "<sup><sup>")
|
||||
.replace("</h2>", "</sup></sup>")
|
||||
.replace("<h3>", "<sup>")
|
||||
.replace("</h3>", "</sup>")
|
||||
;
|
||||
return text;
|
||||
}
|
||||
};
|
||||
public final static SmpFilter FILTER_NONE = new SmpFilter() {
|
||||
@Override
|
||||
public String filter(String text) {
|
||||
return text;
|
||||
}
|
||||
};
|
||||
|
||||
//########################
|
||||
//## Singleton
|
|
@ -0,0 +1,47 @@
|
|||
/*#######################################################
|
||||
*
|
||||
* Maintained 2018-2023 by Gregor Santner <gsantner AT mailbox DOT org>
|
||||
*
|
||||
* License: Apache 2.0
|
||||
* https://github.com/gsantner/opoc/#licensing
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
#########################################################*/
|
||||
package net.gsantner.opoc.preference;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@SuppressWarnings({"UnusedReturnValue", "SpellCheckingInspection", "unused", "SameParameterValue"})
|
||||
public interface PropertyBackend<TKEY, TTHIS> {
|
||||
String getString(TKEY key, String defaultValue);
|
||||
|
||||
int getInt(TKEY key, int defaultValue);
|
||||
|
||||
long getLong(TKEY key, long defaultValue);
|
||||
|
||||
boolean getBool(TKEY key, boolean defaultValue);
|
||||
|
||||
float getFloat(TKEY key, float defaultValue);
|
||||
|
||||
double getDouble(TKEY key, double defaultValue);
|
||||
|
||||
List<Integer> getIntList(TKEY key);
|
||||
|
||||
List<String> getStringList(TKEY key);
|
||||
|
||||
TTHIS setString(TKEY key, String value);
|
||||
|
||||
TTHIS setInt(TKEY key, int value);
|
||||
|
||||
TTHIS setLong(TKEY key, long value);
|
||||
|
||||
TTHIS setBool(TKEY key, boolean value);
|
||||
|
||||
TTHIS setFloat(TKEY key, float value);
|
||||
|
||||
TTHIS setDouble(TKEY key, double value);
|
||||
|
||||
TTHIS setIntList(TKEY key, List<Integer> value);
|
||||
|
||||
TTHIS setStringList(TKEY key, List<String> value);
|
||||
}
|
|
@ -0,0 +1,617 @@
|
|||
/*#######################################################
|
||||
*
|
||||
* Maintained 2016-2023 by Gregor Santner <gsantner AT mailbox DOT org>
|
||||
*
|
||||
* License: Apache 2.0
|
||||
* https://github.com/gsantner/opoc/#licensing
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
#########################################################*/
|
||||
|
||||
/*
|
||||
* This is a wrapper for settings based on SharedPreferences
|
||||
* with keys in resources. Extend from this class and add
|
||||
* getters/setters for the app's settings.
|
||||
* Example:
|
||||
public boolean isAppFirstStart(boolean doSet) {
|
||||
int value = getInt(R.string.pref_key__app_first_start, -1);
|
||||
if (doSet) {
|
||||
setBool(true);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
public boolean isAppCurrentVersionFirstStart(boolean doSet) {
|
||||
int value = getInt(R.string.pref_key__app_first_start_current_version, -1);
|
||||
if (doSet) {
|
||||
setInt(R.string.pref_key__app_first_start_current_version, BuildConfig.VERSION_CODE);
|
||||
}
|
||||
return value != BuildConfig.VERSION_CODE;
|
||||
}
|
||||
*/
|
||||
|
||||
package net.gsantner.opoc.preference;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.support.annotation.ColorRes;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.StringRes;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
/**
|
||||
* Wrapper for settings based on SharedPreferences, optionally with keys in resources
|
||||
* Default SharedPreference (_prefApp) will be taken if no SP is specified, else the first one
|
||||
*/
|
||||
@SuppressWarnings({"WeakerAccess", "unused", "SpellCheckingInspection", "SameParameterValue"})
|
||||
public class SharedPreferencesPropertyBackend implements PropertyBackend<String, SharedPreferencesPropertyBackend> {
|
||||
protected static final String ARRAY_SEPARATOR = "%%%";
|
||||
protected static final String ARRAY_SEPARATOR_SUBSTITUTE = "§§§";
|
||||
public static final String SHARED_PREF_APP = "app";
|
||||
private static String _debugLog = "";
|
||||
|
||||
|
||||
//
|
||||
// Members, Constructors
|
||||
//
|
||||
protected final SharedPreferences _prefApp;
|
||||
protected final String _prefAppName;
|
||||
protected final Context _context;
|
||||
|
||||
public SharedPreferencesPropertyBackend(final Context context) {
|
||||
this(context, SHARED_PREF_APP);
|
||||
}
|
||||
|
||||
public SharedPreferencesPropertyBackend(final Context context, final String prefAppName) {
|
||||
_context = context.getApplicationContext();
|
||||
_prefAppName = TextUtils.isEmpty(prefAppName) ?
|
||||
_context.getPackageName() + "_preferences" : prefAppName;
|
||||
_prefApp = _context.getSharedPreferences(_prefAppName, Context.MODE_PRIVATE);
|
||||
}
|
||||
|
||||
//
|
||||
// Methods
|
||||
//
|
||||
public Context getContext() {
|
||||
return _context;
|
||||
}
|
||||
|
||||
public boolean isKeyEqual(String key, int stringKeyResourceId) {
|
||||
return key.equals(rstr(stringKeyResourceId));
|
||||
}
|
||||
|
||||
public void resetSettings() {
|
||||
resetSettings(_prefApp);
|
||||
}
|
||||
|
||||
@SuppressLint("ApplySharedPref")
|
||||
public void resetSettings(final SharedPreferences pref) {
|
||||
pref.edit().clear().commit();
|
||||
}
|
||||
|
||||
public boolean isPrefSet(@StringRes int stringKeyResourceId) {
|
||||
return isPrefSet(_prefApp, stringKeyResourceId);
|
||||
}
|
||||
|
||||
public boolean isPrefSet(final SharedPreferences pref, @StringRes int stringKeyResourceId) {
|
||||
return pref.contains(rstr(stringKeyResourceId));
|
||||
}
|
||||
|
||||
public void registerPreferenceChangedListener(SharedPreferences.OnSharedPreferenceChangeListener value) {
|
||||
registerPreferenceChangedListener(_prefApp, value);
|
||||
}
|
||||
|
||||
public void registerPreferenceChangedListener(final SharedPreferences pref, SharedPreferences.OnSharedPreferenceChangeListener value) {
|
||||
pref.registerOnSharedPreferenceChangeListener(value);
|
||||
}
|
||||
|
||||
public void unregisterPreferenceChangedListener(SharedPreferences.OnSharedPreferenceChangeListener value) {
|
||||
unregisterPreferenceChangedListener(_prefApp, value);
|
||||
}
|
||||
|
||||
public void unregisterPreferenceChangedListener(final SharedPreferences pref, SharedPreferences.OnSharedPreferenceChangeListener value) {
|
||||
pref.unregisterOnSharedPreferenceChangeListener(value);
|
||||
}
|
||||
|
||||
public SharedPreferences getDefaultPreferences() {
|
||||
return _prefApp;
|
||||
}
|
||||
|
||||
public SharedPreferences.Editor getDefaultPreferencesEditor() {
|
||||
return _prefApp.edit();
|
||||
}
|
||||
|
||||
public String getDefaultPreferencesName() {
|
||||
return _prefAppName;
|
||||
}
|
||||
|
||||
|
||||
private SharedPreferences gp(final SharedPreferences... pref) {
|
||||
return (pref != null && pref.length > 0 ? pref[0] : _prefApp);
|
||||
}
|
||||
|
||||
|
||||
public static void limitListTo(final List<?> list, int maxSize, boolean removeDuplicates) {
|
||||
Object o;
|
||||
int pos;
|
||||
|
||||
for (int i = 0; removeDuplicates && i < list.size(); i++) {
|
||||
o = list.get(i);
|
||||
while ((pos = list.lastIndexOf(o)) != i && pos >= 0) {
|
||||
list.remove(pos);
|
||||
}
|
||||
}
|
||||
while ((pos = list.size()) > maxSize && pos > 0) {
|
||||
list.remove(list.size() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Getter for resources
|
||||
//
|
||||
public String rstr(@StringRes int stringKeyResourceId) {
|
||||
return _context.getString(stringKeyResourceId);
|
||||
}
|
||||
|
||||
public int rcolor(@ColorRes int resColorId) {
|
||||
return ContextCompat.getColor(_context, resColorId);
|
||||
}
|
||||
|
||||
public String[] rstrs(int... keyResourceIds) {
|
||||
String[] ret = new String[keyResourceIds.length];
|
||||
for (int i = 0; i < keyResourceIds.length; i++) {
|
||||
ret[i] = rstr(keyResourceIds[i]);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Getter & Setter for String
|
||||
//
|
||||
public void setString(@StringRes int keyResourceId, String value, final SharedPreferences... pref) {
|
||||
gp(pref).edit().putString(rstr(keyResourceId), value).apply();
|
||||
}
|
||||
|
||||
public void setString(String key, String value, final SharedPreferences... pref) {
|
||||
gp(pref).edit().putString(key, value).apply();
|
||||
}
|
||||
|
||||
public void setString(@StringRes int keyResourceId, @StringRes int defaultValueResourceId, final SharedPreferences... pref) {
|
||||
gp(pref).edit().putString(rstr(keyResourceId), rstr(defaultValueResourceId)).apply();
|
||||
}
|
||||
|
||||
public String getString(@StringRes int keyResourceId, String defaultValue, final SharedPreferences... pref) {
|
||||
return gp(pref).getString(rstr(keyResourceId), defaultValue);
|
||||
}
|
||||
|
||||
public String getString(@StringRes int keyResourceId, @StringRes int defaultValueResourceId, final SharedPreferences... pref) {
|
||||
return gp(pref).getString(rstr(keyResourceId), rstr(defaultValueResourceId));
|
||||
}
|
||||
|
||||
public String getString(String key, String defaultValue, final SharedPreferences... pref) {
|
||||
try {
|
||||
return gp(pref).getString(key, defaultValue);
|
||||
} catch (ClassCastException e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
public String getString(@StringRes int keyResourceId, String defaultValue, @StringRes int keyResourceIdDefaultValue, final SharedPreferences... pref) {
|
||||
return getString(rstr(keyResourceId), rstr(keyResourceIdDefaultValue), pref);
|
||||
}
|
||||
|
||||
private void setStringListOne(String key, List<String> values, final SharedPreferences pref) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (String value : values) {
|
||||
sb.append(ARRAY_SEPARATOR);
|
||||
sb.append(value.replace(ARRAY_SEPARATOR, ARRAY_SEPARATOR_SUBSTITUTE));
|
||||
}
|
||||
setString(key, sb.toString().replaceFirst(ARRAY_SEPARATOR, ""), pref);
|
||||
}
|
||||
|
||||
private ArrayList<String> getStringListOne(String key, final SharedPreferences pref) {
|
||||
ArrayList<String> ret = new ArrayList<>();
|
||||
String value = getString(key, ARRAY_SEPARATOR).replace(ARRAY_SEPARATOR_SUBSTITUTE, ARRAY_SEPARATOR);
|
||||
if (value.equals(ARRAY_SEPARATOR) || TextUtils.isEmpty(value)) {
|
||||
return ret;
|
||||
}
|
||||
ret.addAll(Arrays.asList(value.split(ARRAY_SEPARATOR)));
|
||||
return ret;
|
||||
}
|
||||
|
||||
public void setStringArray(@StringRes int keyResourceId, String[] values, final SharedPreferences... pref) {
|
||||
setStringArray(rstr(keyResourceId), values, pref);
|
||||
}
|
||||
|
||||
public void setStringArray(String key, String[] values, final SharedPreferences... pref) {
|
||||
setStringListOne(key, Arrays.asList(values), gp(pref));
|
||||
}
|
||||
|
||||
public void setStringList(@StringRes int keyResourceId, List<String> values, final SharedPreferences... pref) {
|
||||
setStringArray(rstr(keyResourceId), values.toArray(new String[values.size()]), pref);
|
||||
}
|
||||
|
||||
public void setStringList(String key, List<String> values, final SharedPreferences... pref) {
|
||||
setStringArray(key, values.toArray(new String[values.size()]), pref);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public String[] getStringArray(@StringRes int keyResourceId, final SharedPreferences... pref) {
|
||||
return getStringArray(rstr(keyResourceId), pref);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public String[] getStringArray(String key, final SharedPreferences... pref) {
|
||||
List<String> list = getStringListOne(key, gp(pref));
|
||||
return list.toArray(new String[list.size()]);
|
||||
}
|
||||
|
||||
|
||||
public ArrayList<String> getStringList(@StringRes int keyResourceId, final SharedPreferences... pref) {
|
||||
return getStringListOne(rstr(keyResourceId), gp(pref));
|
||||
}
|
||||
|
||||
public ArrayList<String> getStringList(String key, final SharedPreferences... pref) {
|
||||
return getStringListOne(key, gp(pref));
|
||||
}
|
||||
|
||||
//
|
||||
// Getter & Setter for integer
|
||||
//
|
||||
public void setInt(@StringRes int keyResourceId, int value, final SharedPreferences... pref) {
|
||||
gp(pref).edit().putInt(rstr(keyResourceId), value).apply();
|
||||
}
|
||||
|
||||
public void setInt(String key, int value, final SharedPreferences... pref) {
|
||||
gp(pref).edit().putInt(key, value).apply();
|
||||
}
|
||||
|
||||
public int getInt(@StringRes int keyResourceId, int defaultValue, final SharedPreferences... pref) {
|
||||
return getInt(rstr(keyResourceId), defaultValue, pref);
|
||||
}
|
||||
|
||||
public int getInt(String key, int defaultValue, final SharedPreferences... pref) {
|
||||
try {
|
||||
return gp(pref).getInt(key, defaultValue);
|
||||
} catch (ClassCastException e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
public int getIntOfStringPref(@StringRes int keyResId, int defaultValue, final SharedPreferences... pref) {
|
||||
return getIntOfStringPref(rstr(keyResId), defaultValue, gp(pref));
|
||||
}
|
||||
|
||||
public int getIntOfStringPref(String key, int defaultValue, final SharedPreferences... pref) {
|
||||
String strNum = getString(key, Integer.toString(defaultValue), gp(pref));
|
||||
return Integer.valueOf(strNum);
|
||||
}
|
||||
|
||||
private void setIntListOne(String key, List<Integer> values, final SharedPreferences pref) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (Integer value : values) {
|
||||
sb.append(ARRAY_SEPARATOR);
|
||||
sb.append(value.toString());
|
||||
}
|
||||
setString(key, sb.toString().replaceFirst(ARRAY_SEPARATOR, ""), pref);
|
||||
}
|
||||
|
||||
private ArrayList<Integer> getIntListOne(String key, final SharedPreferences pref) {
|
||||
ArrayList<Integer> ret = new ArrayList<>();
|
||||
String value = getString(key, ARRAY_SEPARATOR);
|
||||
if (value.equals(ARRAY_SEPARATOR)) {
|
||||
return ret;
|
||||
}
|
||||
for (String s : value.split(ARRAY_SEPARATOR)) {
|
||||
ret.add(Integer.parseInt(s));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
public void setIntArray(@StringRes int keyResourceId, Integer[] values, final SharedPreferences... pref) {
|
||||
setIntArray(rstr(keyResourceId), values, gp(pref));
|
||||
}
|
||||
|
||||
public void setIntArray(String key, Integer[] values, final SharedPreferences... pref) {
|
||||
setIntListOne(key, Arrays.asList(values), gp(pref));
|
||||
}
|
||||
|
||||
public Integer[] getIntArray(@StringRes int keyResourceId, final SharedPreferences... pref) {
|
||||
return getIntArray(rstr(keyResourceId), gp(pref));
|
||||
}
|
||||
|
||||
public Integer[] getIntArray(String key, final SharedPreferences... pref) {
|
||||
List<Integer> data = getIntListOne(key, gp(pref));
|
||||
return data.toArray(new Integer[data.size()]);
|
||||
}
|
||||
|
||||
|
||||
public void setIntList(@StringRes int keyResourceId, List<Integer> values, final SharedPreferences... pref) {
|
||||
setIntListOne(rstr(keyResourceId), values, gp(pref));
|
||||
}
|
||||
|
||||
public void setIntList(String key, List<Integer> values, final SharedPreferences... pref) {
|
||||
setIntListOne(key, values, gp(pref));
|
||||
}
|
||||
|
||||
public ArrayList<Integer> getIntList(@StringRes int keyResourceId, final SharedPreferences... pref) {
|
||||
return getIntListOne(rstr(keyResourceId), gp(pref));
|
||||
}
|
||||
|
||||
public ArrayList<Integer> getIntList(String key, final SharedPreferences... pref) {
|
||||
return getIntListOne(key, gp(pref));
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Getter & Setter for Long
|
||||
//
|
||||
public void setLong(@StringRes int keyResourceId, long value, final SharedPreferences... pref) {
|
||||
gp(pref).edit().putLong(rstr(keyResourceId), value).apply();
|
||||
}
|
||||
|
||||
public void setLong(String key, long value, final SharedPreferences... pref) {
|
||||
gp(pref).edit().putLong(key, value).apply();
|
||||
}
|
||||
|
||||
public long getLong(@StringRes int keyResourceId, long defaultValue, final SharedPreferences... pref) {
|
||||
return getLong(rstr(keyResourceId), defaultValue, pref);
|
||||
}
|
||||
|
||||
public long getLong(String key, long defaultValue, final SharedPreferences... pref) {
|
||||
try {
|
||||
return gp(pref).getLong(key, defaultValue);
|
||||
} catch (ClassCastException e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Getter & Setter for Float
|
||||
//
|
||||
public void setFloat(@StringRes int keyResourceId, float value, final SharedPreferences... pref) {
|
||||
gp(pref).edit().putFloat(rstr(keyResourceId), value).apply();
|
||||
}
|
||||
|
||||
public void setFloat(String key, float value, final SharedPreferences... pref) {
|
||||
gp(pref).edit().putFloat(key, value).apply();
|
||||
}
|
||||
|
||||
public float getFloat(@StringRes int keyResourceId, float defaultValue, final SharedPreferences... pref) {
|
||||
return getFloat(rstr(keyResourceId), defaultValue);
|
||||
}
|
||||
|
||||
public float getFloat(String key, float defaultValue, final SharedPreferences... pref) {
|
||||
try {
|
||||
return gp(pref).getFloat(key, defaultValue);
|
||||
} catch (ClassCastException e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Getter & Setter for Double
|
||||
//
|
||||
public void setDouble(@StringRes int keyResourceId, double value, final SharedPreferences... pref) {
|
||||
setLong(rstr(keyResourceId), Double.doubleToRawLongBits(value));
|
||||
}
|
||||
|
||||
public void setDouble(String key, double value, final SharedPreferences... pref) {
|
||||
setLong(key, Double.doubleToRawLongBits(value));
|
||||
}
|
||||
|
||||
public double getDouble(@StringRes int keyResourceId, double defaultValue, final SharedPreferences... pref) {
|
||||
return getDouble(rstr(keyResourceId), defaultValue, gp(pref));
|
||||
}
|
||||
|
||||
public double getDouble(String key, double defaultValue, final SharedPreferences... pref) {
|
||||
return Double.longBitsToDouble(getLong(key, Double.doubleToRawLongBits(defaultValue), gp(pref)));
|
||||
}
|
||||
|
||||
//
|
||||
// Getter & Setter for boolean
|
||||
//
|
||||
public void setBool(@StringRes int keyResourceId, boolean value, final SharedPreferences... pref) {
|
||||
gp(pref).edit().putBoolean(rstr(keyResourceId), value).apply();
|
||||
}
|
||||
|
||||
public void setBool(String key, boolean value, final SharedPreferences... pref) {
|
||||
gp(pref).edit().putBoolean(key, value).apply();
|
||||
}
|
||||
|
||||
public boolean getBool(@StringRes int keyResourceId, boolean defaultValue, final SharedPreferences... pref) {
|
||||
return getBool(rstr(keyResourceId), defaultValue);
|
||||
}
|
||||
|
||||
public boolean getBool(String key, boolean defaultValue, final SharedPreferences... pref) {
|
||||
try {
|
||||
return gp(pref).getBoolean(key, defaultValue);
|
||||
} catch (ClassCastException e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Getter & Setter for Color
|
||||
//
|
||||
public int getColor(String key, @ColorRes int defaultColor, final SharedPreferences... pref) {
|
||||
return getInt(key, rcolor(defaultColor));
|
||||
}
|
||||
|
||||
public int getColor(@StringRes int keyResourceId, @ColorRes int defaultColor, final SharedPreferences... pref) {
|
||||
return getColor(rstr(keyResourceId), defaultColor);
|
||||
}
|
||||
|
||||
//
|
||||
// PropertyBackend<String> implementations
|
||||
//
|
||||
@Override
|
||||
public String getString(String key, String defaultValue) {
|
||||
return getString(key, defaultValue, _prefApp);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getInt(String key, int defaultValue) {
|
||||
return getInt(key, defaultValue, _prefApp);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getLong(String key, long defaultValue) {
|
||||
return getLong(key, defaultValue, _prefApp);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getBool(String key, boolean defaultValue) {
|
||||
return getBool(key, defaultValue, _prefApp);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getFloat(String key, float defaultValue) {
|
||||
return getFloat(key, defaultValue, _prefApp);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getDouble(String key, double defaultValue) {
|
||||
return getDouble(key, defaultValue, _prefApp);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArrayList<Integer> getIntList(String key) {
|
||||
return getIntList(key, _prefApp);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArrayList<String> getStringList(String key) {
|
||||
return getStringList(key, _prefApp);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SharedPreferencesPropertyBackend setString(String key, String value) {
|
||||
setString(key, value, _prefApp);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SharedPreferencesPropertyBackend setInt(String key, int value) {
|
||||
setInt(key, value, _prefApp);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SharedPreferencesPropertyBackend setLong(String key, long value) {
|
||||
setLong(key, value, _prefApp);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SharedPreferencesPropertyBackend setBool(String key, boolean value) {
|
||||
setBool(key, value, _prefApp);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SharedPreferencesPropertyBackend setFloat(String key, float value) {
|
||||
setFloat(key, value, _prefApp);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SharedPreferencesPropertyBackend setDouble(String key, double value) {
|
||||
setDouble(key, value, _prefApp);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SharedPreferencesPropertyBackend setIntList(String key, List<Integer> value) {
|
||||
setIntListOne(key, value, _prefApp);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SharedPreferencesPropertyBackend setStringList(String key, List<String> value) {
|
||||
setStringListOne(key, value, _prefApp);
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean contains(String key, final SharedPreferences... pref) {
|
||||
return gp(pref).contains(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* A method to determine if current hour is between begin and end.
|
||||
* This is especially useful for time-based light/dark mode
|
||||
*/
|
||||
public static boolean isCurrentHourOfDayBetween(int begin, int end) {
|
||||
begin = (begin >= 23 || begin < 0) ? 0 : begin;
|
||||
end = (end >= 23 || end < 0) ? 0 : end;
|
||||
int h = Calendar.getInstance().get(Calendar.HOUR_OF_DAY);
|
||||
return h >= begin && h <= end;
|
||||
}
|
||||
|
||||
/**
|
||||
* Substract current datetime by given amount of days
|
||||
*/
|
||||
public Date getDateOfDaysAgo(int days) {
|
||||
Calendar cal = new GregorianCalendar();
|
||||
cal.add(Calendar.DATE, -days);
|
||||
return cal.getTime();
|
||||
}
|
||||
|
||||
/**
|
||||
* Substract current datetime by given amount of days and check if the given date passed
|
||||
*/
|
||||
public boolean didDaysPassedSince(Date date, int days) {
|
||||
if (date == null || days < 0) {
|
||||
return false;
|
||||
}
|
||||
return date.before(getDateOfDaysAgo(days));
|
||||
}
|
||||
|
||||
public boolean afterDaysTrue(String key, int daysSinceLastTime, int firstTime, final SharedPreferences... pref) {
|
||||
Date d = new Date(System.currentTimeMillis());
|
||||
if (!contains(key)) {
|
||||
d = getDateOfDaysAgo(daysSinceLastTime - firstTime);
|
||||
setLong(key, d.getTime());
|
||||
return firstTime < 1;
|
||||
} else {
|
||||
d = new Date(getLong(key, d.getTime()));
|
||||
}
|
||||
boolean trigger = didDaysPassedSince(d, daysSinceLastTime);
|
||||
if (trigger) {
|
||||
setLong(key, new Date(System.currentTimeMillis()).getTime());
|
||||
}
|
||||
return trigger;
|
||||
}
|
||||
|
||||
public static void clearDebugLog() {
|
||||
_debugLog = "";
|
||||
}
|
||||
|
||||
public static String getDebugLog() {
|
||||
return _debugLog;
|
||||
}
|
||||
|
||||
public static synchronized void appendDebugLog(String text) {
|
||||
_debugLog += "[" + new Date().toString() + "] " + text + "\n";
|
||||
}
|
||||
|
||||
public static boolean ne(final String str) {
|
||||
return str != null && !str.trim().isEmpty();
|
||||
}
|
||||
|
||||
public static boolean fexists(final String fp) {
|
||||
return ne(fp) && (new File(fp)).exists();
|
||||
}
|
||||
}
|
|
@ -1,26 +1,24 @@
|
|||
/*
|
||||
* ------------------------------------------------------------------------------
|
||||
* Gregor Santner <gsantner.github.io> wrote this. You can do whatever you want
|
||||
* with it. If we meet some day, and you think it is worth it, you can buy me a
|
||||
* coke in return. Provided as is without any kind of warranty. Do not blame or
|
||||
* sue me if something goes wrong. No attribution required. - Gregor Santner
|
||||
/*#######################################################
|
||||
*
|
||||
* License: Creative Commons Zero (CC0 1.0)
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/
|
||||
* ----------------------------------------------------------------------------
|
||||
*/
|
||||
* Maintained 2017-2023 by Gregor Santner <gsantner AT mailbox DOT org>
|
||||
*
|
||||
* License: Apache 2.0
|
||||
* https://github.com/gsantner/opoc/#licensing
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
#########################################################*/
|
||||
|
||||
/*
|
||||
* A ListPreference that displays a list of available languages
|
||||
* Requires:
|
||||
* The BuildConfig field "APPLICATION_LANGUAGES" which is a array of all available languages
|
||||
* opoc/ContextUtils
|
||||
The BuildConfig field "APPLICATION_LANGUAGES" which is a array of all available languages
|
||||
opoc/ContextUtils
|
||||
* BuildConfig field can be defined by using the method below
|
||||
|
||||
buildConfigField("String[]", "APPLICATION_LANGUAGES", '{' + getUsedAndroidLanguages().collect {"\"${it}\""}.join(",") + '}')
|
||||
buildConfigField "String[]", "APPLICATION_LANGUAGES", "${getUsedAndroidLanguages()}"
|
||||
|
||||
@SuppressWarnings(["UnnecessaryQualifiedReference", "SpellCheckingInspection"])
|
||||
static String[] getUsedAndroidLanguages() {
|
||||
@SuppressWarnings(["UnnecessaryQualifiedReference", "SpellCheckingInspection", "GroovyUnusedDeclaration"])
|
||||
// Returns used android languages as a buildConfig array: {'de', 'it', ..}"
|
||||
static String getUsedAndroidLanguages() {
|
||||
Set<String> langs = new HashSet<>()
|
||||
new File('.').eachFileRecurse(groovy.io.FileType.DIRECTORIES) {
|
||||
final foldername = it.name
|
||||
|
@ -28,30 +26,30 @@ static String[] getUsedAndroidLanguages() {
|
|||
new File(it.toString()).eachFileRecurse(groovy.io.FileType.FILES) {
|
||||
if (it.name.toLowerCase().endsWith(".xml") && it.getCanonicalFile().getText('UTF-8').contains("<string")) {
|
||||
langs.add(foldername.replace("values-", ""))
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return langs.toArray(new String[langs.size()])
|
||||
return '{' + langs.collect { "\"${it}\"" }.join(",") + '}'
|
||||
}
|
||||
|
||||
* Summary: Change language of this app. Restart app for changes to take effect
|
||||
|
||||
* Define element in Preferences-XML:
|
||||
<net.gsantner.opoc.ui.LanguagePreference
|
||||
<net.gsantner.opoc.preference.LanguagePreference
|
||||
android:icon="@drawable/ic_language_black_24dp"
|
||||
android:key="@string/pref_key__language"
|
||||
android:summary="@string/pref_desc__language"
|
||||
android:title="@string/pref_title__language"/>
|
||||
*/
|
||||
package net.gsantner.opoc.ui;
|
||||
package net.gsantner.opoc.preference.nonsupport;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.preference.ListPreference;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.text.TextUtils;
|
||||
import android.util.AttributeSet;
|
||||
|
||||
import net.gsantner.opoc.util.ContextUtils;
|
||||
|
@ -69,7 +67,7 @@ public class LanguagePreference extends ListPreference {
|
|||
private static final String SYSTEM_LANGUAGE_CODE = "";
|
||||
|
||||
// The language of res/values/ -> (usually English)
|
||||
public String _systemLanguageName = "★System★";
|
||||
public String _systemLanguageName = "System";
|
||||
public String _defaultLanguageCode = "en";
|
||||
|
||||
public LanguagePreference(Context context) {
|
||||
|
@ -95,7 +93,7 @@ public class LanguagePreference extends ListPreference {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected boolean callChangeListener(Object newValue) {
|
||||
public boolean callChangeListener(Object newValue) {
|
||||
if (newValue instanceof String) {
|
||||
// Does not apply to existing UI, use recreate()
|
||||
new ContextUtils(getContext()).setAppLanguage((String) newValue);
|
||||
|
@ -114,11 +112,11 @@ public class LanguagePreference extends ListPreference {
|
|||
// Fetch readable details
|
||||
ContextUtils contextUtils = new ContextUtils(context);
|
||||
List<String> languages = new ArrayList<>();
|
||||
Object bcof = contextUtils.getBuildConfigValue("APPLICATION_LANGUAGES");
|
||||
Object bcof = contextUtils.getBuildConfigValue("DETECTED_ANDROID_LOCALES");
|
||||
if (bcof instanceof String[]) {
|
||||
for (String langId : (String[]) bcof) {
|
||||
Locale locale = contextUtils.getLocaleByAndroidCode(langId);
|
||||
languages.add(summarizeLocale(locale) + ";" + langId);
|
||||
languages.add(summarizeLocale(locale, langId) + ";" + langId);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -133,9 +131,9 @@ public class LanguagePreference extends ListPreference {
|
|||
entryval[i + 2] = languages.get(i).split(";")[1];
|
||||
}
|
||||
entryval[0] = SYSTEM_LANGUAGE_CODE;
|
||||
entries[0] = _systemLanguageName + "\n[" + summarizeLocale(context.getResources().getConfiguration().locale) + "]";
|
||||
entries[0] = _systemLanguageName + " » " + summarizeLocale(context.getResources().getConfiguration().locale, "");
|
||||
entryval[1] = _defaultLanguageCode;
|
||||
entries[1] = summarizeLocale(contextUtils.getLocaleByAndroidCode(_defaultLanguageCode));
|
||||
entries[1] = summarizeLocale(contextUtils.getLocaleByAndroidCode(_defaultLanguageCode), _defaultLanguageCode);
|
||||
|
||||
setEntries(entries);
|
||||
setEntryValues(entryval);
|
||||
|
@ -143,20 +141,30 @@ public class LanguagePreference extends ListPreference {
|
|||
|
||||
// Concat english and localized language name
|
||||
// Append country if country specific (e.g. Portuguese Brazil)
|
||||
private String summarizeLocale(Locale locale) {
|
||||
private String summarizeLocale(final Locale locale, final String localeAndroidCode) {
|
||||
String country = locale.getDisplayCountry(locale);
|
||||
String language = locale.getDisplayLanguage(locale);
|
||||
return locale.getDisplayLanguage(Locale.ENGLISH)
|
||||
+ " (" + language.substring(0, 1).toUpperCase() + language.substring(1)
|
||||
+ ((!country.isEmpty() && !country.toLowerCase().equals(language.toLowerCase())) ? (", " + country) : "")
|
||||
String ret = locale.getDisplayLanguage(Locale.ENGLISH)
|
||||
+ " (" + language.substring(0, 1).toUpperCase(Locale.getDefault()) + language.substring(1)
|
||||
+ ((!country.isEmpty() && !country.toLowerCase(Locale.getDefault()).equals(language.toLowerCase(Locale.getDefault()))) ? (", " + country) : "")
|
||||
+ ")";
|
||||
|
||||
if (localeAndroidCode.equals("zh-rCN")) {
|
||||
ret = ret.substring(0, ret.indexOf(" ") + 1) + "Simplified" + ret.substring(ret.indexOf(" "));
|
||||
} else if (localeAndroidCode.equals("zh-rTW")) {
|
||||
ret = ret.substring(0, ret.indexOf(" ") + 1) + "Traditional" + ret.substring(ret.indexOf(" "));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Add current language to summary
|
||||
@Override
|
||||
public CharSequence getSummary() {
|
||||
Locale locale = new ContextUtils(getContext()).getLocaleByAndroidCode(getValue());
|
||||
return super.getSummary() + "\n\n" + summarizeLocale(locale);
|
||||
String prefix = TextUtils.isEmpty(super.getSummary())
|
||||
? "" : super.getSummary() + "\n\n";
|
||||
return prefix + summarizeLocale(locale, getValue());
|
||||
}
|
||||
|
||||
public String getSystemLanguageName() {
|
|
@ -0,0 +1,394 @@
|
|||
/*#######################################################
|
||||
*
|
||||
* Maintained 2017-2023 by Gregor Santner <gsantner AT mailbox DOT org>
|
||||
*
|
||||
* License: Apache 2.0
|
||||
* https://github.com/gsantner/opoc/#licensing
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
#########################################################*/
|
||||
package net.gsantner.opoc.ui;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Activity;
|
||||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import android.content.res.ColorStateList;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Typeface;
|
||||
import android.os.Build;
|
||||
import android.support.annotation.ColorInt;
|
||||
import android.support.annotation.DrawableRes;
|
||||
import android.support.annotation.LayoutRes;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.annotation.StringRes;
|
||||
import android.support.v7.app.AlertDialog;
|
||||
import android.support.v7.widget.AppCompatEditText;
|
||||
import android.support.v7.widget.TooltipCompat;
|
||||
import android.text.InputType;
|
||||
import android.text.Spannable;
|
||||
import android.text.SpannableString;
|
||||
import android.text.TextUtils;
|
||||
import android.view.Gravity;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.Button;
|
||||
import android.widget.Checkable;
|
||||
import android.widget.Filter;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.ListView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import net.gsantner.opoc.android.dummy.TextWatcherDummy;
|
||||
import net.gsantner.opoc.util.Callback;
|
||||
import net.gsantner.opoc.util.ContextUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
public class SearchOrCustomTextDialog {
|
||||
|
||||
public static class DialogOptions {
|
||||
|
||||
// Callback for search text or text of single item
|
||||
@Nullable
|
||||
public Callback.a1<String> callback = null;
|
||||
|
||||
// Callback for indices of selected items.
|
||||
// List will contain single item if isMultiSelectEnabled == false;
|
||||
@Nullable
|
||||
public Callback.a1<List<Integer>> positionCallback = null;
|
||||
|
||||
public boolean isMultiSelectEnabled = false;
|
||||
public List<? extends CharSequence> data = null;
|
||||
public List<? extends CharSequence> highlightData = null;
|
||||
public List<Integer> iconsForData;
|
||||
public String messageText = "";
|
||||
public String defaultText = "";
|
||||
public boolean isSearchEnabled = true;
|
||||
public boolean isDarkDialog = false;
|
||||
public int dialogWidthDp = WindowManager.LayoutParams.MATCH_PARENT;
|
||||
public int dialogHeightDp = WindowManager.LayoutParams.WRAP_CONTENT;
|
||||
public int gravity = Gravity.NO_GRAVITY;
|
||||
public int searchInputType = InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS;
|
||||
public boolean searchIsRegex = false;
|
||||
public Callback.a1<Spannable> highlighter = null;
|
||||
public String extraFilter = null;
|
||||
public List<Integer> preSelected = null;
|
||||
|
||||
public Callback.a0 neutralButtonCallback = null;
|
||||
|
||||
@ColorInt
|
||||
public int textColor = 0xFF000000;
|
||||
@ColorInt
|
||||
public int highlightColor = 0xFF00FF00;
|
||||
@StringRes
|
||||
public int cancelButtonText = android.R.string.cancel;
|
||||
@StringRes
|
||||
public int neutralButtonText = 0;
|
||||
@StringRes
|
||||
public int okButtonText = android.R.string.ok;
|
||||
@StringRes
|
||||
public int titleText = 0;
|
||||
@StringRes
|
||||
public int searchHintText = android.R.string.search_go;
|
||||
@DrawableRes
|
||||
public int clearInputIcon = android.R.drawable.ic_input_delete;
|
||||
}
|
||||
|
||||
private static class Adapter extends ArrayAdapter<Integer> {
|
||||
@LayoutRes
|
||||
private final int _layout;
|
||||
private final int _layoutHeight;
|
||||
private final LayoutInflater _inflater;
|
||||
private final DialogOptions _dopt;
|
||||
private final List<Integer> _filteredItems;
|
||||
private final Set<Integer> _selectedItems;
|
||||
private final Pattern _extraPattern;
|
||||
|
||||
public static Adapter create(final Context context, final DialogOptions dopt) {
|
||||
return new Adapter(context, dopt, dopt.isMultiSelectEnabled ? android.R.layout.simple_list_item_multiple_choice : android.R.layout.simple_list_item_1, new ArrayList<>());
|
||||
}
|
||||
|
||||
private Adapter(final Context context, final DialogOptions dopt, final int layout, final List<Integer> filteredItems) {
|
||||
super(context, layout, filteredItems);
|
||||
_layout = layout;
|
||||
_filteredItems = filteredItems;
|
||||
_inflater = LayoutInflater.from(context);
|
||||
_dopt = dopt;
|
||||
_extraPattern = (_dopt.extraFilter == null ? null : Pattern.compile(_dopt.extraFilter));
|
||||
_selectedItems = new HashSet<>(_dopt.preSelected != null ? _dopt.preSelected : Collections.emptyList());
|
||||
ContextUtils cu = new ContextUtils(context);
|
||||
_layoutHeight = (int) cu.convertDpToPx(36);
|
||||
cu.freeContextRef();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public View getView(int pos, @Nullable View convertView, @NonNull ViewGroup parent) {
|
||||
final int index = getItem(pos);
|
||||
|
||||
final TextView textView;
|
||||
if (convertView == null) {
|
||||
textView = (TextView) _inflater.inflate(_layout, parent, false);
|
||||
textView.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT));
|
||||
textView.setMinHeight(_layoutHeight);
|
||||
} else {
|
||||
textView = (TextView) convertView;
|
||||
}
|
||||
|
||||
if (textView instanceof Checkable) {
|
||||
((Checkable) textView).setChecked(_selectedItems.contains(index));
|
||||
}
|
||||
|
||||
if (index >= 0 && _dopt.iconsForData != null && index < _dopt.iconsForData.size() && _dopt.iconsForData.get(index) != 0) {
|
||||
textView.setCompoundDrawablesWithIntrinsicBounds(_dopt.iconsForData.get(index), 0, 0, 0);
|
||||
textView.setCompoundDrawablePadding(32);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
textView.setCompoundDrawableTintList(ColorStateList.valueOf(_dopt.isDarkDialog ? Color.WHITE : Color.BLACK));
|
||||
}
|
||||
} else {
|
||||
textView.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
final CharSequence text = _dopt.data.get(index).toString();
|
||||
if (_dopt.highlightData != null) {
|
||||
final boolean hl = _dopt.highlightData.contains(text);
|
||||
textView.setTextColor(hl ? _dopt.highlightColor : _dopt.textColor);
|
||||
textView.setTypeface(null, hl ? Typeface.BOLD : Typeface.NORMAL);
|
||||
}
|
||||
|
||||
if (_dopt.highlighter != null) {
|
||||
Spannable s = new SpannableString(text);
|
||||
_dopt.highlighter.callback(s);
|
||||
textView.setText(s);
|
||||
} else {
|
||||
textView.setText(text);
|
||||
}
|
||||
|
||||
return textView;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Filter getFilter() {
|
||||
return new Filter() {
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
protected void publishResults(final CharSequence constraint, final FilterResults results) {
|
||||
_filteredItems.clear();
|
||||
_filteredItems.addAll((List<Integer>) results.values);
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected FilterResults performFiltering(final CharSequence constraint) {
|
||||
final List<Integer> resList = new ArrayList<>();
|
||||
|
||||
if (_dopt.data != null) {
|
||||
final String fil = constraint.toString();
|
||||
final boolean emptySearch = fil.isEmpty();
|
||||
for (int i = 0; i < _dopt.data.size(); i++) {
|
||||
final String str = _dopt.data.get(i).toString();
|
||||
final boolean matchExtra = (_extraPattern == null) || _extraPattern.matcher(str).find();
|
||||
final Locale locale = Locale.getDefault();
|
||||
final boolean matchNormal = str.toLowerCase(locale).contains(fil.toLowerCase(locale));
|
||||
final boolean matchRegex = _dopt.searchIsRegex && (str.matches(fil));
|
||||
if (matchExtra && (matchNormal || matchRegex || emptySearch)) {
|
||||
resList.add(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final FilterResults res = new FilterResults();
|
||||
res.values = resList;
|
||||
res.count = resList.size();
|
||||
return res;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public static void showMultiChoiceDialogWithSearchFilterUI(final Activity activity, final DialogOptions dopt) {
|
||||
final AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(activity, dopt.isDarkDialog
|
||||
? android.support.v7.appcompat.R.style.Theme_AppCompat_Dialog
|
||||
: android.support.v7.appcompat.R.style.Theme_AppCompat_Light_Dialog
|
||||
);
|
||||
final Adapter listAdapter = Adapter.create(activity, dopt);
|
||||
|
||||
final AppCompatEditText searchEditText = new AppCompatEditText(activity);
|
||||
searchEditText.setText(dopt.defaultText);
|
||||
searchEditText.setSingleLine(true);
|
||||
searchEditText.setMaxLines(1);
|
||||
searchEditText.setTextColor(dopt.textColor);
|
||||
searchEditText.setHintTextColor((dopt.textColor & 0x00FFFFFF) | 0x99000000);
|
||||
searchEditText.setHint(dopt.searchHintText);
|
||||
searchEditText.setInputType(dopt.searchInputType == 0 ? searchEditText.getInputType() : dopt.searchInputType);
|
||||
searchEditText.addTextChangedListener(TextWatcherDummy.after((cbEditable) -> listAdapter.getFilter().filter(cbEditable)));
|
||||
|
||||
final ContextUtils cu = new ContextUtils(activity);
|
||||
final int margin = (int) cu.convertDpToPx(8);
|
||||
cu.freeContextRef();
|
||||
|
||||
final LinearLayout searchLayout = new LinearLayout(activity);
|
||||
searchLayout.setOrientation(LinearLayout.HORIZONTAL);
|
||||
|
||||
LinearLayout.LayoutParams lp;
|
||||
lp = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.MATCH_PARENT, 1);
|
||||
lp.gravity = Gravity.START | Gravity.BOTTOM;
|
||||
searchLayout.addView(searchEditText, lp);
|
||||
|
||||
// 'Button to clear the search box'
|
||||
final ImageView clearButton = new ImageView(activity);
|
||||
clearButton.setImageResource(dopt.clearInputIcon);
|
||||
TooltipCompat.setTooltipText(clearButton, activity.getString(android.R.string.cancel));
|
||||
clearButton.setColorFilter(dopt.isDarkDialog ? Color.WHITE : Color.parseColor("#ff505050"));
|
||||
lp = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.MATCH_PARENT, 0);
|
||||
lp.gravity = Gravity.END | Gravity.CENTER_VERTICAL;
|
||||
lp.setMargins(margin, 0, (int) (margin * 1.5), 0);
|
||||
searchLayout.addView(clearButton, lp);
|
||||
clearButton.setOnClickListener((v) -> searchEditText.setText(""));
|
||||
|
||||
final ListView listView = new ListView(activity);
|
||||
final LinearLayout linearLayout = new LinearLayout(activity);
|
||||
listView.setAdapter(listAdapter);
|
||||
listView.setVisibility(dopt.data != null && !dopt.data.isEmpty() ? View.VISIBLE : View.GONE);
|
||||
linearLayout.setOrientation(LinearLayout.VERTICAL);
|
||||
|
||||
if (dopt.isSearchEnabled) {
|
||||
lp = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT);
|
||||
lp.setMargins(margin, margin / 2, margin, margin / 2);
|
||||
linearLayout.addView(searchLayout, lp);
|
||||
}
|
||||
|
||||
final LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, 0);
|
||||
layoutParams.weight = 1;
|
||||
linearLayout.addView(listView, layoutParams);
|
||||
if (!TextUtils.isEmpty(dopt.messageText)) {
|
||||
dialogBuilder.setMessage(dopt.messageText);
|
||||
}
|
||||
|
||||
dialogBuilder.setView(linearLayout)
|
||||
.setOnCancelListener(null)
|
||||
.setNegativeButton(dopt.cancelButtonText, (dialogInterface, i) -> dialogInterface.dismiss());
|
||||
|
||||
if (dopt.titleText != 0) {
|
||||
dialogBuilder.setTitle(dopt.titleText);
|
||||
}
|
||||
|
||||
// Ok button action
|
||||
if ((dopt.isSearchEnabled && dopt.callback != null) || (dopt.isMultiSelectEnabled)) {
|
||||
dialogBuilder.setPositiveButton(dopt.okButtonText, (dialogInterface, i) -> {
|
||||
final String searchText = dopt.isSearchEnabled ? searchEditText.getText().toString() : null;
|
||||
if (dopt.positionCallback != null && !listAdapter._selectedItems.isEmpty()) {
|
||||
final List<Integer> sel = new ArrayList<>(listAdapter._selectedItems);
|
||||
Collections.sort(sel);
|
||||
dopt.positionCallback.callback(sel);
|
||||
} else if (dopt.callback != null && !TextUtils.isEmpty(searchText)) {
|
||||
dopt.callback.callback(searchText);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Setup neutralbutton
|
||||
if (dopt.neutralButtonCallback != null && dopt.neutralButtonText != 0) {
|
||||
dialogBuilder.setNeutralButton(dopt.neutralButtonText, (dialogInterface, i) -> {
|
||||
dopt.neutralButtonCallback.callback();
|
||||
});
|
||||
}
|
||||
|
||||
final AlertDialog dialog = dialogBuilder.create();
|
||||
|
||||
searchEditText.setOnKeyListener((keyView, keyCode, keyEvent) -> {
|
||||
if ((keyEvent.getAction() == KeyEvent.ACTION_DOWN) && (keyCode == KeyEvent.KEYCODE_ENTER)) {
|
||||
dialog.dismiss();
|
||||
if (dopt.callback != null && !TextUtils.isEmpty(searchEditText.getText().toString())) {
|
||||
dopt.callback.callback(searchEditText.getText().toString());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
Window w;
|
||||
if ((w = dialog.getWindow()) != null && dopt.isSearchEnabled) {
|
||||
w.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE | WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
|
||||
}
|
||||
dialog.show();
|
||||
if ((w = dialog.getWindow()) != null) {
|
||||
int ds_w = dopt.dialogWidthDp < 100 ? dopt.dialogWidthDp : ((int) (dopt.dialogWidthDp * activity.getResources().getDisplayMetrics().density));
|
||||
int ds_h = dopt.dialogHeightDp < 100 ? dopt.dialogHeightDp : ((int) (dopt.dialogHeightDp * activity.getResources().getDisplayMetrics().density));
|
||||
w.setLayout(ds_w, ds_h);
|
||||
}
|
||||
|
||||
if ((w = dialog.getWindow()) != null && dopt.gravity != Gravity.NO_GRAVITY) {
|
||||
WindowManager.LayoutParams wlp = w.getAttributes();
|
||||
wlp.gravity = dopt.gravity;
|
||||
w.setAttributes(wlp);
|
||||
}
|
||||
|
||||
if (dopt.isSearchEnabled) {
|
||||
searchEditText.requestFocus();
|
||||
}
|
||||
if (dopt.defaultText != null) {
|
||||
listAdapter.getFilter().filter(searchEditText.getText());
|
||||
}
|
||||
|
||||
// Helper function to trigger callback with single item
|
||||
final Callback.b1<Integer> directActivate = (position) -> {
|
||||
final int index = listAdapter._filteredItems.get(position);
|
||||
dialog.dismiss();
|
||||
if (dopt.callback != null) {
|
||||
dopt.callback.callback(dopt.data.get(index).toString());
|
||||
}
|
||||
if (dopt.positionCallback != null) {
|
||||
dopt.positionCallback.callback(Collections.singletonList(index));
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
// Helper function to append selection count to OK button
|
||||
final Button okButton = dialog.getButton(Dialog.BUTTON_POSITIVE);
|
||||
final String okText = activity.getString(dopt.okButtonText) + (dopt.isMultiSelectEnabled ? " (%d)" : "");
|
||||
final Callback.a0 setOkButtonState = () -> {
|
||||
okButton.setText(okText.replace("%d", Integer.toString(listAdapter._selectedItems.size())));
|
||||
};
|
||||
|
||||
// Set ok button text initially
|
||||
setOkButtonState.callback();
|
||||
|
||||
// Item click action
|
||||
listView.setOnItemClickListener((parent, textView, pos, id) -> {
|
||||
if (dopt.isMultiSelectEnabled) {
|
||||
final int index = listAdapter._filteredItems.get(pos);
|
||||
if (listAdapter._selectedItems.contains(index)) {
|
||||
listAdapter._selectedItems.remove(index);
|
||||
} else {
|
||||
listAdapter._selectedItems.add(index);
|
||||
}
|
||||
if (textView instanceof Checkable) {
|
||||
((Checkable) textView).setChecked(listAdapter._selectedItems.contains(index));
|
||||
}
|
||||
setOkButtonState.callback();
|
||||
} else {
|
||||
directActivate.callback(pos);
|
||||
}
|
||||
});
|
||||
|
||||
// long click always activates
|
||||
listView.setOnItemLongClickListener((parent, view, pos, id) -> directActivate.callback(pos));
|
||||
}
|
||||
}
|
|
@ -1,21 +1,30 @@
|
|||
/*
|
||||
* ------------------------------------------------------------------------------
|
||||
* Gregor Santner <gsantner.github.io> wrote this. You can do whatever you want
|
||||
* with it. If we meet some day, and you think it is worth it, you can buy me a
|
||||
* coke in return. Provided as is without any kind of warranty. Do not blame or
|
||||
* sue me if something goes wrong. No attribution required. - Gregor Santner
|
||||
/*#######################################################
|
||||
*
|
||||
* License: Creative Commons Zero (CC0 1.0)
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/
|
||||
* ----------------------------------------------------------------------------
|
||||
*/
|
||||
* Maintained 2016-2023 by Gregor Santner <gsantner AT mailbox DOT org>
|
||||
*
|
||||
* License of this file: Apache 2.0
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* https://github.com/gsantner/opoc/#licensing
|
||||
*
|
||||
#########################################################*/
|
||||
package net.gsantner.opoc.util;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.ActivityManager;
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.res.TypedArray;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.provider.CalendarContract;
|
||||
import android.support.annotation.ColorInt;
|
||||
import android.support.annotation.StringRes;
|
||||
import android.support.design.widget.Snackbar;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
import android.support.v7.app.AlertDialog;
|
||||
import android.support.v7.widget.AppCompatTextView;
|
||||
import android.text.Html;
|
||||
|
@ -23,11 +32,16 @@ import android.text.SpannableString;
|
|||
import android.text.method.LinkMovementMethod;
|
||||
import android.util.TypedValue;
|
||||
import android.view.View;
|
||||
import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.webkit.WebView;
|
||||
import android.widget.ScrollView;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
||||
@SuppressWarnings({"WeakerAccess", "unused", "SameParameterValue", "SpellCheckingInspection"})
|
||||
@SuppressWarnings({"WeakerAccess", "unused", "SameParameterValue", "SpellCheckingInspection", "rawtypes", "UnusedReturnValue"})
|
||||
public class ActivityUtils extends net.gsantner.opoc.util.ContextUtils {
|
||||
//########################
|
||||
//## Members, Constructors
|
||||
|
@ -39,6 +53,12 @@ public class ActivityUtils extends net.gsantner.opoc.util.ContextUtils {
|
|||
_activity = activity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void freeContextRef() {
|
||||
super.freeContextRef();
|
||||
_activity = null;
|
||||
}
|
||||
|
||||
//########################
|
||||
//## Methods
|
||||
//########################
|
||||
|
@ -77,9 +97,11 @@ public class ActivityUtils extends net.gsantner.opoc.util.ContextUtils {
|
|||
}
|
||||
|
||||
|
||||
public void showSnackBar(@StringRes int stringResId, boolean showLong) {
|
||||
Snackbar.make(_activity.findViewById(android.R.id.content), stringResId,
|
||||
showLong ? Snackbar.LENGTH_LONG : Snackbar.LENGTH_SHORT).show();
|
||||
public Snackbar showSnackBar(@StringRes int stringResId, boolean showLong) {
|
||||
Snackbar s = Snackbar.make(_activity.findViewById(android.R.id.content), stringResId,
|
||||
showLong ? Snackbar.LENGTH_LONG : Snackbar.LENGTH_SHORT);
|
||||
s.show();
|
||||
return s;
|
||||
}
|
||||
|
||||
public void showSnackBar(@StringRes int stringResId, boolean showLong, @StringRes int actionResId, View.OnClickListener listener) {
|
||||
|
@ -89,11 +111,58 @@ public class ActivityUtils extends net.gsantner.opoc.util.ContextUtils {
|
|||
.show();
|
||||
}
|
||||
|
||||
public void hideSoftKeyboard() {
|
||||
InputMethodManager inputMethodManager = (InputMethodManager) _activity.getSystemService(Activity.INPUT_METHOD_SERVICE);
|
||||
if (_activity.getCurrentFocus() != null && _activity.getCurrentFocus().getWindowToken() != null) {
|
||||
inputMethodManager.hideSoftInputFromWindow(_activity.getCurrentFocus().getWindowToken(), 0);
|
||||
public ActivityUtils setSoftKeyboardVisibile(boolean visible, View... editView) {
|
||||
final Activity activity = _activity;
|
||||
if (activity != null) {
|
||||
final View v = (editView != null && editView.length > 0) ? (editView[0]) : (activity.getCurrentFocus() != null && activity.getCurrentFocus().getWindowToken() != null ? activity.getCurrentFocus() : null);
|
||||
final InputMethodManager imm = (InputMethodManager) activity.getSystemService(Activity.INPUT_METHOD_SERVICE);
|
||||
if (v != null && imm != null) {
|
||||
Runnable r = () -> {
|
||||
if (visible) {
|
||||
v.requestFocus();
|
||||
imm.showSoftInput(v, InputMethodManager.SHOW_FORCED);
|
||||
} else {
|
||||
imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
|
||||
}
|
||||
};
|
||||
r.run();
|
||||
for (int d : new int[]{100, 350}) {
|
||||
v.postDelayed(r, d);
|
||||
}
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public ActivityUtils hideSoftKeyboard() {
|
||||
if (_activity != null) {
|
||||
InputMethodManager imm = (InputMethodManager) _activity.getSystemService(Activity.INPUT_METHOD_SERVICE);
|
||||
if (imm != null && _activity.getCurrentFocus() != null && _activity.getCurrentFocus().getWindowToken() != null) {
|
||||
imm.hideSoftInputFromWindow(_activity.getCurrentFocus().getWindowToken(), 0);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public ActivityUtils showSoftKeyboard() {
|
||||
if (_activity != null) {
|
||||
InputMethodManager imm = (InputMethodManager) _activity.getSystemService(Activity.INPUT_METHOD_SERVICE);
|
||||
if (imm != null && _activity.getCurrentFocus() != null && _activity.getCurrentFocus().getWindowToken() != null) {
|
||||
showSoftKeyboard(_activity.getCurrentFocus());
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public ActivityUtils showSoftKeyboard(View textInputView) {
|
||||
if (_activity != null) {
|
||||
InputMethodManager imm = (InputMethodManager) _activity.getSystemService(Activity.INPUT_METHOD_SERVICE);
|
||||
if (imm != null && textInputView != null) {
|
||||
imm.showSoftInput(textInputView, InputMethodManager.SHOW_FORCED);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public void showDialogWithHtmlTextView(@StringRes int resTitleId, String html) {
|
||||
|
@ -101,23 +170,37 @@ public class ActivityUtils extends net.gsantner.opoc.util.ContextUtils {
|
|||
}
|
||||
|
||||
public void showDialogWithHtmlTextView(@StringRes int resTitleId, String text, boolean isHtml, DialogInterface.OnDismissListener dismissedListener) {
|
||||
ScrollView scroll = new ScrollView(_context);
|
||||
AppCompatTextView textView = new AppCompatTextView(_context);
|
||||
int padding = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 16,
|
||||
_context.getResources().getDisplayMetrics());
|
||||
textView.setMovementMethod(new LinkMovementMethod());
|
||||
textView.setPadding(padding, 0, padding, 0);
|
||||
int padding = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 16, _context.getResources().getDisplayMetrics());
|
||||
|
||||
scroll.setPadding(padding, 0, padding, 0);
|
||||
scroll.addView(textView);
|
||||
textView.setMovementMethod(new LinkMovementMethod());
|
||||
textView.setText(isHtml ? new SpannableString(Html.fromHtml(text)) : text);
|
||||
textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 17);
|
||||
|
||||
AlertDialog.Builder dialog = new AlertDialog.Builder(_context)
|
||||
.setPositiveButton(android.R.string.ok, null).setOnDismissListener(dismissedListener)
|
||||
.setView(scroll);
|
||||
if (resTitleId != 0) {
|
||||
dialog.setTitle(resTitleId);
|
||||
}
|
||||
dialogFullWidth(dialog.show(), true, false);
|
||||
}
|
||||
|
||||
public void showDialogWithRawFileInWebView(String fileInRaw, @StringRes int resTitleId) {
|
||||
WebView wv = new WebView(_context);
|
||||
wv.loadUrl("file:///android_res/raw/" + fileInRaw);
|
||||
AlertDialog.Builder dialog = new AlertDialog.Builder(_context)
|
||||
.setPositiveButton(android.R.string.ok, null)
|
||||
.setOnDismissListener(dismissedListener)
|
||||
.setTitle(resTitleId)
|
||||
.setView(textView);
|
||||
dialog.show();
|
||||
.setView(wv);
|
||||
dialogFullWidth(dialog.show(), true, false);
|
||||
}
|
||||
|
||||
// Toggle with no param, else set visibility according to first bool
|
||||
public void toggleStatusbarVisibility(boolean... optionalForceVisible) {
|
||||
public ActivityUtils toggleStatusbarVisibility(boolean... optionalForceVisible) {
|
||||
WindowManager.LayoutParams attrs = _activity.getWindow().getAttributes();
|
||||
int flag = WindowManager.LayoutParams.FLAG_FULLSCREEN;
|
||||
if (optionalForceVisible.length == 0) {
|
||||
|
@ -128,5 +211,136 @@ public class ActivityUtils extends net.gsantner.opoc.util.ContextUtils {
|
|||
attrs.flags |= flag;
|
||||
}
|
||||
_activity.getWindow().setAttributes(attrs);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ActivityUtils showGooglePlayEntryForThisApp() {
|
||||
String pkgId = "details?id=" + _activity.getPackageName();
|
||||
Intent goToMarket = new Intent(Intent.ACTION_VIEW, Uri.parse("market://" + pkgId));
|
||||
goToMarket.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY |
|
||||
(Build.VERSION.SDK_INT >= 21 ? Intent.FLAG_ACTIVITY_NEW_DOCUMENT : Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) |
|
||||
Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
|
||||
try {
|
||||
_activity.startActivity(goToMarket);
|
||||
} catch (ActivityNotFoundException e) {
|
||||
_activity.startActivity(new Intent(Intent.ACTION_VIEW,
|
||||
Uri.parse("https://play.google.com/store/apps/" + pkgId)));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public ActivityUtils setStatusbarColor(int color, boolean... fromRes) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
if (fromRes != null && fromRes.length > 0 && fromRes[0]) {
|
||||
color = ContextCompat.getColor(_context, color);
|
||||
}
|
||||
|
||||
_activity.getWindow().setStatusBarColor(color);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public ActivityUtils setLauncherActivityEnabled(Class activityClass, boolean enable) {
|
||||
try {
|
||||
ComponentName component = new ComponentName(_context, activityClass);
|
||||
_context.getPackageManager().setComponentEnabledSetting(component, enable ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED : PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean isLauncherEnabled(Class activityClass) {
|
||||
try {
|
||||
ComponentName component = new ComponentName(_context, activityClass);
|
||||
return _context.getPackageManager().getComponentEnabledSetting(component) != PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@ColorInt
|
||||
public Integer getCurrentPrimaryColor() {
|
||||
TypedValue typedValue = new TypedValue();
|
||||
_context.getTheme().resolveAttribute(getResId(ResType.ATTR, "colorPrimary"), typedValue, true);
|
||||
return typedValue.data;
|
||||
}
|
||||
|
||||
@ColorInt
|
||||
public Integer getCurrentPrimaryDarkColor() {
|
||||
TypedValue typedValue = new TypedValue();
|
||||
_context.getTheme().resolveAttribute(getResId(ResType.ATTR, "colorPrimaryDark"), typedValue, true);
|
||||
return typedValue.data;
|
||||
}
|
||||
|
||||
@ColorInt
|
||||
public Integer getCurrentAccentColor() {
|
||||
TypedValue typedValue = new TypedValue();
|
||||
_context.getTheme().resolveAttribute(getResId(ResType.ATTR, "colorAccent"), typedValue, true);
|
||||
return typedValue.data;
|
||||
}
|
||||
|
||||
@ColorInt
|
||||
public Integer getActivityBackgroundColor() {
|
||||
TypedArray array = _activity.getTheme().obtainStyledAttributes(new int[]{
|
||||
android.R.attr.colorBackground,
|
||||
});
|
||||
int c = array.getColor(0, 0xFF0000);
|
||||
array.recycle();
|
||||
return c;
|
||||
}
|
||||
|
||||
public ActivityUtils startCalendarApp() {
|
||||
Uri.Builder builder = CalendarContract.CONTENT_URI.buildUpon();
|
||||
builder.appendPath("time");
|
||||
builder.appendPath(Long.toString(System.currentTimeMillis()));
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW, builder.build());
|
||||
_activity.startActivity(intent);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect if the activity is currently in splitscreen/multiwindow mode
|
||||
*/
|
||||
public boolean isInSplitScreenMode() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
return _activity.isInMultiWindowMode();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show dialog in full width / show keyboard
|
||||
*
|
||||
* @param dialog Get via dialog.show()
|
||||
*/
|
||||
public void dialogFullWidth(AlertDialog dialog, boolean fullWidth, boolean showKeyboard) {
|
||||
try {
|
||||
Window w;
|
||||
if (dialog != null && (w = dialog.getWindow()) != null) {
|
||||
if (fullWidth) {
|
||||
w.setLayout(WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.WRAP_CONTENT);
|
||||
}
|
||||
if (showKeyboard) {
|
||||
w.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
|
||||
}
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
// Make activity/app not show up in the recents history - call before finish / System.exit
|
||||
public ActivityUtils removeActivityFromHistory() {
|
||||
try {
|
||||
ActivityManager am = (ActivityManager) _activity.getSystemService(Context.ACTIVITY_SERVICE);
|
||||
if (am != null && android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
|
||||
List<ActivityManager.AppTask> tasks = am.getAppTasks();
|
||||
if (tasks != null && !tasks.isEmpty()) {
|
||||
tasks.get(0).setExcludeFromRecents(true);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,14 +1,12 @@
|
|||
/*
|
||||
* ---------------------------------------------------------------------------- *
|
||||
* Gregor Santner <gsantner.github.io> wrote this file. You can do whatever
|
||||
* you want with this stuff. If we meet some day, and you think this stuff is
|
||||
* worth it, you can buy me a coke in return. Provided as is without any kind
|
||||
* of warranty. No attribution required. - Gregor Santner
|
||||
/*#######################################################
|
||||
*
|
||||
* License of this file: Creative Commons Zero (CC0 1.0)
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/
|
||||
* ----------------------------------------------------------------------------
|
||||
*/
|
||||
* Maintained 2017-2023 by Gregor Santner <gsantner AT mailbox DOT org>
|
||||
*
|
||||
* License: Apache 2.0
|
||||
* https://github.com/gsantner/opoc/#licensing
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
#########################################################*/
|
||||
|
||||
/*
|
||||
* Place adblock hosts file in raw: src/main/res/raw/adblock_domains__xyz.txt
|
||||
|
@ -47,8 +45,9 @@ import java.util.Set;
|
|||
/**
|
||||
* Simple Host-Based AdBlocker
|
||||
*/
|
||||
@SuppressWarnings({"WeakerAccess", "SpellCheckingInspection", "unused"})
|
||||
@SuppressWarnings({"WeakerAccess", "SpellCheckingInspection", "unused", "TryFinallyCanBeTryWithResources"})
|
||||
public class AdBlock {
|
||||
private static final Object synchronizeObj = new Object();
|
||||
private static final AdBlock instance = new AdBlock();
|
||||
|
||||
public static AdBlock getInstance() {
|
||||
|
@ -62,7 +61,9 @@ public class AdBlock {
|
|||
//########################
|
||||
private final Set<String> _adblockHostsFromRaw = new HashSet<>();
|
||||
private final Set<String> _adblockHosts = new HashSet<>();
|
||||
private boolean _isLoaded;
|
||||
private final List<Callback.b3<URI, String, String>> _customBlockCallbacks = new ArrayList<>();
|
||||
private boolean _isLoaded = false;
|
||||
private boolean _isAdblockLogging = false;
|
||||
|
||||
//########################
|
||||
//##
|
||||
|
@ -73,25 +74,47 @@ public class AdBlock {
|
|||
}
|
||||
|
||||
public boolean isAdHost(String urlS) {
|
||||
boolean block = false;
|
||||
if (urlS != null && !urlS.isEmpty() && urlS.startsWith("http")) {
|
||||
try {
|
||||
URI url = new URI(urlS);
|
||||
URI url;
|
||||
try {
|
||||
url = new URI(urlS);
|
||||
} catch (Exception e) {
|
||||
url = new URI(urlS.replaceFirst("[?].*", ""));
|
||||
}
|
||||
String host = url.getHost().trim();
|
||||
if (host.startsWith("www.") && host.length() >= 4) {
|
||||
host = host.substring(4);
|
||||
}
|
||||
return _adblockHosts.contains(host) || _adblockHosts.contains("www." + host);
|
||||
block = _adblockHosts.contains(host) || _adblockHosts.contains("www." + host);
|
||||
for (Callback.b3<URI, String, String> cb : _customBlockCallbacks) {
|
||||
if (block) {
|
||||
break;
|
||||
}
|
||||
try {
|
||||
block = cb.callback(url, urlS, host);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
} catch (URISyntaxException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
}
|
||||
return false;
|
||||
|
||||
if (_isAdblockLogging) {
|
||||
Log.d(getClass().getSimpleName(), "UrlAllowed-" + (block ? "N" : "Y") + " " + urlS);
|
||||
}
|
||||
return block;
|
||||
}
|
||||
|
||||
public AdBlock reset() {
|
||||
_adblockHosts.clear();
|
||||
_adblockHosts.addAll(_adblockHostsFromRaw);
|
||||
synchronized (synchronizeObj) {
|
||||
_adblockHosts.clear();
|
||||
_adblockHosts.addAll(_adblockHostsFromRaw);
|
||||
_customBlockCallbacks.clear();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -103,7 +126,7 @@ public class AdBlock {
|
|||
return new WebResourceResponse("text/plain", "utf-8", new ByteArrayInputStream("".getBytes()));
|
||||
}
|
||||
|
||||
public void addBlockedHosts(String... hosts) {
|
||||
public AdBlock addBlockedHosts(String... hosts) {
|
||||
for (String host : hosts) {
|
||||
if (host != null) {
|
||||
host = host.trim();
|
||||
|
@ -111,23 +134,29 @@ public class AdBlock {
|
|||
host = host.substring(4);
|
||||
}
|
||||
if (!host.startsWith("#") && !host.startsWith("\"")) {
|
||||
_adblockHosts.add(host);
|
||||
synchronized (synchronizeObj) {
|
||||
_adblockHosts.add(host);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public void loadHostsFromRawAssetsAsync(final Context context) {
|
||||
new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
public void loadHostsFromRawAssetsAsync(final Context context, final boolean... debugIgnoreAssets) {
|
||||
if (debugIgnoreAssets != null && debugIgnoreAssets.length > 0 && debugIgnoreAssets[0]) {
|
||||
_isLoaded = true;
|
||||
return;
|
||||
}
|
||||
|
||||
new Thread(() -> {
|
||||
try {
|
||||
synchronized (synchronizeObj) {
|
||||
loadHostsFromRawAssets(context);
|
||||
_isLoaded = true;
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
@ -173,4 +202,17 @@ public class AdBlock {
|
|||
}
|
||||
return adblockResIds;
|
||||
}
|
||||
|
||||
// URI uri, String url, String host
|
||||
public AdBlock addCustomBlockCallback(Callback.b3<URI, String, String> cb) {
|
||||
synchronized (synchronizeObj) {
|
||||
_customBlockCallbacks.add(cb);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public AdBlock setLogEnabled(boolean isAdblockLogging) {
|
||||
_isAdblockLogging = isAdblockLogging;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,408 +0,0 @@
|
|||
/*
|
||||
* ------------------------------------------------------------------------------
|
||||
* Gregor Santner <gsantner.github.io> wrote this. You can do whatever you want
|
||||
* with it. If we meet some day, and you think it is worth it, you can buy me a
|
||||
* coke in return. Provided as is without any kind of warranty. Do not blame or
|
||||
* sue me if something goes wrong. No attribution required. - Gregor Santner
|
||||
*
|
||||
* License: Creative Commons Zero (CC0 1.0)
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/
|
||||
* ----------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/*
|
||||
* This is a wrapper for settings based on SharedPreferences
|
||||
* with keys in resources. Extend from this class and add
|
||||
* getters/setters for the app's settings.
|
||||
* Example:
|
||||
public boolean isAppFirstStart(boolean doSet) {
|
||||
boolean value = getBool(prefApp, R.string.pref_key__app_first_start, true);
|
||||
if (doSet) {
|
||||
setBool(prefApp, R.string.pref_key__app_first_start, false);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
public boolean isAppCurrentVersionFirstStart(boolean doSet) {
|
||||
int value = getInt(prefApp, R.string.pref_key__app_first_start_current_version, -1);
|
||||
if (doSet) {
|
||||
setInt(prefApp, R.string.pref_key__app_first_start_current_version, BuildConfig.VERSION_CODE);
|
||||
}
|
||||
return value != BuildConfig.VERSION_CODE && !BuildConfig.IS_TEST_BUILD;
|
||||
}
|
||||
|
||||
* Maybe add a singleton for this:
|
||||
* Whereas App.get() is returning ApplicationContext
|
||||
private AppSettings(Context _context) {
|
||||
super(_context);
|
||||
}
|
||||
|
||||
public static AppSettings get() {
|
||||
return new AppSettings(App.get());
|
||||
}
|
||||
*/
|
||||
|
||||
package net.gsantner.opoc.util;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.support.annotation.ColorRes;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.StringRes;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
/**
|
||||
* Wrapper for settings based on SharedPreferences with keys in resources
|
||||
* Default SharedPreference (_prefApp) will be taken if no SP is specified, else the first one
|
||||
*/
|
||||
@SuppressWarnings({"WeakerAccess", "unused", "SpellCheckingInspection", "SameParameterValue"})
|
||||
public class AppSettingsBase {
|
||||
protected static final String ARRAY_SEPARATOR = "%%%";
|
||||
protected static final String ARRAY_SEPARATOR_SUBSTITUTE = "§§§";
|
||||
public static final String SHARED_PREF_APP = "app";
|
||||
|
||||
//########################
|
||||
//## Members, Constructors
|
||||
//########################
|
||||
protected final SharedPreferences _prefApp;
|
||||
protected final String _prefAppName;
|
||||
protected final Context _context;
|
||||
|
||||
public AppSettingsBase(final Context context) {
|
||||
this(context, SHARED_PREF_APP);
|
||||
}
|
||||
|
||||
public AppSettingsBase(final Context context, final String prefAppName) {
|
||||
_context = context.getApplicationContext();
|
||||
_prefAppName = TextUtils.isEmpty(prefAppName) ?
|
||||
_context.getPackageName() + "_preferences" : prefAppName;
|
||||
_prefApp = _context.getSharedPreferences(_prefAppName, Context.MODE_PRIVATE);
|
||||
}
|
||||
|
||||
//#####################
|
||||
//## Methods
|
||||
//#####################
|
||||
public Context getContext() {
|
||||
return _context;
|
||||
}
|
||||
|
||||
public boolean isKeyEqual(String key, int stringKeyResourceId) {
|
||||
return key.equals(rstr(stringKeyResourceId));
|
||||
}
|
||||
|
||||
public void resetSettings() {
|
||||
resetSettings(_prefApp);
|
||||
}
|
||||
|
||||
@SuppressLint("ApplySharedPref")
|
||||
public void resetSettings(final SharedPreferences pref) {
|
||||
pref.edit().clear().commit();
|
||||
}
|
||||
|
||||
public boolean isPrefSet(@StringRes int stringKeyResourceId) {
|
||||
return isPrefSet(_prefApp, stringKeyResourceId);
|
||||
}
|
||||
|
||||
public boolean isPrefSet(final SharedPreferences pref, @StringRes int stringKeyResourceId) {
|
||||
return pref.contains(rstr(stringKeyResourceId));
|
||||
}
|
||||
|
||||
public void registerPreferenceChangedListener(SharedPreferences.OnSharedPreferenceChangeListener value) {
|
||||
registerPreferenceChangedListener(_prefApp, value);
|
||||
}
|
||||
|
||||
public void registerPreferenceChangedListener(final SharedPreferences pref, SharedPreferences.OnSharedPreferenceChangeListener value) {
|
||||
pref.registerOnSharedPreferenceChangeListener(value);
|
||||
}
|
||||
|
||||
public void unregisterPreferenceChangedListener(SharedPreferences.OnSharedPreferenceChangeListener value) {
|
||||
unregisterPreferenceChangedListener(_prefApp, value);
|
||||
}
|
||||
|
||||
public void unregisterPreferenceChangedListener(final SharedPreferences pref, SharedPreferences.OnSharedPreferenceChangeListener value) {
|
||||
pref.unregisterOnSharedPreferenceChangeListener(value);
|
||||
}
|
||||
|
||||
public SharedPreferences getDefaultPreferences() {
|
||||
return _prefApp;
|
||||
}
|
||||
|
||||
public SharedPreferences.Editor getDefaultPreferencesEditor() {
|
||||
return _prefApp.edit();
|
||||
}
|
||||
|
||||
public String getDefaultPreferencesName() {
|
||||
return _prefAppName;
|
||||
}
|
||||
|
||||
|
||||
private SharedPreferences gp(final SharedPreferences... pref) {
|
||||
return (pref != null && pref.length > 0 ? pref[0] : _prefApp);
|
||||
}
|
||||
|
||||
//#################################
|
||||
//## Getter for resources
|
||||
//#################################
|
||||
public String rstr(@StringRes int stringKeyResourceId) {
|
||||
return _context.getString(stringKeyResourceId);
|
||||
}
|
||||
|
||||
public int rcolor(@ColorRes int resColorId) {
|
||||
return ContextCompat.getColor(_context, resColorId);
|
||||
}
|
||||
|
||||
|
||||
//#################################
|
||||
//## Getter & Setter for String
|
||||
//#################################
|
||||
public void setString(@StringRes int keyResourceId, String value, final SharedPreferences... pref) {
|
||||
gp(pref).edit().putString(rstr(keyResourceId), value).apply();
|
||||
}
|
||||
|
||||
public void setString(String key, String value, final SharedPreferences... pref) {
|
||||
gp(pref).edit().putString(key, value).apply();
|
||||
}
|
||||
|
||||
public void setString(@StringRes int keyResourceId, @StringRes int defaultValueResourceId, final SharedPreferences... pref) {
|
||||
gp(pref).edit().putString(rstr(keyResourceId), rstr(defaultValueResourceId)).apply();
|
||||
}
|
||||
|
||||
public String getString(@StringRes int keyResourceId, String defaultValue, final SharedPreferences... pref) {
|
||||
return gp(pref).getString(rstr(keyResourceId), defaultValue);
|
||||
}
|
||||
|
||||
public String getString(@StringRes int keyResourceId, @StringRes int defaultValueResourceId, final SharedPreferences... pref) {
|
||||
return gp(pref).getString(rstr(keyResourceId), rstr(defaultValueResourceId));
|
||||
}
|
||||
|
||||
public String getString(String key, String defaultValue, final SharedPreferences... pref) {
|
||||
return gp(pref).getString(key, defaultValue);
|
||||
}
|
||||
|
||||
public String getString(@StringRes int keyResourceId, String defaultValue, @StringRes int keyResourceIdDefaultValue, final SharedPreferences... pref) {
|
||||
return gp(pref).getString(rstr(keyResourceId), rstr(keyResourceIdDefaultValue));
|
||||
}
|
||||
|
||||
public void setStringArray(@StringRes int keyResourceId, Object[] values, final SharedPreferences... pref) {
|
||||
setStringArray(rstr(keyResourceId), values, gp(pref));
|
||||
}
|
||||
|
||||
public void setStringArray(String key, Object[] values, final SharedPreferences... pref) {
|
||||
setStringArray(key, values, gp(pref));
|
||||
}
|
||||
|
||||
private void setStringArray(String key, Object[] values, final SharedPreferences pref) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (Object value : values) {
|
||||
sb.append(ARRAY_SEPARATOR);
|
||||
sb.append(value.toString().replace(ARRAY_SEPARATOR, ARRAY_SEPARATOR_SUBSTITUTE));
|
||||
}
|
||||
setString(key, sb.toString().replaceFirst(ARRAY_SEPARATOR, ""), pref);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public String[] getStringArray(@StringRes int keyResourceId, final SharedPreferences... pref) {
|
||||
return getStringArray(rstr(keyResourceId), gp(pref));
|
||||
}
|
||||
|
||||
private String[] getStringArray(String key, final SharedPreferences... pref) {
|
||||
String value = gp(pref)
|
||||
.getString(key, ARRAY_SEPARATOR)
|
||||
.replace(ARRAY_SEPARATOR_SUBSTITUTE, ARRAY_SEPARATOR);
|
||||
if (value.equals(ARRAY_SEPARATOR)) {
|
||||
return new String[0];
|
||||
}
|
||||
return value.split(ARRAY_SEPARATOR);
|
||||
}
|
||||
|
||||
public void setStringList(@StringRes int keyResourceId, List<String> values, final SharedPreferences... pref) {
|
||||
setStringArray(rstr(keyResourceId), values.toArray(new String[values.size()]), pref);
|
||||
}
|
||||
|
||||
public void setStringList(String key, List<String> values, final SharedPreferences... pref) {
|
||||
setStringArray(key, values.toArray(new String[values.size()]), pref);
|
||||
}
|
||||
|
||||
public ArrayList<String> getStringList(@StringRes int keyResourceId, final SharedPreferences... pref) {
|
||||
return new ArrayList<>(Arrays.asList(getStringArray(rstr(keyResourceId), gp(pref))));
|
||||
}
|
||||
|
||||
public ArrayList<String> getStringList(String key, final SharedPreferences... pref) {
|
||||
return new ArrayList<>(Arrays.asList(getStringArray(key, gp(pref))));
|
||||
}
|
||||
|
||||
//#################################
|
||||
//## Getter & Setter for integer
|
||||
//#################################
|
||||
public void setInt(@StringRes int keyResourceId, int value, final SharedPreferences... pref) {
|
||||
gp(pref).edit().putInt(rstr(keyResourceId), value).apply();
|
||||
}
|
||||
|
||||
public void setInt(String key, int value, final SharedPreferences... pref) {
|
||||
gp(pref).edit().putInt(key, value).apply();
|
||||
}
|
||||
|
||||
public int getInt(@StringRes int keyResourceId, int defaultValue, final SharedPreferences... pref) {
|
||||
return gp(pref).getInt(rstr(keyResourceId), defaultValue);
|
||||
}
|
||||
|
||||
public int getInt(String key, int defaultValue, final SharedPreferences... pref) {
|
||||
return gp(pref).getInt(key, defaultValue);
|
||||
}
|
||||
|
||||
public int getIntOfStringPref(@StringRes int keyResId, int defaultValue, final SharedPreferences... pref) {
|
||||
return getIntOfStringPref(rstr(keyResId), defaultValue, gp(pref));
|
||||
}
|
||||
|
||||
public int getIntOfStringPref(String key, int defaultValue, final SharedPreferences... pref) {
|
||||
String strNum = getString(key, Integer.toString(defaultValue), gp(pref));
|
||||
return Integer.valueOf(strNum);
|
||||
}
|
||||
|
||||
|
||||
public void setIntArray(@StringRes int keyResourceId, Object[] values, final SharedPreferences... pref) {
|
||||
setIntArray(rstr(keyResourceId), values, gp(pref));
|
||||
}
|
||||
|
||||
public void setIntArray(String key, Object[] values, final SharedPreferences... pref) {
|
||||
setIntArray(key, values, gp(pref));
|
||||
}
|
||||
|
||||
private void setIntArray(String key, Object[] values, final SharedPreferences pref) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (Object value : values) {
|
||||
sb.append(ARRAY_SEPARATOR);
|
||||
sb.append(value.toString());
|
||||
}
|
||||
setString(key, sb.toString().replaceFirst(ARRAY_SEPARATOR, ""), pref);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public Integer[] getIntArray(@StringRes int keyResourceId, final SharedPreferences... pref) {
|
||||
return getIntArray(rstr(keyResourceId), gp(pref));
|
||||
}
|
||||
|
||||
private Integer[] getIntArray(String key, final SharedPreferences... pref) {
|
||||
String value = gp(pref).getString(key, ARRAY_SEPARATOR);
|
||||
if (value.equals(ARRAY_SEPARATOR)) {
|
||||
return new Integer[0];
|
||||
}
|
||||
String[] split = value.split(ARRAY_SEPARATOR);
|
||||
Integer[] ret = new Integer[split.length];
|
||||
for (int i = 0; i < ret.length; i++) {
|
||||
ret[i] = Integer.parseInt(split[i]);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
public void setIntList(@StringRes int keyResourceId, List<Integer> values, final SharedPreferences... pref) {
|
||||
setIntArray(rstr(keyResourceId), values.toArray(new Integer[values.size()]), pref);
|
||||
}
|
||||
|
||||
public void setIntList(String key, List<Integer> values, final SharedPreferences... pref) {
|
||||
setIntArray(key, values.toArray(new Integer[values.size()]), pref);
|
||||
}
|
||||
|
||||
public ArrayList<Integer> getIntList(@StringRes int keyResourceId, final SharedPreferences... pref) {
|
||||
return new ArrayList<>(Arrays.asList(getIntArray(rstr(keyResourceId), gp(pref))));
|
||||
}
|
||||
|
||||
public ArrayList<Integer> getIntList(String key, final SharedPreferences... pref) {
|
||||
return new ArrayList<>(Arrays.asList(getIntArray(key, gp(pref))));
|
||||
}
|
||||
|
||||
|
||||
//#################################
|
||||
//## Getter & Setter for Long
|
||||
//#################################
|
||||
public void setLong(@StringRes int keyResourceId, long value, final SharedPreferences... pref) {
|
||||
gp(pref).edit().putLong(rstr(keyResourceId), value).apply();
|
||||
}
|
||||
|
||||
public void setLong(String key, long value, final SharedPreferences... pref) {
|
||||
gp(pref).edit().putLong(key, value).apply();
|
||||
}
|
||||
|
||||
public long getLong(@StringRes int keyResourceId, long defaultValue, final SharedPreferences... pref) {
|
||||
return gp(pref).getLong(rstr(keyResourceId), defaultValue);
|
||||
}
|
||||
|
||||
public long getLong(String key, long defaultValue, final SharedPreferences... pref) {
|
||||
return gp(pref).getLong(key, defaultValue);
|
||||
}
|
||||
|
||||
//#################################
|
||||
//## Getter & Setter for Float
|
||||
//#################################
|
||||
public void setFloat(@StringRes int keyResourceId, float value, final SharedPreferences... pref) {
|
||||
gp(pref).edit().putFloat(rstr(keyResourceId), value).apply();
|
||||
}
|
||||
|
||||
public void setFloat(String key, float value, final SharedPreferences... pref) {
|
||||
gp(pref).edit().putFloat(key, value).apply();
|
||||
}
|
||||
|
||||
public float getFloat(@StringRes int keyResourceId, float defaultValue, final SharedPreferences... pref) {
|
||||
return gp(pref).getFloat(rstr(keyResourceId), defaultValue);
|
||||
}
|
||||
|
||||
public float getFloat(String key, float defaultValue, final SharedPreferences... pref) {
|
||||
return gp(pref).getFloat(key, defaultValue);
|
||||
}
|
||||
|
||||
//#################################
|
||||
//## Getter & Setter for Double
|
||||
//#################################
|
||||
public void setDouble(@StringRes int keyResourceId, double value, final SharedPreferences... pref) {
|
||||
setLong(rstr(keyResourceId), Double.doubleToRawLongBits(value));
|
||||
}
|
||||
|
||||
public void setDouble(String key, double value, final SharedPreferences... pref) {
|
||||
setLong(key, Double.doubleToRawLongBits(value));
|
||||
}
|
||||
|
||||
public double getDouble(@StringRes int keyResourceId, double defaultValue, final SharedPreferences... pref) {
|
||||
return getDouble(rstr(keyResourceId), defaultValue, gp(pref));
|
||||
}
|
||||
|
||||
public double getDouble(String key, double defaultValue, final SharedPreferences... pref) {
|
||||
return Double.longBitsToDouble(getLong(key, Double.doubleToRawLongBits(defaultValue), gp(pref)));
|
||||
}
|
||||
|
||||
//#################################
|
||||
//## Getter & Setter for boolean
|
||||
//#################################
|
||||
public void setBool(@StringRes int keyResourceId, boolean value, final SharedPreferences... pref) {
|
||||
gp(pref).edit().putBoolean(rstr(keyResourceId), value).apply();
|
||||
}
|
||||
|
||||
public void setBool(String key, boolean value, final SharedPreferences... pref) {
|
||||
gp(pref).edit().putBoolean(key, value).apply();
|
||||
}
|
||||
|
||||
public boolean getBool(@StringRes int keyResourceId, boolean defaultValue, final SharedPreferences... pref) {
|
||||
return gp(pref).getBoolean(rstr(keyResourceId), defaultValue);
|
||||
}
|
||||
|
||||
public boolean getBool(String key, boolean defaultValue, final SharedPreferences... pref) {
|
||||
return gp(pref).getBoolean(key, defaultValue);
|
||||
}
|
||||
|
||||
//#################################
|
||||
//## Getter & Setter for Color
|
||||
//#################################
|
||||
public int getColor(String key, @ColorRes int defaultColor, final SharedPreferences... pref) {
|
||||
return gp(pref).getInt(key, rcolor(defaultColor));
|
||||
}
|
||||
|
||||
public int getColor(@StringRes int keyResourceId, @ColorRes int defaultColor, final SharedPreferences... pref) {
|
||||
return gp(pref).getInt(rstr(keyResourceId), rcolor(defaultColor));
|
||||
}
|
||||
}
|
86
app/src/main/java/net/gsantner/opoc/util/Callback.java
Normal file
|
@ -0,0 +1,86 @@
|
|||
/*#######################################################
|
||||
*
|
||||
* Maintained 2018-2023 by Gregor Santner <gsantner AT mailbox DOT org>
|
||||
*
|
||||
* License of this file: Apache 2.0
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* https://github.com/gsantner/opoc/#licensing
|
||||
*
|
||||
#########################################################*/
|
||||
package net.gsantner.opoc.util;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public class Callback {
|
||||
|
||||
public interface a0 {
|
||||
void callback();
|
||||
}
|
||||
|
||||
public interface a1<A> {
|
||||
void callback(A arg1);
|
||||
}
|
||||
|
||||
public interface a2<A, B> {
|
||||
void callback(A arg1, B arg2);
|
||||
}
|
||||
|
||||
public interface a3<A, B, C> {
|
||||
void callback(A arg1, B arg2, C arg3);
|
||||
}
|
||||
|
||||
public interface a4<A, B, C, D> {
|
||||
void callback(A arg1, B arg2, C arg3, D arg4);
|
||||
}
|
||||
|
||||
public interface a5<A, B, C, D, E> {
|
||||
void callback(A arg1, B arg2, C arg3, D arg4, E arg5);
|
||||
}
|
||||
|
||||
public interface b0 {
|
||||
boolean callback();
|
||||
}
|
||||
|
||||
public interface b1<A> {
|
||||
boolean callback(A arg1);
|
||||
}
|
||||
|
||||
public interface b2<A, B> {
|
||||
boolean callback(A arg1, B arg2);
|
||||
}
|
||||
|
||||
public interface b3<A, B, C> {
|
||||
boolean callback(A arg1, B arg2, C arg3);
|
||||
}
|
||||
|
||||
public interface b4<A, B, C, D> {
|
||||
boolean callback(A arg1, B arg2, C arg3, D arg4);
|
||||
}
|
||||
|
||||
public interface b5<A, B, C, D, E> {
|
||||
boolean callback(A arg1, B arg2, C arg3, D arg4, E arg5);
|
||||
}
|
||||
|
||||
public interface s0 {
|
||||
String callback();
|
||||
}
|
||||
|
||||
public interface s1<A> {
|
||||
String callback(A arg1);
|
||||
}
|
||||
|
||||
public interface s2<A, B> {
|
||||
String callback(A arg1, B arg2);
|
||||
}
|
||||
|
||||
public interface s3<A, B, C> {
|
||||
String callback(A arg1, B arg2, C arg3);
|
||||
}
|
||||
|
||||
public interface s4<A, B, C, D> {
|
||||
String callback(A arg1, B arg2, C arg3, D arg4);
|
||||
}
|
||||
|
||||
public interface s5<A, B, C, D, E> {
|
||||
String callback(A arg1, B arg2, C arg3, D arg4, E arg5);
|
||||
}
|
||||
}
|
57
app/src/main/java/net/gsantner/opoc/util/DownloadTask.java
Normal file
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
This file is part of the dandelion*.
|
||||
|
||||
dandelion* is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
dandelion* is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with the dandelion*.
|
||||
|
||||
If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package net.gsantner.opoc.util;
|
||||
|
||||
import android.os.AsyncTask;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
|
||||
import info.guardianproject.netcipher.NetCipher;
|
||||
|
||||
public class DownloadTask extends AsyncTask<String, Void, Boolean> {
|
||||
private File _targetFile;
|
||||
private Callback.a2<Boolean, File> _callback;
|
||||
|
||||
public DownloadTask(File targetFile, @Nullable Callback.a2<Boolean, File> callback) {
|
||||
_targetFile = targetFile;
|
||||
_callback = callback;
|
||||
}
|
||||
|
||||
protected Boolean doInBackground(String... urls) {
|
||||
if (urls != null && urls.length > 0 && urls[0] != null) {
|
||||
try {
|
||||
HttpsURLConnection connection = NetCipher.getHttpsURLConnection(urls[0]);
|
||||
return NetworkUtils.downloadFile(null, _targetFile, connection, null);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected void onPostExecute(Boolean result) {
|
||||
if (_callback != null) {
|
||||
_callback.callback(result, _targetFile);
|
||||
}
|
||||
}
|
||||
}
|
527
app/src/main/java/net/gsantner/opoc/util/FileUtils.java
Normal file
|
@ -0,0 +1,527 @@
|
|||
/*#######################################################
|
||||
*
|
||||
* Maintained 2017-2023 by Gregor Santner <gsantner AT mailbox DOT org>
|
||||
*
|
||||
* License of this file: Apache 2.0
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* https://github.com/gsantner/opoc/#licensing
|
||||
*
|
||||
#########################################################*/
|
||||
package net.gsantner.opoc.util;
|
||||
|
||||
|
||||
import android.text.TextUtils;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.FileReader;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.net.URLConnection;
|
||||
import java.text.DecimalFormat;
|
||||
import java.text.DecimalFormatSymbols;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@SuppressWarnings({"WeakerAccess", "unused", "SameParameterValue", "SpellCheckingInspection", "deprecation", "TryFinallyCanBeTryWithResources"})
|
||||
public class FileUtils {
|
||||
// Used on methods like copyFile(src, dst)
|
||||
private static final int BUFFER_SIZE = 4096;
|
||||
|
||||
public static String readTextFileFast(final File file) {
|
||||
try {
|
||||
return new String(readCloseStreamWithSize(new FileInputStream(file), (int) file.length()));
|
||||
} catch (FileNotFoundException e) {
|
||||
System.err.println("readTextFileFast: File " + file + " not found.");
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
public static byte[] readCloseStreamWithSize(final InputStream stream, int size) {
|
||||
byte[] data = new byte[size];
|
||||
try (DataInputStream dis = new DataInputStream(stream)) {
|
||||
dis.readFully(data);
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
public static String readTextFile(final File file) {
|
||||
try {
|
||||
return readCloseTextStream(new FileInputStream(file));
|
||||
} catch (FileNotFoundException e) {
|
||||
System.err.println("readTextFile: File " + file + " not found.");
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
public static String readCloseTextStream(final InputStream stream) {
|
||||
return readCloseTextStream(stream, true).get(0);
|
||||
}
|
||||
|
||||
public static List<String> readCloseTextStream(final InputStream stream, boolean concatToOneString) {
|
||||
final ArrayList<String> lines = new ArrayList<>();
|
||||
BufferedReader reader = null;
|
||||
String line = "";
|
||||
try {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
reader = new BufferedReader(new InputStreamReader(stream));
|
||||
|
||||
while ((line = reader.readLine()) != null) {
|
||||
if (concatToOneString) {
|
||||
sb.append(line).append('\n');
|
||||
} else {
|
||||
lines.add(line);
|
||||
}
|
||||
}
|
||||
line = sb.toString();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
if (reader != null) {
|
||||
try {
|
||||
reader.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (concatToOneString) {
|
||||
lines.clear();
|
||||
lines.add(line);
|
||||
}
|
||||
return lines;
|
||||
}
|
||||
|
||||
public static byte[] readBinaryFile(final File file) {
|
||||
try {
|
||||
return readCloseBinaryStream(new FileInputStream(file), (int) file.length());
|
||||
} catch (FileNotFoundException e) {
|
||||
System.err.println("readBinaryFile: File " + file + " not found.");
|
||||
}
|
||||
|
||||
return new byte[0];
|
||||
}
|
||||
|
||||
public static byte[] readCloseBinaryStream(final InputStream stream, int byteCount) {
|
||||
final ArrayList<String> lines = new ArrayList<>();
|
||||
BufferedInputStream reader = null;
|
||||
byte[] buf = new byte[byteCount];
|
||||
int totalBytesRead = 0;
|
||||
try {
|
||||
reader = new BufferedInputStream(stream);
|
||||
while (totalBytesRead < byteCount) {
|
||||
int bytesRead = reader.read(buf, totalBytesRead, byteCount - totalBytesRead);
|
||||
if (bytesRead > 0) {
|
||||
totalBytesRead = totalBytesRead + bytesRead;
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
if (reader != null) {
|
||||
try {
|
||||
reader.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
// Read binary stream (of unknown conf size)
|
||||
public static byte[] readCloseBinaryStream(final InputStream stream) {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
try {
|
||||
byte[] buffer = new byte[BUFFER_SIZE];
|
||||
int read;
|
||||
while ((read = stream.read(buffer)) != -1) {
|
||||
baos.write(buffer, 0, read);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
if (stream != null) {
|
||||
try {
|
||||
stream.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
return baos.toByteArray();
|
||||
}
|
||||
|
||||
public static boolean writeFile(final File file, byte[] data) {
|
||||
try {
|
||||
OutputStream output = null;
|
||||
try {
|
||||
output = new BufferedOutputStream(new FileOutputStream(file));
|
||||
output.write(data);
|
||||
output.flush();
|
||||
return true;
|
||||
} finally {
|
||||
if (output != null) {
|
||||
output.close();
|
||||
}
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean writeFile(final File file, final String content) {
|
||||
BufferedWriter writer = null;
|
||||
try {
|
||||
if (!file.getParentFile().isDirectory() && !file.getParentFile().mkdirs())
|
||||
return false;
|
||||
|
||||
writer = new BufferedWriter(new FileWriter(file));
|
||||
writer.write(content);
|
||||
writer.flush();
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
} finally {
|
||||
if (writer != null) {
|
||||
try {
|
||||
writer.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean copyFile(final File src, final File dst) {
|
||||
// Just touch file if src is empty
|
||||
if (src.length() == 0) {
|
||||
return touch(dst);
|
||||
}
|
||||
|
||||
InputStream is = null;
|
||||
FileOutputStream os = null;
|
||||
try {
|
||||
try {
|
||||
is = new FileInputStream(src);
|
||||
os = new FileOutputStream(dst);
|
||||
byte[] buf = new byte[BUFFER_SIZE];
|
||||
int len;
|
||||
while ((len = is.read(buf)) > 0) {
|
||||
os.write(buf, 0, len);
|
||||
}
|
||||
return true;
|
||||
} finally {
|
||||
if (is != null) {
|
||||
is.close();
|
||||
}
|
||||
if (os != null) {
|
||||
os.close();
|
||||
}
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean copyFile(final File src, final FileOutputStream os) {
|
||||
InputStream is = null;
|
||||
try {
|
||||
try {
|
||||
is = new FileInputStream(src);
|
||||
byte[] buf = new byte[BUFFER_SIZE];
|
||||
int len;
|
||||
while ((len = is.read(buf)) > 0) {
|
||||
os.write(buf, 0, len);
|
||||
}
|
||||
return true;
|
||||
} finally {
|
||||
if (is != null) {
|
||||
is.close();
|
||||
}
|
||||
if (os != null) {
|
||||
os.close();
|
||||
}
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Returns -1 if the file did not contain any of the needles, otherwise,
|
||||
// the index of which needle was found in the contents of the file.
|
||||
//
|
||||
// Needless MUST be in lower-case.
|
||||
public static int fileContains(File file, String... needles) {
|
||||
try {
|
||||
FileInputStream in = new FileInputStream(file);
|
||||
|
||||
int i;
|
||||
String line;
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
|
||||
while ((line = reader.readLine()) != null) {
|
||||
for (i = 0; i != needles.length; ++i)
|
||||
if (line.toLowerCase(Locale.ROOT).contains(needles[i])) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
in.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static boolean deleteRecursive(final File file) {
|
||||
boolean ok = true;
|
||||
if (file.exists()) {
|
||||
if (file.isDirectory()) {
|
||||
for (File child : file.listFiles())
|
||||
ok &= deleteRecursive(child);
|
||||
}
|
||||
ok &= file.delete();
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
// Example: Check if this is maybe a conf: (str, "jpg", "png", "jpeg")
|
||||
public static boolean hasExtension(String str, String... extensions) {
|
||||
String lc = str.toLowerCase(Locale.ROOT);
|
||||
for (String extension : extensions) {
|
||||
if (lc.endsWith("." + extension.toLowerCase(Locale.ROOT))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean renameFile(File srcFile, File destFile) {
|
||||
if (srcFile.getAbsolutePath().equals(destFile.getAbsolutePath())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// renameTo will fail in case of case-changed filename in same dir.Even on case-sensitive FS!!!
|
||||
if (srcFile.getParent().equals(destFile.getParent()) && srcFile.getName().toLowerCase(Locale.getDefault()).equals(destFile.getName().toLowerCase(Locale.getDefault()))) {
|
||||
File tmpFile = new File(destFile.getParent(), UUID.randomUUID().getLeastSignificantBits() + ".tmp");
|
||||
if (!tmpFile.exists()) {
|
||||
renameFile(srcFile, tmpFile);
|
||||
srcFile = tmpFile;
|
||||
}
|
||||
}
|
||||
|
||||
if (!srcFile.renameTo(destFile)) {
|
||||
if (copyFile(srcFile, destFile) && !srcFile.delete()) {
|
||||
if (!destFile.delete()) {
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@SuppressWarnings("ResultOfMethodCallIgnored")
|
||||
public static boolean renameFileInSameFolder(File srcFile, String destFilename) {
|
||||
return renameFile(srcFile, new File(srcFile.getParent(), destFilename));
|
||||
}
|
||||
|
||||
public static boolean touch(File file) {
|
||||
try {
|
||||
if (!file.exists()) {
|
||||
new FileOutputStream(file).close();
|
||||
}
|
||||
return file.setLastModified(System.currentTimeMillis());
|
||||
} catch (IOException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Get relative path to specified destination
|
||||
public static String relativePath(File src, File dest) {
|
||||
try {
|
||||
String[] srcSplit = (src.isDirectory() ? src : src.getParentFile()).getCanonicalPath().split(Pattern.quote(File.separator));
|
||||
String[] destSplit = dest.getCanonicalPath().split(Pattern.quote(File.separator));
|
||||
StringBuilder sb = new StringBuilder();
|
||||
int i = 0;
|
||||
|
||||
for (; i < destSplit.length && i < srcSplit.length; ++i) {
|
||||
if (!destSplit[i].equals(srcSplit[i]))
|
||||
break;
|
||||
}
|
||||
if (i != srcSplit.length) {
|
||||
for (int iUpperDir = i; iUpperDir < srcSplit.length; ++iUpperDir) {
|
||||
sb.append("..");
|
||||
sb.append(File.separator);
|
||||
}
|
||||
}
|
||||
for (; i < destSplit.length; ++i) {
|
||||
sb.append(destSplit[i]);
|
||||
sb.append(File.separator);
|
||||
}
|
||||
if (!dest.getPath().endsWith("/") && !dest.getPath().endsWith("\\")) {
|
||||
sb.delete(sb.length() - File.separator.length(), sb.length());
|
||||
}
|
||||
return sb.toString();
|
||||
} catch (IOException | NullPointerException exception) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to detect MimeType by backwards compatible methods
|
||||
*/
|
||||
public static String getMimeType(File file) {
|
||||
String guess = null;
|
||||
if (file != null) {
|
||||
if (file.exists() && file.isFile()) {
|
||||
InputStream is = null;
|
||||
try {
|
||||
is = new BufferedInputStream(new FileInputStream(file));
|
||||
guess = URLConnection.guessContentTypeFromStream(is);
|
||||
} catch (Exception ignored) {
|
||||
} finally {
|
||||
if (is != null) {
|
||||
try {
|
||||
is.close();
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String filename = file.getName().replace(".jenc", "");
|
||||
int dot = filename.lastIndexOf(".") + 1;
|
||||
if (dot > 0 && dot < filename.length()) {
|
||||
switch (filename.substring(dot)) {
|
||||
case "md":
|
||||
case "markdown":
|
||||
case "mkd":
|
||||
case "mdown":
|
||||
case "mkdn":
|
||||
case "mdwn":
|
||||
case "rmd":
|
||||
guess = "text/markdown";
|
||||
break;
|
||||
case "txt":
|
||||
guess = "text/plain";
|
||||
break;
|
||||
case "webp":
|
||||
guess = "image/webp";
|
||||
break;
|
||||
case "jpg":
|
||||
case "jpeg":
|
||||
guess = "image/jpeg";
|
||||
break;
|
||||
case "png":
|
||||
guess = "image/png";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (TextUtils.isEmpty(guess)) {
|
||||
guess = URLConnection.guessContentTypeFromName(filename);
|
||||
}
|
||||
}
|
||||
|
||||
return TextUtils.isEmpty(guess) ? "*/*" : guess;
|
||||
}
|
||||
|
||||
public static boolean isTextFile(File file) {
|
||||
String mime = getMimeType(file);
|
||||
return mime != null && mime.startsWith("text/");
|
||||
}
|
||||
|
||||
/**
|
||||
* Analyze given textfile and retrieve multiple information from it
|
||||
* Information is written back to the {@link AtomicInteger} parameters
|
||||
*/
|
||||
public static void retrieveTextFileSummary(File file, AtomicInteger numCharacters, AtomicInteger numLines, AtomicInteger numWords) {
|
||||
BufferedReader br = null;
|
||||
try {
|
||||
br = new BufferedReader(new FileReader(file));
|
||||
String line;
|
||||
while ((line = br.readLine()) != null) {
|
||||
numLines.getAndIncrement();
|
||||
numCharacters.getAndSet(numCharacters.get() + line.length());
|
||||
if (!line.equals("")) {
|
||||
numWords.getAndSet(numWords.get() + line.split("\\s+").length);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
numCharacters.set(-1);
|
||||
numLines.set(-1);
|
||||
numWords.set(-1);
|
||||
} finally {
|
||||
if (br != null) {
|
||||
try {
|
||||
br.close();
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Format filesize to human readable format
|
||||
* Get size in bytes e.g. from {@link File} using {@code File#length()}
|
||||
*/
|
||||
public static String getReadableFileSize(long size, boolean abbreviation) {
|
||||
if (size <= 0) {
|
||||
return "0B";
|
||||
}
|
||||
String[] units = abbreviation ? new String[]{"B", "kB", "MB", "GB", "TB"} : new String[]{"Bytes", "Kilobytes", "Megabytes", "Gigabytes", "Terabytes"};
|
||||
int unit = (int) (Math.log10(size) / Math.log10(1024));
|
||||
return new DecimalFormat("#,##0.#", DecimalFormatSymbols.getInstance(Locale.ENGLISH)).format(size / Math.pow(1024, unit)) + " " + units[unit];
|
||||
}
|
||||
|
||||
public static int[] getTimeDiffHMS(long now, long past) {
|
||||
int[] ret = new int[3];
|
||||
long diff = Math.abs(now - past);
|
||||
ret[0] = (int) (diff / (1000 * 60 * 60)); // hours
|
||||
ret[1] = (int) (diff / (1000 * 60)) % 60; // min
|
||||
ret[2] = (int) (diff / 1000) % 60; // sec
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static String getHumanReadableByteCountSI(final long bytes) {
|
||||
if (bytes < 1000) {
|
||||
return String.format(Locale.getDefault(), "%d%s", bytes, "B");
|
||||
} else if (bytes < 1000000) {
|
||||
return String.format(Locale.getDefault(), "%.2f%s", (bytes / 1000f), "KB");
|
||||
} else if (bytes < 1000000000) {
|
||||
return String.format(Locale.getDefault(), "%.2f%s", (bytes / 1000000f), "MB");
|
||||
} else if (bytes < 1000000000000L) {
|
||||
return String.format(Locale.getDefault(), "%.2f%s", (bytes / 1000000000f), "GB");
|
||||
} else {
|
||||
return String.format(Locale.getDefault(), "%.2f%s", (bytes / 1000000000000f), "TB");
|
||||
}
|
||||
}
|
||||
|
||||
public static File join(File file, String... childSegments) {
|
||||
for (final String s : childSegments != null ? childSegments : new String[0]) {
|
||||
file = new File(file, s);
|
||||
}
|
||||
return file;
|
||||
}
|
||||
}
|
233
app/src/main/java/net/gsantner/opoc/util/NetworkUtils.java
Normal file
|
@ -0,0 +1,233 @@
|
|||
/*#######################################################
|
||||
*
|
||||
* Maintained 2017-2023 by Gregor Santner <gsantner AT mailbox DOT org>
|
||||
*
|
||||
* License of this file: Apache 2.0
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* https://github.com/gsantner/opoc/#licensing
|
||||
*
|
||||
#########################################################*/
|
||||
package net.gsantner.opoc.util;
|
||||
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.net.URLDecoder;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@SuppressWarnings({"WeakerAccess", "unused", "SameParameterValue", "SpellCheckingInspection", "deprecation"})
|
||||
public class NetworkUtils {
|
||||
private static final String UTF8 = "UTF-8";
|
||||
public static final String GET = "GET";
|
||||
public static final String POST = "POST";
|
||||
public static final String PATCH = "PATCH";
|
||||
|
||||
private final static int BUFFER_SIZE = 4096;
|
||||
|
||||
// Downloads a file from the give url to the output file
|
||||
// Creates the file's parent directory if it doesn't exist
|
||||
public static boolean downloadFile(final String url, final File out) {
|
||||
return downloadFile(url, out, null);
|
||||
}
|
||||
|
||||
public static boolean downloadFile(final String url, final File out, final Callback.a1<Float> progressCallback) {
|
||||
try {
|
||||
return downloadFile(new URL(url), out, progressCallback);
|
||||
} catch (MalformedURLException e) {
|
||||
// Won't happen
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean downloadFile(final URL url, final File outFile, final Callback.a1<Float> progressCallback) {
|
||||
return downloadFile(url, outFile, null, progressCallback);
|
||||
}
|
||||
|
||||
public static boolean downloadFile(final URL url, final File outFile, HttpURLConnection connection, final Callback.a1<Float> progressCallback) {
|
||||
InputStream input = null;
|
||||
OutputStream output = null;
|
||||
try {
|
||||
if (connection == null) {
|
||||
connection = (HttpURLConnection) url.openConnection();
|
||||
}
|
||||
connection.connect();
|
||||
input = connection.getResponseCode() < HttpURLConnection.HTTP_BAD_REQUEST
|
||||
? connection.getInputStream() : connection.getErrorStream();
|
||||
|
||||
|
||||
if (!outFile.getParentFile().isDirectory())
|
||||
if (!outFile.getParentFile().mkdirs())
|
||||
return false;
|
||||
output = new FileOutputStream(outFile);
|
||||
|
||||
int count;
|
||||
int written = 0;
|
||||
final float invLength = 1f / connection.getContentLength();
|
||||
|
||||
byte[] data = new byte[BUFFER_SIZE];
|
||||
while ((count = input.read(data)) != -1) {
|
||||
output.write(data, 0, count);
|
||||
if (invLength != -1f && progressCallback != null) {
|
||||
written += count;
|
||||
progressCallback.callback(written * invLength);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
} finally {
|
||||
try {
|
||||
if (output != null)
|
||||
output.close();
|
||||
if (input != null)
|
||||
input.close();
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
if (connection != null)
|
||||
connection.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
// No parameters, method can be GET, POST, etc.
|
||||
public static String performCall(final String url, final String method) {
|
||||
try {
|
||||
return performCall(new URL(url), method, "");
|
||||
} catch (MalformedURLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
public static String performCall(final String url, final String method, final String data) {
|
||||
try {
|
||||
return performCall(new URL(url), method, data);
|
||||
} catch (MalformedURLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
// URL encoded parameters
|
||||
public static String performCall(final String url, final String method, final HashMap<String, String> params) {
|
||||
try {
|
||||
return performCall(new URL(url), method, encodeQuery(params));
|
||||
} catch (UnsupportedEncodingException | MalformedURLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
// Defaults to POST
|
||||
public static String performCall(final String url, final JSONObject json) {
|
||||
return performCall(url, POST, json);
|
||||
}
|
||||
|
||||
public static String performCall(final String url, final String method, final JSONObject json) {
|
||||
try {
|
||||
return performCall(new URL(url), method, json.toString());
|
||||
} catch (MalformedURLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
private static String performCall(final URL url, final String method, final String data) {
|
||||
return performCall(url, method, data, null);
|
||||
}
|
||||
|
||||
@SuppressWarnings("CharsetObjectCanBeUsed")
|
||||
private static String performCall(final URL url, final String method, final String data, final HttpURLConnection existingConnection) {
|
||||
try {
|
||||
final HttpURLConnection connection = existingConnection != null
|
||||
? existingConnection : (HttpURLConnection) url.openConnection();
|
||||
connection.setRequestMethod(method);
|
||||
connection.setDoInput(true);
|
||||
|
||||
if (data != null && !data.isEmpty()) {
|
||||
connection.setDoOutput(true);
|
||||
final OutputStream output = connection.getOutputStream();
|
||||
output.write(data.getBytes(Charset.forName("UTF-8")));
|
||||
output.flush();
|
||||
output.close();
|
||||
}
|
||||
|
||||
InputStream input = connection.getResponseCode() < HttpURLConnection.HTTP_BAD_REQUEST
|
||||
? connection.getInputStream() : connection.getErrorStream();
|
||||
|
||||
return FileUtils.readCloseTextStream(connection.getInputStream());
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
private static String encodeQuery(final HashMap<String, String> params) throws UnsupportedEncodingException {
|
||||
final StringBuilder result = new StringBuilder();
|
||||
boolean first = true;
|
||||
for (Map.Entry<String, String> entry : params.entrySet()) {
|
||||
if (first) first = false;
|
||||
else result.append("&");
|
||||
|
||||
result.append(URLEncoder.encode(entry.getKey(), UTF8));
|
||||
result.append("=");
|
||||
result.append(URLEncoder.encode(entry.getValue(), UTF8));
|
||||
}
|
||||
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
public static HashMap<String, String> getDataMap(final String query) {
|
||||
final HashMap<String, String> result = new HashMap<>();
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
String name = "";
|
||||
|
||||
try {
|
||||
for (int i = 0; i < query.length(); i++) {
|
||||
char c = query.charAt(i);
|
||||
switch (c) {
|
||||
case '=':
|
||||
name = URLDecoder.decode(sb.toString(), UTF8);
|
||||
sb.setLength(0);
|
||||
break;
|
||||
case '&':
|
||||
result.put(name, URLDecoder.decode(sb.toString(), UTF8));
|
||||
sb.setLength(0);
|
||||
break;
|
||||
default:
|
||||
sb.append(c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!name.isEmpty())
|
||||
result.put(name, URLDecoder.decode(sb.toString(), UTF8));
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static void httpGetAsync(final String url, final Callback.a1<String> callback) {
|
||||
new Thread(() -> {
|
||||
try {
|
||||
String c = NetworkUtils.performCall(url, GET);
|
||||
callback.callback(c);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
}
|