diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index afecfe1a..5ac91667 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -21,7 +21,4 @@ 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. - - About logging: - https://gsantner.net/android-contribution-guide/?packageid=com.github.dfa.diaspora_android&name=dandelion&web=https://github.com/gsantner/dandelion#logcat --> diff --git a/.github/workflows/build-android-project.yml b/.github/workflows/build-android-project.yml new file mode 100644 index 00000000..07923101 --- /dev/null +++ b/.github/workflows/build-android-project.yml @@ -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 diff --git a/.gitignore b/.gitignore index 6aa6839b..59057232 100644 --- a/.gitignore +++ b/.gitignore @@ -36,6 +36,7 @@ tmp/ ### Gradle ### .gradle build/ +dist/ gradle-app.setting # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index b4204f72..00000000 --- a/.travis.yml +++ /dev/null @@ -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" diff --git a/CHANGELOG.md b/CHANGELOG.md index d4950a5c..a5861d19 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,20 @@ +### 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 @@ -6,7 +23,6 @@ **New features:** - All new Aspects and Tags, using a searchable dialog -- A new home - project blog/page: **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. diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 7969e4cd..16e6a136 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -1,6 +1,5 @@ -* **[Gregor Santner](http://gsantner.net)**
~° Current developer of dandelion +* **[Gregor Santner](http://github.com/gsantner)**
~° Development of dandelion * **[Paul Schaub](https://github.com/vanitasvitae)**
~° Development of dandelion * **[Martín Vukovic](martinvukovic AT protonmail DOT com)**
~° Diaspora Native WebApp * **[Gaukler Faun](https://github.com/scoute-dich)**
~° Diaspora Native WebApp additions @@ -30,3 +29,4 @@ Where: * **[Jean Lucas](jean AT 4ray DOT co)**
~° Spanish translation * **[asereze](https://github.com/asereze)**
~° Sardinian translation * **[Xosé M. Lamas](http://xmgz.eu)**
~° Galician translation +* **[massimiliano](https://framagit.org/massimiliano)**
~° Contributor diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..09b21c00 --- /dev/null +++ b/Makefile @@ -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 "@@' | sed 's@@@' | 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 "-----------------------------------------------------------------------------------" + diff --git a/NEWS.md b/NEWS.md new file mode 100644 index 00000000..6139d132 --- /dev/null +++ b/NEWS.md @@ -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 diff --git a/README.md b/README.md index 57ba4cc3..a061ee60 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,9 @@ -[![GitHub release](https://img.shields.io/github/tag/gsantner/dandelion.svg)](https://github.com/gsantner/dandelion/releases) -[![Build Status](https://travis-ci.org/gsantner/dandelion.svg?branch=master)](https://travis-ci.org/gsantner/dandelion) -[![Translate - with Stringlate](https://img.shields.io/badge/stringlate-translate-green.svg)](https://lonamiwebs.github.io/stringlate/translate?git=https%3A%2F%2Fgithub.com%2Fgsantner%2Fdandelion.git&mail=gro.xobliam@@rentnasg) -[![Chat - Matrix](https://img.shields.io/badge/chat-on%20matrix-blue.svg)](https://matrix.to/#/#dandelion:matrix.org) [![Chat - FreeNode IRC](https://img.shields.io/badge/chat-on%20irc-blue.svg)](https://kiwiirc.com/client/irc.freenode.net/?nick=dandelion-anon|?##dandelion) -[![Donate](https://img.shields.io/badge/donate-appreciation-orange.svg)](https://gsantner.net/supportme/?project=dandelion&source=readme) -[![Donate LiberaPay](https://img.shields.io/badge/donate-liberapay-orange.svg)](https://liberapay.com/gsantner/donate) +[![GitHub releases](https://img.shields.io/github/tag/gsantner/dandelion.svg)](https://github.com/gsantner/dandelion/releases) +[![GitHub downloads](https://img.shields.io/github/downloads/gsantner/dandelion/total.svg?logo=github&logoColor=lime)](https://github.com/gsantner/dandelion/releases) +[![Translate on Crowdin](https://img.shields.io/badge/translate-crowdin-green.svg)](https://crowdin.com/project/diaspora-for-android/invite) +[![Chat on Matrix](https://img.shields.io/badge/chat-matrix-blue.svg)](https://matrix.to/#/#dandelion:matrix.org) +[![GitHub CI](https://github.com/gsantner/dandelion/workflows/CI/badge.svg)](https://github.com/gsantner/dandelion/actions) +[![Codacy code quality](https://img.shields.io/codacy/grade/aff869c440bc48b7bd64680e97cbc453)](https://www.codacy.com/app/gsantner/dandelion) # dandelion\* @@ -39,12 +39,11 @@ dandelion\* requires access to the Internet and to external storage to be able t ## Contributions 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. See [gsantner's android contribution guide](https://gsantner.net/android-contribution-guide/?packageid=com.github.dfa.diaspora_android&name=dandelion&web=https://github.com/gsantner/dandelion&source=readme#logcat) for more information. +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. -Translations can be contributed on GitHub or via [E-Mail](https://gsantner.net/#contact). You can use Stringlate ([![Translate - with Stringlate](https://img.shields.io/badge/stringlate-translate-green.svg)](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 IRC or Matrix channel (bridged) and say hello! Don't be afraid to start talking. [![Chat - Matrix](https://img.shields.io/badge/chat-on%20matrix-blue.svg)](https://matrix.to/#/#dandelion:matrix.org) [![Chat - FreeNode IRC](https://img.shields.io/badge/chat-on%20irc-blue.svg)](https://kiwiirc.com/client/irc.freenode.net/?nick=dandelion-anon|?##dandelion) +Translations can be contributed on GitHub. You can use Stringlate ([![Translate - with Stringlate](https://img.shields.io/badge/stringlate-translate-green.svg)](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. [![Chat - Matrix](https://img.shields.io/badge/chat-on%20matrix-blue.svg)](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. @@ -62,23 +61,21 @@ For more licensing informations, see [`3rd party licenses`](/app/src/main/res/ra ## Screenshots
- - - - - + + + + +
- - - - + + + +
### Notice #### Maintainers -- gsantner ([GitHub](https://github.com/gsantner), [Web](https://gsantner.net/supportme/?project=dandelion&source=readme), [diaspora*](https://pod.geraspora.de/people/d1cbdd70095301341e834860008dbc6c)) - - Bitcoin: [1B9ZyYdQoY9BxMe9dRUEKaZbJWsbQqfXU5](https://gsantner.net/supportme/?project=dandelion&source=readme) +- 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 diff --git a/SCREENSHOTS.md b/SCREENSHOTS.md deleted file mode 100644 index 9403120d..00000000 --- a/SCREENSHOTS.md +++ /dev/null @@ -1,16 +0,0 @@ -## Screenshots - -
- - - - - -
- -
- - - - -
diff --git a/app/build.gradle b/app/build.gradle index 540277f5..09c8527f 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -6,44 +6,27 @@ if (enable_plugin_kotlin) { } android { - compileSdkVersion version_setup_compileSdk - flavorDimensions "default" + buildToolsVersion rootProject.ext.version_buildTools + compileSdkVersion rootProject.ext.version_compileSdk defaultConfig { - minSdkVersion version_setup_minSdk - targetSdkVersion version_setup_targetSdk + 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[]", "DETECTED_ANDROID_LOCALES", "${findUsedAndroidLocales()}" + buildConfigField "String", "BUILD_DATE", "\"${getBuildDate()}\"" buildConfigField "String", "GITHASH", "\"${getGitHash()}\"" - resValue "string", "manifest_package_id", "com.github.dfa.diaspora_android" - - applicationId "com.github.dfa.diaspora_android" - versionName "1.2.2" - versionCode 35 - - - vectorDrawables.useSupportLibrary = true - } - - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } - - buildTypes { - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' - } - } - - sourceSets { - if (enable_plugin_kotlin) { - main.java.srcDirs += 'src/main/kotlin' - } + setProperty("archivesBaseName", applicationId + "-v" + versionCode + "-" + versionName) } + flavorDimensions "default" productFlavors { flavorDefault { } @@ -57,15 +40,58 @@ android { applicationId "net.gsantner.dandelior" } - flavorTest { + 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 { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } + + 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,7 +101,7 @@ 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_library_appcompat}" @@ -83,16 +109,19 @@ dependencies { 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 '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-jre7:$version_plugin_kotlin" + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:${version_plugin_kotlin}" } // Processors diff --git a/app/src/flavorTest/res/drawable-anydpi-v26/ic_launcher.xml b/app/src/flavorAtest/res/drawable-anydpi-v26/ic_launcher.xml similarity index 100% rename from app/src/flavorTest/res/drawable-anydpi-v26/ic_launcher.xml rename to app/src/flavorAtest/res/drawable-anydpi-v26/ic_launcher.xml diff --git a/app/src/flavorTest/res/drawable-anydpi-v26/ic_launcher_round.xml b/app/src/flavorAtest/res/drawable-anydpi-v26/ic_launcher_round.xml similarity index 100% rename from app/src/flavorTest/res/drawable-anydpi-v26/ic_launcher_round.xml rename to app/src/flavorAtest/res/drawable-anydpi-v26/ic_launcher_round.xml diff --git a/app/src/flavorTest/res/drawable-hdpi/ic_launcher.png b/app/src/flavorAtest/res/drawable-hdpi/ic_launcher.png similarity index 100% rename from app/src/flavorTest/res/drawable-hdpi/ic_launcher.png rename to app/src/flavorAtest/res/drawable-hdpi/ic_launcher.png diff --git a/app/src/flavorTest/res/drawable-hdpi/ic_launcher_round.png b/app/src/flavorAtest/res/drawable-hdpi/ic_launcher_round.png similarity index 100% rename from app/src/flavorTest/res/drawable-hdpi/ic_launcher_round.png rename to app/src/flavorAtest/res/drawable-hdpi/ic_launcher_round.png diff --git a/app/src/flavorAtest/res/drawable-ldpi/ic_launcher.png b/app/src/flavorAtest/res/drawable-ldpi/ic_launcher.png new file mode 100644 index 00000000..b85b0c40 Binary files /dev/null and b/app/src/flavorAtest/res/drawable-ldpi/ic_launcher.png differ diff --git a/app/src/flavorTest/res/drawable-mdpi/ic_launcher.png b/app/src/flavorAtest/res/drawable-mdpi/ic_launcher.png similarity index 100% rename from app/src/flavorTest/res/drawable-mdpi/ic_launcher.png rename to app/src/flavorAtest/res/drawable-mdpi/ic_launcher.png diff --git a/app/src/flavorTest/res/drawable-mdpi/ic_launcher_round.png b/app/src/flavorAtest/res/drawable-mdpi/ic_launcher_round.png similarity index 100% rename from app/src/flavorTest/res/drawable-mdpi/ic_launcher_round.png rename to app/src/flavorAtest/res/drawable-mdpi/ic_launcher_round.png diff --git a/app/src/flavorTest/res/drawable-xhdpi/ic_launcher.png b/app/src/flavorAtest/res/drawable-xhdpi/ic_launcher.png similarity index 100% rename from app/src/flavorTest/res/drawable-xhdpi/ic_launcher.png rename to app/src/flavorAtest/res/drawable-xhdpi/ic_launcher.png diff --git a/app/src/flavorTest/res/drawable-xhdpi/ic_launcher_round.png b/app/src/flavorAtest/res/drawable-xhdpi/ic_launcher_round.png similarity index 100% rename from app/src/flavorTest/res/drawable-xhdpi/ic_launcher_round.png rename to app/src/flavorAtest/res/drawable-xhdpi/ic_launcher_round.png diff --git a/app/src/flavorTest/res/drawable-xxhdpi/ic_launcher.png b/app/src/flavorAtest/res/drawable-xxhdpi/ic_launcher.png similarity index 100% rename from app/src/flavorTest/res/drawable-xxhdpi/ic_launcher.png rename to app/src/flavorAtest/res/drawable-xxhdpi/ic_launcher.png diff --git a/app/src/flavorTest/res/drawable-xxhdpi/ic_launcher_round.png b/app/src/flavorAtest/res/drawable-xxhdpi/ic_launcher_round.png similarity index 100% rename from app/src/flavorTest/res/drawable-xxhdpi/ic_launcher_round.png rename to app/src/flavorAtest/res/drawable-xxhdpi/ic_launcher_round.png diff --git a/app/src/flavorTest/res/drawable-xxxhdpi/ic_launcher.png b/app/src/flavorAtest/res/drawable-xxxhdpi/ic_launcher.png similarity index 100% rename from app/src/flavorTest/res/drawable-xxxhdpi/ic_launcher.png rename to app/src/flavorAtest/res/drawable-xxxhdpi/ic_launcher.png diff --git a/app/src/flavorTest/res/drawable-xxxhdpi/ic_launcher_round.png b/app/src/flavorAtest/res/drawable-xxxhdpi/ic_launcher_round.png similarity index 100% rename from app/src/flavorTest/res/drawable-xxxhdpi/ic_launcher_round.png rename to app/src/flavorAtest/res/drawable-xxxhdpi/ic_launcher_round.png diff --git a/app/src/flavorTest/res/drawable/ic_launcher_background.xml b/app/src/flavorAtest/res/drawable/ic_launcher_background.xml similarity index 100% rename from app/src/flavorTest/res/drawable/ic_launcher_background.xml rename to app/src/flavorAtest/res/drawable/ic_launcher_background.xml diff --git a/app/src/flavorTest/res/drawable/ic_launcher_foreground.xml b/app/src/flavorAtest/res/drawable/ic_launcher_foreground.xml similarity index 100% rename from app/src/flavorTest/res/drawable/ic_launcher_foreground.xml rename to app/src/flavorAtest/res/drawable/ic_launcher_foreground.xml diff --git a/app/src/flavorTest/res/ic_launcher-web.png b/app/src/flavorAtest/res/ic_launcher-web.png similarity index 100% rename from app/src/flavorTest/res/ic_launcher-web.png rename to app/src/flavorAtest/res/ic_launcher-web.png diff --git a/app/src/flavorTest/res/values/strings-flavor.xml b/app/src/flavorAtest/res/values/strings-flavor.xml similarity index 100% rename from app/src/flavorTest/res/values/strings-flavor.xml rename to app/src/flavorAtest/res/values/strings-flavor.xml diff --git a/app/src/flavorDandelior/ic_launcher-web.png b/app/src/flavorDandelior/ic_launcher-web.png index 91d4f752..39789cb4 100644 Binary files a/app/src/flavorDandelior/ic_launcher-web.png and b/app/src/flavorDandelior/ic_launcher-web.png differ diff --git a/app/src/flavorDandelior/res/drawable-hdpi/ic_launcher.png b/app/src/flavorDandelior/res/drawable-hdpi/ic_launcher.png index 63a719ac..07dad0c9 100644 Binary files a/app/src/flavorDandelior/res/drawable-hdpi/ic_launcher.png and b/app/src/flavorDandelior/res/drawable-hdpi/ic_launcher.png differ diff --git a/app/src/flavorDandelior/res/drawable-hdpi/ic_launcher_round.png b/app/src/flavorDandelior/res/drawable-hdpi/ic_launcher_round.png index c64eb6a6..07dad0c9 100644 Binary files a/app/src/flavorDandelior/res/drawable-hdpi/ic_launcher_round.png and b/app/src/flavorDandelior/res/drawable-hdpi/ic_launcher_round.png differ diff --git a/app/src/flavorDandelior/res/drawable-ldpi/ic_launcher.png b/app/src/flavorDandelior/res/drawable-ldpi/ic_launcher.png index 5ccf4981..66c10bba 100644 Binary files a/app/src/flavorDandelior/res/drawable-ldpi/ic_launcher.png and b/app/src/flavorDandelior/res/drawable-ldpi/ic_launcher.png differ diff --git a/app/src/flavorDandelior/res/drawable-ldpi/ic_launcher_round.png b/app/src/flavorDandelior/res/drawable-ldpi/ic_launcher_round.png index c0ff8a34..66c10bba 100644 Binary files a/app/src/flavorDandelior/res/drawable-ldpi/ic_launcher_round.png and b/app/src/flavorDandelior/res/drawable-ldpi/ic_launcher_round.png differ diff --git a/app/src/flavorDandelior/res/drawable-mdpi/ic_launcher.png b/app/src/flavorDandelior/res/drawable-mdpi/ic_launcher.png index 162c8910..1288f555 100644 Binary files a/app/src/flavorDandelior/res/drawable-mdpi/ic_launcher.png and b/app/src/flavorDandelior/res/drawable-mdpi/ic_launcher.png differ diff --git a/app/src/flavorDandelior/res/drawable-mdpi/ic_launcher_round.png b/app/src/flavorDandelior/res/drawable-mdpi/ic_launcher_round.png index 062b3c81..70ce8fb1 100644 Binary files a/app/src/flavorDandelior/res/drawable-mdpi/ic_launcher_round.png and b/app/src/flavorDandelior/res/drawable-mdpi/ic_launcher_round.png differ diff --git a/app/src/flavorDandelior/res/drawable-xhdpi/ic_launcher.png b/app/src/flavorDandelior/res/drawable-xhdpi/ic_launcher.png index 776b6491..37e164bc 100644 Binary files a/app/src/flavorDandelior/res/drawable-xhdpi/ic_launcher.png and b/app/src/flavorDandelior/res/drawable-xhdpi/ic_launcher.png differ diff --git a/app/src/flavorDandelior/res/drawable-xhdpi/ic_launcher_round.png b/app/src/flavorDandelior/res/drawable-xhdpi/ic_launcher_round.png index 948fdea3..d9040fa3 100644 Binary files a/app/src/flavorDandelior/res/drawable-xhdpi/ic_launcher_round.png and b/app/src/flavorDandelior/res/drawable-xhdpi/ic_launcher_round.png differ diff --git a/app/src/flavorDandelior/res/drawable-xxhdpi/ic_launcher.png b/app/src/flavorDandelior/res/drawable-xxhdpi/ic_launcher.png index 16a0c5e3..56e46378 100644 Binary files a/app/src/flavorDandelior/res/drawable-xxhdpi/ic_launcher.png and b/app/src/flavorDandelior/res/drawable-xxhdpi/ic_launcher.png differ diff --git a/app/src/flavorDandelior/res/drawable-xxhdpi/ic_launcher_round.png b/app/src/flavorDandelior/res/drawable-xxhdpi/ic_launcher_round.png index f0f4e870..56e46378 100644 Binary files a/app/src/flavorDandelior/res/drawable-xxhdpi/ic_launcher_round.png and b/app/src/flavorDandelior/res/drawable-xxhdpi/ic_launcher_round.png differ diff --git a/app/src/flavorDandelior/res/drawable-xxxhdpi/ic_launcher.png b/app/src/flavorDandelior/res/drawable-xxxhdpi/ic_launcher.png index 2e04f097..9fe1d567 100644 Binary files a/app/src/flavorDandelior/res/drawable-xxxhdpi/ic_launcher.png and b/app/src/flavorDandelior/res/drawable-xxxhdpi/ic_launcher.png differ diff --git a/app/src/flavorDandelior/res/drawable-xxxhdpi/ic_launcher_round.png b/app/src/flavorDandelior/res/drawable-xxxhdpi/ic_launcher_round.png index abb14898..9fe1d567 100644 Binary files a/app/src/flavorDandelior/res/drawable-xxxhdpi/ic_launcher_round.png and b/app/src/flavorDandelior/res/drawable-xxxhdpi/ic_launcher_round.png differ diff --git a/app/src/flavorTest/res/drawable-ldpi/ic_launcher.png b/app/src/flavorTest/res/drawable-ldpi/ic_launcher.png deleted file mode 100644 index a4c4ac0b..00000000 Binary files a/app/src/flavorTest/res/drawable-ldpi/ic_launcher.png and /dev/null differ diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index ba46503f..2e1a7bd1 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -15,6 +15,7 @@ android:allowBackup="false" android:icon="@drawable/ic_launcher" android:label="@string/app_name" + android:requestLegacyExternalStorage="true" android:theme="@style/DiasporaLight"> - @@ -68,6 +70,7 @@ + diff --git a/app/src/main/java/com/github/dfa/diaspora_android/App.java b/app/src/main/java/com/github/dfa/diaspora_android/App.java index 841730e2..150cb378 100644 --- a/app/src/main/java/com/github/dfa/diaspora_android/App.java +++ b/app/src/main/java/com/github/dfa/diaspora_android/App.java @@ -36,6 +36,7 @@ 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; @@ -51,6 +52,7 @@ 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(); @@ -58,7 +60,7 @@ public class App extends Application { String a = new ContextUtils(this).bcstr("FLAVOR", ""); a += "__"; - if (appSettings.isAppFirstStart() && "flavorDandelior".equals(new ContextUtils(this).bcstr("FLAVOR", ""))){ + if (appSettings.isAppFirstStart() && "flavorDandelior".equals(new ContextUtils(this).bcstr("FLAVOR", ""))) { appSettings.setAmoledColorMode(true); } diff --git a/app/src/main/java/com/github/dfa/diaspora_android/activity/DiasporaStreamFragment.java b/app/src/main/java/com/github/dfa/diaspora_android/activity/DiasporaStreamFragment.java index b583fffe..03517332 100644 --- a/app/src/main/java/com/github/dfa/diaspora_android/activity/DiasporaStreamFragment.java +++ b/app/src/main/java/com/github/dfa/diaspora_android/activity/DiasporaStreamFragment.java @@ -128,7 +128,7 @@ public class DiasporaStreamFragment extends BrowserFragment { @Override public boolean onOptionsItemSelected(MenuItem item) { AppLog.d(this, "StreamFragment.onOptionsItemSelected()"); - ShareUtil shu = new ShareUtil(getContext()).setFileProviderAuthority(BuildConfig.APPLICATION_ID); + ShareUtil shu = new ShareUtil(getContext()); PermissionChecker permc = new PermissionChecker(getActivity()); switch (item.getItemId()) { case R.id.action_reload: { @@ -185,7 +185,7 @@ public class DiasporaStreamFragment extends BrowserFragment { if (permc.mkdirIfStoragePermissionGranted(fileSaveDirectory)) { Bitmap bmp = ShareUtil.getBitmapFromWebView(webView); String filename = "dandelion-" + ShareUtil.SDF_SHORT.format(new Date()) + ".jpg"; - _cu.writeImageToFileJpeg(new File(fileSaveDirectory, filename), bmp); + _cu.writeImageToFile(new File(fileSaveDirectory, filename), bmp); Snackbar.make(webView, getString(R.string.saving_screenshot_as) + " " + filename, Snackbar.LENGTH_LONG).show(); } @@ -195,7 +195,7 @@ public class DiasporaStreamFragment extends BrowserFragment { case R.id.action_share_screenshot: { if (permc.doIfExtStoragePermissionGranted(getString(R.string.screenshot_permission__appspecific))) { - shu.shareImage(ShareUtil.getBitmapFromWebView(webView), Bitmap.CompressFormat.JPEG); + shu.shareImage(ShareUtil.getBitmapFromWebView(webView)); } return true; } diff --git a/app/src/main/java/com/github/dfa/diaspora_android/activity/MainActivity.java b/app/src/main/java/com/github/dfa/diaspora_android/activity/MainActivity.java index 9eb6045a..ba8af918 100644 --- a/app/src/main/java/com/github/dfa/diaspora_android/activity/MainActivity.java +++ b/app/src/main/java/com/github/dfa/diaspora_android/activity/MainActivity.java @@ -565,12 +565,10 @@ public class MainActivity extends ThemedActivity } else if ("sc_activities".equals(action)) { openDiasporaUrl(urls.getActivityUrl()); return; - } - else if ("sc_contacts".equals(action)) { + } else if ("sc_contacts".equals(action)) { onNavigationItemSelected(navView.getMenu().findItem(R.id.nav_aspects)); return; - } - else if ("sc_tags".equals(action)) { + } else if ("sc_tags".equals(action)) { onNavigationItemSelected(navView.getMenu().findItem(R.id.nav_followed_tags)); return; } diff --git a/app/src/main/java/com/github/dfa/diaspora_android/activity/SettingsActivity.java b/app/src/main/java/com/github/dfa/diaspora_android/activity/SettingsActivity.java index acda2b0d..0a38744e 100644 --- a/app/src/main/java/com/github/dfa/diaspora_android/activity/SettingsActivity.java +++ b/app/src/main/java/com/github/dfa/diaspora_android/activity/SettingsActivity.java @@ -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); diff --git a/app/src/main/java/com/github/dfa/diaspora_android/data/DiasporaPodList.java b/app/src/main/java/com/github/dfa/diaspora_android/data/DiasporaPodList.java index e0fa9955..3052ea77 100644 --- a/app/src/main/java/com/github/dfa/diaspora_android/data/DiasporaPodList.java +++ b/app/src/main/java/com/github/dfa/diaspora_android/data/DiasporaPodList.java @@ -13,7 +13,7 @@ import java.util.List; /** - * Created by gsantner (https://gsantner.net/ 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 diff --git a/app/src/main/java/com/github/dfa/diaspora_android/data/DiasporaUserProfile.java b/app/src/main/java/com/github/dfa/diaspora_android/data/DiasporaUserProfile.java index e54b351a..22efea59 100644 --- a/app/src/main/java/com/github/dfa/diaspora_android/data/DiasporaUserProfile.java +++ b/app/src/main/java/com/github/dfa/diaspora_android/data/DiasporaUserProfile.java @@ -32,7 +32,7 @@ import org.json.JSONObject; /** * User profile - * Created by gsantner (https://gsantner.net/) 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; diff --git a/app/src/main/java/com/github/dfa/diaspora_android/listener/DiasporaUserProfileChangedListener.java b/app/src/main/java/com/github/dfa/diaspora_android/listener/DiasporaUserProfileChangedListener.java index 644612c4..dc1b67dc 100644 --- a/app/src/main/java/com/github/dfa/diaspora_android/listener/DiasporaUserProfileChangedListener.java +++ b/app/src/main/java/com/github/dfa/diaspora_android/listener/DiasporaUserProfileChangedListener.java @@ -21,7 +21,7 @@ package com.github.dfa.diaspora_android.listener; import com.github.dfa.diaspora_android.data.DiasporaUserProfile; /** - * Created by gsantner (https://gsantner.net/) 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 { diff --git a/app/src/main/java/com/github/dfa/diaspora_android/ui/PodSelectionDialog.java b/app/src/main/java/com/github/dfa/diaspora_android/ui/PodSelectionDialog.java index aa885038..abb31703 100644 --- a/app/src/main/java/com/github/dfa/diaspora_android/ui/PodSelectionDialog.java +++ b/app/src/main/java/com/github/dfa/diaspora_android/ui/PodSelectionDialog.java @@ -37,7 +37,7 @@ import butterknife.OnItemSelected; /** * Dialog that helps the user configure a pod - * Created by gsantner (http://gsantner.net) 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"; diff --git a/app/src/main/java/com/github/dfa/diaspora_android/util/AppSettings.java b/app/src/main/java/com/github/dfa/diaspora_android/util/AppSettings.java index 8d3bb655..1ced82bb 100644 --- a/app/src/main/java/com/github/dfa/diaspora_android/util/AppSettings.java +++ b/app/src/main/java/com/github/dfa/diaspora_android/util/AppSettings.java @@ -37,7 +37,7 @@ import java.util.List; /** * Settings - * Created by gsantner (https://gsantner.net/) 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 SharedPreferencesPropertyBackend { @@ -359,6 +359,14 @@ public class AppSettings extends SharedPreferencesPropertyBackend { 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); } @@ -443,6 +451,7 @@ public class AppSettings extends SharedPreferencesPropertyBackend { public boolean isAmoledColorMode() { 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); } diff --git a/app/src/main/java/com/github/dfa/diaspora_android/util/DiasporaUrlHelper.java b/app/src/main/java/com/github/dfa/diaspora_android/util/DiasporaUrlHelper.java index a283540b..b272dac0 100644 --- a/app/src/main/java/com/github/dfa/diaspora_android/util/DiasporaUrlHelper.java +++ b/app/src/main/java/com/github/dfa/diaspora_android/util/DiasporaUrlHelper.java @@ -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; @@ -353,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; + } } diff --git a/app/src/main/java/com/github/dfa/diaspora_android/web/BrowserFragment.java b/app/src/main/java/com/github/dfa/diaspora_android/web/BrowserFragment.java index 28db279b..bfc53fd9 100644 --- a/app/src/main/java/com/github/dfa/diaspora_android/web/BrowserFragment.java +++ b/app/src/main/java/com/github/dfa/diaspora_android/web/BrowserFragment.java @@ -21,6 +21,7 @@ package com.github.dfa.diaspora_android.web; import android.content.Context; import android.content.MutableContextWrapper; import android.os.Bundle; +import android.support.v4.widget.SwipeRefreshLayout; import android.view.View; import android.view.ViewGroup; import android.webkit.WebSettings; @@ -50,6 +51,7 @@ public class BrowserFragment extends ThemedFragment { protected WebSettings webSettings; protected String pendingUrl; + protected SwipeRefreshLayout swipe;//pull to refresh @Override protected int getLayoutResId() { @@ -90,6 +92,18 @@ 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 @@ -192,6 +206,7 @@ public class BrowserFragment extends ThemedFragment { @Override public void run() { getWebView().reload(); + swipe.setRefreshing(false);//pull to refresh } }); diff --git a/app/src/main/java/com/github/dfa/diaspora_android/web/ContextMenuWebView.java b/app/src/main/java/com/github/dfa/diaspora_android/web/ContextMenuWebView.java index 277b2caa..e1194c9e 100644 --- a/app/src/main/java/com/github/dfa/diaspora_android/web/ContextMenuWebView.java +++ b/app/src/main/java/com/github/dfa/diaspora_android/web/ContextMenuWebView.java @@ -78,7 +78,7 @@ public class ContextMenuWebView extends NestedWebView { public boolean onMenuItemClick(MenuItem item) { HitTestResult result = getHitTestResult(); String url = result.getExtra(); - final ShareUtil shu = new ShareUtil(context).setFileProviderAuthority(BuildConfig.APPLICATION_ID); + final ShareUtil shu = new ShareUtil(context); final PermissionChecker permc = new PermissionChecker(parentActivity); final AppSettings appSettings = new AppSettings(context); diff --git a/app/src/main/java/com/github/dfa/diaspora_android/web/CustomWebViewClient.java b/app/src/main/java/com/github/dfa/diaspora_android/web/CustomWebViewClient.java index c6af6f85..5e0011d8 100644 --- a/app/src/main/java/com/github/dfa/diaspora_android/web/CustomWebViewClient.java +++ b/app/src/main/java/com/github/dfa/diaspora_android/web/CustomWebViewClient.java @@ -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); diff --git a/app/src/main/java/com/github/dfa/diaspora_android/web/WebHelper.java b/app/src/main/java/com/github/dfa/diaspora_android/web/WebHelper.java index 44cf8ad3..49d10340 100644 --- a/app/src/main/java/com/github/dfa/diaspora_android/web/WebHelper.java +++ b/app/src/main/java/com/github/dfa/diaspora_android/web/WebHelper.java @@ -32,7 +32,6 @@ import com.github.dfa.diaspora_android.activity.MainActivity; /** * Created by Gregor Santner on 07.08.16. - * http://gsantner.net */ public class WebHelper { diff --git a/app/src/main/java/net/gsantner/opoc/activity/GsFragmentBase.java b/app/src/main/java/net/gsantner/opoc/activity/GsFragmentBase.java index cab23816..479551d9 100644 --- a/app/src/main/java/net/gsantner/opoc/activity/GsFragmentBase.java +++ b/app/src/main/java/net/gsantner/opoc/activity/GsFragmentBase.java @@ -1,9 +1,8 @@ /*####################################################### * - * Maintained by Gregor Santner, 2017- - * https://gsantner.net/ + * Maintained 2017-2023 by Gregor Santner * - * License: Apache 2.0 / Commercial + * License: Apache 2.0 * https://github.com/gsantner/opoc/#licensing * https://www.apache.org/licenses/LICENSE-2.0 * @@ -16,10 +15,15 @@ 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; @@ -33,6 +37,7 @@ public abstract class GsFragmentBase extends Fragment { protected ContextUtils _cu; protected Bundle _savedInstanceState = null; + protected Menu _fragmentMenu = new MenuItemDummy.Menu(); @Override public void onCreate(Bundle savedInstanceState) { @@ -51,6 +56,9 @@ public abstract class GsFragmentBase extends Fragment { _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; @@ -126,4 +134,27 @@ public abstract class GsFragmentBase extends Fragment { } } } + + @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; + } + } } diff --git a/app/src/main/java/net/gsantner/opoc/android/dummy/MenuItemDummy.java b/app/src/main/java/net/gsantner/opoc/android/dummy/MenuItemDummy.java new file mode 100644 index 00000000..5d3b5f97 --- /dev/null +++ b/app/src/main/java/net/gsantner/opoc/android/dummy/MenuItemDummy.java @@ -0,0 +1,351 @@ +/* + * Maintained 2017-2023 by Gregor Santner + * 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) { + } + } +} diff --git a/app/src/main/java/net/gsantner/opoc/android/dummy/TextWatcherDummy.java b/app/src/main/java/net/gsantner/opoc/android/dummy/TextWatcherDummy.java new file mode 100644 index 00000000..e6a5ea94 --- /dev/null +++ b/app/src/main/java/net/gsantner/opoc/android/dummy/TextWatcherDummy.java @@ -0,0 +1,58 @@ +/* + * Maintained 2017-2023 by Gregor Santner + * 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 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 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 impl) { + return new TextWatcherDummy() { + public void afterTextChanged(final Editable s) { + impl.callback(s); + } + }; + } +} diff --git a/app/src/main/java/net/gsantner/opoc/format/markdown/SimpleMarkdownParser.java b/app/src/main/java/net/gsantner/opoc/format/markdown/SimpleMarkdownParser.java index 1e980f8b..ea117a8a 100644 --- a/app/src/main/java/net/gsantner/opoc/format/markdown/SimpleMarkdownParser.java +++ b/app/src/main/java/net/gsantner/opoc/format/markdown/SimpleMarkdownParser.java @@ -1,9 +1,8 @@ /*####################################################### * - * Maintained by Gregor Santner, 2018- - * https://gsantner.net/ + * Maintained 2018-2023 by Gregor Santner * - * License: Apache 2.0 / Commercial + * License: Apache 2.0 * https://github.com/gsantner/opoc/#licensing * https://www.apache.org/licenses/LICENSE-2.0 * @@ -93,8 +92,8 @@ public class SimpleMarkdownParser { .replaceAll("!\\[(.*?)\\]\\((.*?)\\)", "$1") // img .replaceAll("<(http|https):\\/\\/(.*)>", "$1://$2") // a href (DEP: img) .replaceAll("\\[(.*?)\\]\\((.*?)\\)", "$1") // a href (DEP: img) - .replaceAll("(?m)^([-*] )(.*)$", " $2 ") // unordered list + end line - .replaceAll("(?m)^ (-|\\*) ([^<]*)$", "   $2 ") // unordered list2 + end line + .replaceAll("(?m)^[-*] (.*)$", " $1 ") // unordered list + end line + .replaceAll("(?m)^ [-*] (.*)$", "   $1 ") // unordered list2 + end line .replaceAll("`([^<]*)`", "$1") // code .replace("\\*", "●") // temporary replace escaped star symbol .replaceAll("(?m)\\*\\*(.*)\\*\\*", "$1") // bold (DEP: temp star) @@ -111,6 +110,7 @@ public class SimpleMarkdownParser { public String filter(String text) { text = text .replace("New:", "New:") + .replace("New features:", "New:") .replace("Added:", "Added:") .replace("Add:", "Add:") .replace("Fixed:", "Fixed:") @@ -124,6 +124,20 @@ public class SimpleMarkdownParser { return text; } }; + public final static SmpFilter FILTER_H_TO_SUP = new SmpFilter() { + @Override + public String filter(String text) { + text = text + .replace("

", "") + .replace("

", "") + .replace("

", "") + .replace("

", "") + .replace("

", "") + .replace("

", "") + ; + return text; + } + }; public final static SmpFilter FILTER_NONE = new SmpFilter() { @Override public String filter(String text) { diff --git a/app/src/main/java/net/gsantner/opoc/preference/PropertyBackend.java b/app/src/main/java/net/gsantner/opoc/preference/PropertyBackend.java index 0847021b..f96af499 100644 --- a/app/src/main/java/net/gsantner/opoc/preference/PropertyBackend.java +++ b/app/src/main/java/net/gsantner/opoc/preference/PropertyBackend.java @@ -1,9 +1,8 @@ /*####################################################### * - * Maintained by Gregor Santner, 2018- - * https://gsantner.net/ + * Maintained 2018-2023 by Gregor Santner * - * License: Apache 2.0 / Commercial + * License: Apache 2.0 * https://github.com/gsantner/opoc/#licensing * https://www.apache.org/licenses/LICENSE-2.0 * diff --git a/app/src/main/java/net/gsantner/opoc/preference/SharedPreferencesPropertyBackend.java b/app/src/main/java/net/gsantner/opoc/preference/SharedPreferencesPropertyBackend.java index 8fb486a3..9f5c948c 100644 --- a/app/src/main/java/net/gsantner/opoc/preference/SharedPreferencesPropertyBackend.java +++ b/app/src/main/java/net/gsantner/opoc/preference/SharedPreferencesPropertyBackend.java @@ -1,9 +1,8 @@ /*####################################################### * - * Maintained by Gregor Santner, 2016- - * https://gsantner.net/ + * Maintained 2016-2023 by Gregor Santner * - * License: Apache 2.0 / Commercial + * License: Apache 2.0 * https://github.com/gsantner/opoc/#licensing * https://www.apache.org/licenses/LICENSE-2.0 * @@ -42,8 +41,12 @@ 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; @@ -56,6 +59,8 @@ public class SharedPreferencesPropertyBackend implements PropertyBackend values, final SharedPreferences pref) { @@ -206,9 +223,7 @@ public class SharedPreferencesPropertyBackend implements PropertyBackend getStringListOne(String key, final SharedPreferences pref) { ArrayList ret = new ArrayList<>(); - String value = pref - .getString(key, ARRAY_SEPARATOR) - .replace(ARRAY_SEPARATOR_SUBSTITUTE, ARRAY_SEPARATOR); + String value = getString(key, ARRAY_SEPARATOR).replace(ARRAY_SEPARATOR_SUBSTITUTE, ARRAY_SEPARATOR); if (value.equals(ARRAY_SEPARATOR) || TextUtils.isEmpty(value)) { return ret; } @@ -243,6 +258,7 @@ public class SharedPreferencesPropertyBackend implements PropertyBackend getStringList(@StringRes int keyResourceId, final SharedPreferences... pref) { return getStringListOne(rstr(keyResourceId), gp(pref)); } @@ -263,11 +279,15 @@ public class SharedPreferencesPropertyBackend implements PropertyBackend getIntListOne(String key, final SharedPreferences pref) { ArrayList ret = new ArrayList<>(); - String value = pref.getString(key, ARRAY_SEPARATOR); + String value = getString(key, ARRAY_SEPARATOR); if (value.equals(ARRAY_SEPARATOR)) { return ret; } @@ -347,11 +367,15 @@ public class SharedPreferencesPropertyBackend implements PropertyBackend= 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(); + } } diff --git a/app/src/main/java/net/gsantner/opoc/preference/nonsupport/LanguagePreference.java b/app/src/main/java/net/gsantner/opoc/preference/nonsupport/LanguagePreference.java index 76284a11..04c3be4f 100644 --- a/app/src/main/java/net/gsantner/opoc/preference/nonsupport/LanguagePreference.java +++ b/app/src/main/java/net/gsantner/opoc/preference/nonsupport/LanguagePreference.java @@ -1,7 +1,6 @@ /*####################################################### * - * Maintained by Gregor Santner, 2017- - * https://gsantner.net/ + * Maintained 2017-2023 by Gregor Santner * * License: Apache 2.0 * https://github.com/gsantner/opoc/#licensing diff --git a/app/src/main/java/net/gsantner/opoc/ui/SearchOrCustomTextDialog.java b/app/src/main/java/net/gsantner/opoc/ui/SearchOrCustomTextDialog.java index f83d4e6e..cc703f98 100644 --- a/app/src/main/java/net/gsantner/opoc/ui/SearchOrCustomTextDialog.java +++ b/app/src/main/java/net/gsantner/opoc/ui/SearchOrCustomTextDialog.java @@ -1,7 +1,6 @@ /*####################################################### * - * Maintained by Gregor Santner, 2017- - * https://gsantner.net/ + * Maintained 2017-2023 by Gregor Santner * * License: Apache 2.0 * https://github.com/gsantner/opoc/#licensing @@ -10,42 +9,87 @@ #########################################################*/ 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.text.Editable; +import android.support.v7.widget.TooltipCompat; +import android.text.InputType; +import android.text.Spannable; +import android.text.SpannableString; import android.text.TextUtils; -import android.text.TextWatcher; +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; -@SuppressWarnings("WeakerAccess") +@SuppressLint("SetTextI18n") public class SearchOrCustomTextDialog { public static class DialogOptions { - public Callback.a1 callback; - public List data = new ArrayList<>(); - public List highlightData = new ArrayList<>(); + + // Callback for search text or text of single item + @Nullable + public Callback.a1 callback = null; + + // Callback for indices of selected items. + // List will contain single item if isMultiSelectEnabled == false; + @Nullable + public Callback.a1> positionCallback = null; + + public boolean isMultiSelectEnabled = false; + public List data = null; + public List highlightData = null; + public List 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 highlighter = null; + public String extraFilter = null; + public List preSelected = null; + + public Callback.a0 neutralButtonCallback = null; @ColorInt public int textColor = 0xFF000000; @@ -54,124 +98,219 @@ public class SearchOrCustomTextDialog { @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 = android.R.string.untitled; + 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 { + @LayoutRes + private final int _layout; + private final int _layoutHeight; + private final LayoutInflater _inflater; + private final DialogOptions _dopt; + private final List _filteredItems; + private final Set _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 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) results.values); + notifyDataSetChanged(); + } + + @Override + protected FilterResults performFiltering(final CharSequence constraint) { + final List 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 List allItems = new ArrayList<>(dopt.data); - final List filteredItems = new ArrayList<>(allItems); 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 ArrayAdapter listAdapter = new ArrayAdapter(activity, android.R.layout.simple_list_item_1, filteredItems) { - @NonNull - @Override - public View getView(int pos, @Nullable View convertView, @NonNull ViewGroup parent) { - TextView textView = (TextView) super.getView(pos, convertView, parent); - String text = textView.getText().toString(); - - textView.setTextColor(dopt.textColor); - if (dopt.highlightData.contains(text)) { - textView.setTextColor(dopt.highlightColor); - } - return textView; - } - - @Override - public Filter getFilter() { - return new Filter() { - @SuppressWarnings("unchecked") - @Override - protected void publishResults(final CharSequence constraint, final FilterResults results) { - filteredItems.clear(); - filteredItems.addAll((List) results.values); - notifyDataSetChanged(); - } - - @Override - protected FilterResults performFiltering(final CharSequence constraint) { - final FilterResults res = new FilterResults(); - final ArrayList resList = new ArrayList<>(); - final String fil = constraint.toString(); - - for (final CharSequence str : allItems) { - if ("".equals(fil) || str.toString().toLowerCase(Locale.getDefault()).contains(fil.toLowerCase(Locale.getDefault()))) { - resList.add(str); - } - } - res.values = resList; - res.count = resList.size(); - return res; - } - }; - } - }; + 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))); - searchEditText.addTextChangedListener(new TextWatcher() { - @Override - public void afterTextChanged(final Editable arg0) { - listAdapter.getFilter().filter(searchEditText.getText()); - } + final ContextUtils cu = new ContextUtils(activity); + final int margin = (int) cu.convertDpToPx(8); + cu.freeContextRef(); - @Override - public void onTextChanged(final CharSequence arg0, final int arg1, final int arg2, final int arg3) { - } + final LinearLayout searchLayout = new LinearLayout(activity); + searchLayout.setOrientation(LinearLayout.HORIZONTAL); - @Override - public void beforeTextChanged(final CharSequence arg0, final int arg1, final int arg2, final int arg3) { - } - }); + 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) { - LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT); - ContextUtils cu = new net.gsantner.opoc.util.ContextUtils(listView.getContext()); - int px = (int) (new net.gsantner.opoc.util.ContextUtils(listView.getContext()).convertDpToPx(8)); - lp.setMargins(px, px / 2, px, px / 2); - linearLayout.addView(searchEditText, lp); + 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) - .setTitle(dopt.titleText) .setOnCancelListener(null) - .setNegativeButton(dopt.cancelButtonText, null); - if (dopt.isSearchEnabled) { + .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) -> { - dialogInterface.dismiss(); - if (dopt.callback != null && !TextUtils.isEmpty(searchEditText.getText().toString())) { - dopt.callback.callback(searchEditText.getText().toString()); + final String searchText = dopt.isSearchEnabled ? searchEditText.getText().toString() : null; + if (dopt.positionCallback != null && !listAdapter._selectedItems.isEmpty()) { + final List 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(); - listView.setOnItemClickListener((parent, view, position, id) -> { - dialog.dismiss(); - if (dopt.callback != null) { - dopt.callback.callback(filteredItems.get(position).toString()); - } - }); searchEditText.setOnKeyListener((keyView, keyCode, keyEvent) -> { if ((keyEvent.getAction() == KeyEvent.ACTION_DOWN) && (keyCode == KeyEvent.KEYCODE_ENTER)) { @@ -184,6 +323,72 @@ public class SearchOrCustomTextDialog { 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 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)); } } diff --git a/app/src/main/java/net/gsantner/opoc/util/ActivityUtils.java b/app/src/main/java/net/gsantner/opoc/util/ActivityUtils.java index 2114f202..741b3b76 100644 --- a/app/src/main/java/net/gsantner/opoc/util/ActivityUtils.java +++ b/app/src/main/java/net/gsantner/opoc/util/ActivityUtils.java @@ -1,21 +1,27 @@ /*####################################################### * - * Maintained by Gregor Santner, 2016- - * https://gsantner.net/ + * Maintained 2016-2023 by Gregor Santner * - * License: Apache 2.0 / Commercial - * https://github.com/gsantner/opoc/#licensing - * https://www.apache.org/licenses/LICENSE-2.0 + * 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; @@ -26,12 +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 @@ -43,6 +53,12 @@ public class ActivityUtils extends net.gsantner.opoc.util.ContextUtils { _activity = activity; } + @Override + public void freeContextRef() { + super.freeContextRef(); + _activity = null; + } + //######################## //## Methods //######################## @@ -81,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) { @@ -93,18 +111,58 @@ public class ActivityUtils extends net.gsantner.opoc.util.ContextUtils { .show(); } - public void hideSoftKeyboard() { - 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); + 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 void showSoftKeyboard() { - InputMethodManager imm = (InputMethodManager) _activity.getSystemService(Activity.INPUT_METHOD_SERVICE); - if (imm != null && _activity.getCurrentFocus() != null && _activity.getCurrentFocus().getWindowToken() != null) { - imm.showSoftInput(_activity.getCurrentFocus(), InputMethodManager.SHOW_FORCED); + 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) { @@ -112,19 +170,23 @@ 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) - .setTitle(resTitleId) - .setView(textView); - dialog.show(); + .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) { @@ -134,11 +196,11 @@ public class ActivityUtils extends net.gsantner.opoc.util.ContextUtils { .setPositiveButton(android.R.string.ok, null) .setTitle(resTitleId) .setView(wv); - dialog.show(); + 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) { @@ -149,9 +211,10 @@ public class ActivityUtils extends net.gsantner.opoc.util.ContextUtils { attrs.flags |= flag; } _activity.getWindow().setAttributes(attrs); + return this; } - public void showGooglePlayEntryForThisApp() { + 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 | @@ -161,11 +224,12 @@ public class ActivityUtils extends net.gsantner.opoc.util.ContextUtils { _activity.startActivity(goToMarket); } catch (ActivityNotFoundException e) { _activity.startActivity(new Intent(Intent.ACTION_VIEW, - Uri.parse("http://play.google.com/store/apps/" + pkgId))); + Uri.parse("https://play.google.com/store/apps/" + pkgId))); } + return this; } - public void setStatusbarColor(int color, boolean... fromRes) { + 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); @@ -173,5 +237,110 @@ public class ActivityUtils extends net.gsantner.opoc.util.ContextUtils { _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 tasks = am.getAppTasks(); + if (tasks != null && !tasks.isEmpty()) { + tasks.get(0).setExcludeFromRecents(true); + } + } + + } catch (Exception ignored) { + } + return this; } } diff --git a/app/src/main/java/net/gsantner/opoc/util/AdBlock.java b/app/src/main/java/net/gsantner/opoc/util/AdBlock.java index 304a4b85..c5c81678 100644 --- a/app/src/main/java/net/gsantner/opoc/util/AdBlock.java +++ b/app/src/main/java/net/gsantner/opoc/util/AdBlock.java @@ -1,7 +1,6 @@ /*####################################################### * - * Maintained by Gregor Santner, 2017- - * https://gsantner.net/ + * Maintained 2017-2023 by Gregor Santner * * License: Apache 2.0 * https://github.com/gsantner/opoc/#licensing @@ -46,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() { @@ -61,7 +61,9 @@ public class AdBlock { //######################## private final Set _adblockHostsFromRaw = new HashSet<>(); private final Set _adblockHosts = new HashSet<>(); - private boolean _isLoaded; + private final List> _customBlockCallbacks = new ArrayList<>(); + private boolean _isLoaded = false; + private boolean _isAdblockLogging = false; //######################## //## @@ -72,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 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; } @@ -102,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(); @@ -110,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(); } @@ -172,4 +202,17 @@ public class AdBlock { } return adblockResIds; } + + // URI uri, String url, String host + public AdBlock addCustomBlockCallback(Callback.b3 cb) { + synchronized (synchronizeObj) { + _customBlockCallbacks.add(cb); + } + return this; + } + + public AdBlock setLogEnabled(boolean isAdblockLogging) { + _isAdblockLogging = isAdblockLogging; + return this; + } } diff --git a/app/src/main/java/net/gsantner/opoc/util/Callback.java b/app/src/main/java/net/gsantner/opoc/util/Callback.java index 697e7d77..012cb634 100644 --- a/app/src/main/java/net/gsantner/opoc/util/Callback.java +++ b/app/src/main/java/net/gsantner/opoc/util/Callback.java @@ -1,17 +1,21 @@ /*####################################################### * - * Maintained by Gregor Santner, 2018- - * https://gsantner.net/ + * Maintained 2018-2023 by Gregor Santner * - * License: Apache 2.0 / Commercial - * https://github.com/gsantner/opoc/#licensing - * https://www.apache.org/licenses/LICENSE-2.0 + * 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 { void callback(A arg1); } @@ -31,4 +35,52 @@ public class Callback { public interface a5 { void callback(A arg1, B arg2, C arg3, D arg4, E arg5); } + + public interface b0 { + boolean callback(); + } + + public interface b1 { + boolean callback(A arg1); + } + + public interface b2 { + boolean callback(A arg1, B arg2); + } + + public interface b3 { + boolean callback(A arg1, B arg2, C arg3); + } + + public interface b4 { + boolean callback(A arg1, B arg2, C arg3, D arg4); + } + + public interface b5 { + boolean callback(A arg1, B arg2, C arg3, D arg4, E arg5); + } + + public interface s0 { + String callback(); + } + + public interface s1 { + String callback(A arg1); + } + + public interface s2 { + String callback(A arg1, B arg2); + } + + public interface s3 { + String callback(A arg1, B arg2, C arg3); + } + + public interface s4 { + String callback(A arg1, B arg2, C arg3, D arg4); + } + + public interface s5 { + String callback(A arg1, B arg2, C arg3, D arg4, E arg5); + } } diff --git a/app/src/main/java/net/gsantner/opoc/util/ContextUtils.java b/app/src/main/java/net/gsantner/opoc/util/ContextUtils.java index 1ade61f9..483461d8 100644 --- a/app/src/main/java/net/gsantner/opoc/util/ContextUtils.java +++ b/app/src/main/java/net/gsantner/opoc/util/ContextUtils.java @@ -1,20 +1,21 @@ /*####################################################### * - * Maintained by Gregor Santner, 2016- - * https://gsantner.net/ + * Maintained 2016-2023 by Gregor Santner * - * License: Apache 2.0 / Commercial - * https://github.com/gsantner/opoc/#licensing - * https://www.apache.org/licenses/LICENSE-2.0 + * 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.annotation.SuppressLint; import android.app.Activity; +import android.app.ActivityManager; import android.app.AlarmManager; import android.app.PendingIntent; import android.content.ActivityNotFoundException; +import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.pm.PackageInfo; @@ -37,6 +38,10 @@ import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.net.Uri; import android.os.Build; +import android.os.Environment; +import android.os.SystemClock; +import android.os.VibrationEffect; +import android.os.Vibrator; import android.support.annotation.ColorInt; import android.support.annotation.ColorRes; import android.support.annotation.DrawableRes; @@ -44,9 +49,14 @@ import android.support.annotation.Nullable; import android.support.annotation.RawRes; import android.support.annotation.StringRes; import android.support.graphics.drawable.VectorDrawableCompat; +import android.support.v4.app.ActivityManagerCompat; import android.support.v4.content.ContextCompat; import android.support.v4.graphics.drawable.DrawableCompat; +import android.support.v4.text.TextUtilsCompat; +import android.support.v4.util.Pair; +import android.support.v4.view.ViewCompat; import android.text.Html; +import android.text.InputFilter; import android.text.SpannableString; import android.text.Spanned; import android.text.TextUtils; @@ -55,6 +65,11 @@ import android.util.DisplayMetrics; import android.util.Log; import android.view.Menu; import android.view.MenuItem; +import android.view.MotionEvent; +import android.view.Surface; +import android.view.View; +import android.view.WindowManager; +import android.webkit.MimeTypeMap; import android.widget.ImageView; import android.widget.TextView; @@ -62,17 +77,21 @@ import net.gsantner.opoc.format.markdown.SimpleMarkdownParser; import java.io.BufferedReader; import java.io.File; -import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStreamReader; +import java.lang.reflect.Field; import java.lang.reflect.Method; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.List; import java.util.Locale; +import static android.content.Context.VIBRATOR_SERVICE; import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; import static android.graphics.Bitmap.CompressFormat; -@SuppressWarnings({"WeakerAccess", "unused", "SameParameterValue", "ObsoleteSdkInt", "deprecation", "SpellCheckingInspection"}) +@SuppressWarnings({"WeakerAccess", "unused", "SameParameterValue", "ObsoleteSdkInt", "deprecation", "SpellCheckingInspection", "TryFinallyCanBeTryWithResources", "UnusedAssignment", "UnusedReturnValue"}) public class ContextUtils { // // Members, Constructors @@ -87,6 +106,9 @@ public class ContextUtils { return _context; } + public void freeContextRef() { + _context = null; + } // // Class Methods @@ -101,39 +123,55 @@ public class ContextUtils { * * @return A valid id if the id could be found, else 0 */ - public int getResId(ResType resType, final String name) { - return _context.getResources().getIdentifier(name, resType.name().toLowerCase(), _context.getPackageName()); + public int getResId(final ResType resType, final String name) { + try { + return _context.getResources().getIdentifier(name, resType.name().toLowerCase(), _context.getPackageName()); + } catch (Exception e) { + return 0; + } } /** * Get String by given string ressource id (nuermic) */ - public String rstr(@StringRes int strResId) { - return _context.getString(strResId); + public String rstr(@StringRes final int strResId) { + try { + return _context.getString(strResId); + } catch (Exception e) { + return null; + } } /** * Get String by given string ressource identifier (textual) */ - public String rstr(String strResKey) { + public String rstr(final String strResKey, Object... a0getResKeyAsFallback) { try { return rstr(getResId(ResType.STRING, strResKey)); } catch (Resources.NotFoundException e) { - return null; + return a0getResKeyAsFallback != null && a0getResKeyAsFallback.length > 0 ? strResKey : null; } } /** * Get drawable from given ressource identifier */ - public Drawable rdrawable(@DrawableRes int resId) { - return ContextCompat.getDrawable(_context, resId); + public Drawable rdrawable(@DrawableRes final int resId) { + try { + return ContextCompat.getDrawable(_context, resId); + } catch (Exception e) { + return null; + } } /** * Get color by given color ressource id */ - public int rcolor(@ColorRes int resId) { + public int rcolor(@ColorRes final int resId) { + if (resId == 0) { + Log.e(getClass().getName(), "ContextUtils::rcolor: resId is 0!"); + return Color.BLACK; + } return ContextCompat.getColor(_context, resId); } @@ -159,29 +197,37 @@ public class ContextUtils { * @param intColor The color coded in int * @param withAlpha Optional; Set first bool parameter to true to also include alpha value */ - public String colorToHexString(int intColor, boolean... withAlpha) { + public static String colorToHexString(final int intColor, final boolean... withAlpha) { boolean a = withAlpha != null && withAlpha.length >= 1 && withAlpha[0]; return String.format(a ? "#%08X" : "#%06X", (a ? 0xFFFFFFFF : 0xFFFFFF) & intColor); } + public static String getAndroidVersion() { + return Build.VERSION.RELEASE + " (" + Build.VERSION.SDK_INT + ")"; + } + public String getAppVersionName() { + PackageManager manager = _context.getPackageManager(); try { - PackageManager manager = _context.getPackageManager(); - PackageInfo info = manager.getPackageInfo(getPackageName(), 0); + PackageInfo info = manager.getPackageInfo(getPackageIdManifest(), 0); return info.versionName; } catch (PackageManager.NameNotFoundException e) { - e.printStackTrace(); - return "?"; + try { + PackageInfo info = manager.getPackageInfo(getPackageIdReal(), 0); + return info.versionName; + } catch (PackageManager.NameNotFoundException ignored) { + } } + return "?"; } public String getAppInstallationSource() { String src = null; try { - src = _context.getPackageManager().getInstallerPackageName(getPackageName()); + src = _context.getPackageManager().getInstallerPackageName(getPackageIdManifest()); } catch (Exception ignored) { } - if (TextUtils.isEmpty(src)) { + if (src == null || src.trim().isEmpty()) { return "Sideloaded"; } else if (src.toLowerCase().contains(".amazon.")) { return "Amazon Appstore"; @@ -189,7 +235,7 @@ public class ContextUtils { switch (src) { case "com.android.vending": case "com.google.android.feedback": { - return "Google Play Store"; + return "Google Play"; } case "org.fdroid.fdroid.privileged": case "org.fdroid.fdroid": { @@ -212,23 +258,31 @@ public class ContextUtils { * Send a {@link Intent#ACTION_VIEW} Intent with given paramter * If the parameter is an string a browser will get triggered */ - public void openWebpageInExternalBrowser(final String url) { - Uri uri = Uri.parse(url); - Intent intent = new Intent(Intent.ACTION_VIEW, uri); - intent.addFlags(FLAG_ACTIVITY_NEW_TASK); + public ContextUtils openWebpageInExternalBrowser(final String url) { try { + Uri uri = Uri.parse(url); + Intent intent = new Intent(Intent.ACTION_VIEW, uri); + intent.addFlags(FLAG_ACTIVITY_NEW_TASK); _context.startActivity(intent); - } catch (ActivityNotFoundException e) { + } catch (Exception e) { e.printStackTrace(); } + return this; } /** - * Get this apps package name. The builtin method may fail when used with flavors + * Get the apps base packagename, which is equal with all build flavors and variants */ - public String getPackageName() { + public String getPackageIdManifest() { String pkg = rstr("manifest_package_id"); - return pkg != null ? pkg : _context.getPackageName(); + return !TextUtils.isEmpty(pkg) ? pkg : _context.getPackageName(); + } + + /** + * Get this apps package name, returns the flavor specific package name. + */ + public String getPackageIdReal() { + return _context.getPackageName(); } /** @@ -239,23 +293,36 @@ public class ContextUtils { * of the package set in manifest (root element). * Falls back to applicationId of the app which may differ from manifest. */ - public Object getBuildConfigValue(String fieldName) { - String pkg = getPackageName() + ".BuildConfig"; + public Object getBuildConfigValue(final String fieldName) { + final String pkg = getPackageIdManifest() + ".BuildConfig"; try { Class c = Class.forName(pkg); return c.getField(fieldName).get(null); } catch (Exception e) { e.printStackTrace(); - return null; } + return null; + } + + public List getBuildConfigFields() { + final String pkg = getPackageIdManifest() + ".BuildConfig"; + final List fields = new ArrayList<>(); + try { + for (Field f : Class.forName(pkg).getFields()) { + fields.add(f.getName()); + } + } catch (Exception e) { + e.printStackTrace(); + } + return fields; } /** * Get a BuildConfig bool value */ - public Boolean bcbool(String fieldName, Boolean defaultValue) { + public Boolean bcbool(final String fieldName, final Boolean defaultValue) { Object field = getBuildConfigValue(fieldName); - if (field != null && field instanceof Boolean) { + if (field instanceof Boolean) { return (Boolean) field; } return defaultValue; @@ -264,9 +331,9 @@ public class ContextUtils { /** * Get a BuildConfig string value */ - public String bcstr(String fieldName, String defaultValue) { + public String bcstr(final String fieldName, final String defaultValue) { Object field = getBuildConfigValue(fieldName); - if (field != null && field instanceof String) { + if (field instanceof String) { return (String) field; } return defaultValue; @@ -275,9 +342,9 @@ public class ContextUtils { /** * Get a BuildConfig string value */ - public Integer bcint(String fieldName, int defaultValue) { + public Integer bcint(final String fieldName, final int defaultValue) { Object field = getBuildConfigValue(fieldName); - if (field != null && field instanceof Integer) { + if (field instanceof Integer) { return (Integer) field; } return defaultValue; @@ -297,26 +364,6 @@ public class ContextUtils { return bcbool("IS_FOSS_BUILD", false); } - /** - * Request a bitcoin donation with given details. - * All parameters are awaited as string resource ids - */ - public void showDonateBitcoinRequest(@StringRes final int srBitcoinId, @StringRes final int srBitcoinAmount, @StringRes final int srBitcoinMessage, @StringRes final int srAlternativeDonateUrl) { - if (!isGooglePlayBuild()) { - String btcUri = String.format("bitcoin:%s?amount=%s&label=%s&message=%s", - rstr(srBitcoinId), rstr(srBitcoinAmount), - rstr(srBitcoinMessage), rstr(srBitcoinMessage)); - Intent intent = new Intent(Intent.ACTION_VIEW); - intent.setData(Uri.parse(btcUri)); - intent.addFlags(FLAG_ACTIVITY_NEW_TASK); - try { - _context.startActivity(intent); - } catch (ActivityNotFoundException e) { - openWebpageInExternalBrowser(rstr(srAlternativeDonateUrl)); - } - } - } - public String readTextfileFromRawRes(@RawRes int rawResId, String linePrefix, String linePostfix) { StringBuilder sb = new StringBuilder(); BufferedReader br = null; @@ -365,8 +412,8 @@ public class ContextUtils { * Check if app with given {@code packageName} is installed */ public boolean isAppInstalled(String packageName) { - PackageManager pm = _context.getApplicationContext().getPackageManager(); try { + PackageManager pm = _context.getApplicationContext().getPackageManager(); pm.getPackageInfo(packageName, PackageManager.GET_ACTIVITIES); return true; } catch (PackageManager.NameNotFoundException e) { @@ -378,17 +425,17 @@ public class ContextUtils { * Restart the current app. Supply the class to start on startup */ public void restartApp(Class classToStart) { - Intent inte = new Intent(_context, classToStart); - PendingIntent inteP = PendingIntent.getActivity(_context, 555, inte, PendingIntent.FLAG_CANCEL_CURRENT); + Intent intent = new Intent(_context, classToStart); + PendingIntent pendi = PendingIntent.getActivity(_context, 555, intent, PendingIntent.FLAG_CANCEL_CURRENT); AlarmManager mgr = (AlarmManager) _context.getSystemService(Context.ALARM_SERVICE); if (_context instanceof Activity) { ((Activity) _context).finish(); } if (mgr != null) { - mgr.set(AlarmManager.RTC, System.currentTimeMillis() + 100, inteP); + mgr.set(AlarmManager.RTC, System.currentTimeMillis() + 100, pendi); } else { - inte.addFlags(FLAG_ACTIVITY_NEW_TASK); - _context.startActivity(inte); + intent.addFlags(FLAG_ACTIVITY_NEW_TASK); + _context.startActivity(intent); } Runtime.getRuntime().exit(0); } @@ -457,19 +504,25 @@ public class ContextUtils { * {@code androidLC} may be in any of the forms: en, de, de-rAt * If given an empty string, the default (system) locale gets loaded */ - public void setAppLanguage(String androidLC) { + public void setAppLanguage(final String androidLC) { Locale locale = getLocaleByAndroidCode(androidLC); + locale = (locale != null && !androidLC.isEmpty()) ? locale : Resources.getSystem().getConfiguration().locale; + setLocale(locale); + } + + public ContextUtils setLocale(final Locale locale) { Configuration config = _context.getResources().getConfiguration(); - config.locale = (locale != null && !androidLC.isEmpty()) - ? locale : Resources.getSystem().getConfiguration().locale; + config.locale = (locale != null ? locale : Resources.getSystem().getConfiguration().locale); _context.getResources().updateConfiguration(config, null); + Locale.setDefault(locale); + return this; } /** * Try to guess if the color on top of the given {@code colorOnBottomInt} * should be light or dark. Returns true if top color should be light */ - public boolean shouldColorOnTopBeLight(@ColorInt int colorOnBottomInt) { + public boolean shouldColorOnTopBeLight(@ColorInt final int colorOnBottomInt) { return 186 > (((0.299 * Color.red(colorOnBottomInt)) + ((0.587 * Color.green(colorOnBottomInt)) + (0.114 * Color.blue(colorOnBottomInt))))); @@ -478,7 +531,7 @@ public class ContextUtils { /** * Convert a html string to an android {@link Spanned} object */ - public Spanned htmlToSpanned(String html) { + public Spanned htmlToSpanned(final String html) { Spanned result; if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) { result = Html.fromHtml(html, Html.FROM_HTML_MODE_LEGACY); @@ -502,12 +555,87 @@ public class ContextUtils { return dp * _context.getResources().getDisplayMetrics().density; } + /** + * Get the private directory for the current package (usually /data/data/package.name/) + */ + @SuppressWarnings("StatementWithEmptyBody") + public File getAppDataPrivateDir() { + File filesDir; + try { + filesDir = new File(new File(_context.getPackageManager().getPackageInfo(getPackageIdReal(), 0).applicationInfo.dataDir), "files"); + } catch (PackageManager.NameNotFoundException e) { + filesDir = _context.getFilesDir(); + } + if (!filesDir.exists() && filesDir.mkdirs()) ; + return filesDir; + } + + /** + * Get public (accessible) appdata folders + */ + @SuppressWarnings("StatementWithEmptyBody") + public List> getAppDataPublicDirs(boolean internalStorageFolder, boolean sdcardFolders, boolean storageNameWithoutType) { + List> dirs = new ArrayList<>(); + for (File externalFileDir : ContextCompat.getExternalFilesDirs(_context, null)) { + if (externalFileDir == null || Environment.getExternalStorageDirectory() == null) { + continue; + } + boolean isInt = externalFileDir.getAbsolutePath().startsWith(Environment.getExternalStorageDirectory().getAbsolutePath()); + boolean add = (internalStorageFolder && isInt) || (sdcardFolders && !isInt); + if (add) { + dirs.add(new Pair<>(externalFileDir, getStorageName(externalFileDir, storageNameWithoutType))); + if (!externalFileDir.exists() && externalFileDir.mkdirs()) ; + } + } + return dirs; + } + + public String getStorageName(final File externalFileDir, final boolean storageNameWithoutType) { + boolean isInt = externalFileDir.getAbsolutePath().startsWith(Environment.getExternalStorageDirectory().getAbsolutePath()); + + String[] split = externalFileDir.getAbsolutePath().split("/"); + if (split.length > 2) { + return isInt ? (storageNameWithoutType ? "Internal Storage" : "") : (storageNameWithoutType ? split[2] : ("SD Card (" + split[2] + ")")); + } else { + return "Storage"; + } + } + + public List> getStorages(final boolean internalStorageFolder, final boolean sdcardFolders) { + List> storages = new ArrayList<>(); + for (Pair pair : getAppDataPublicDirs(internalStorageFolder, sdcardFolders, true)) { + if (pair.first != null && pair.first.getAbsolutePath().lastIndexOf("/Android/data") > 0) { + try { + storages.add(new Pair<>(new File(pair.first.getCanonicalPath().replaceFirst("/Android/data.*", "")), pair.second)); + } catch (IOException ignored) { + } + } + } + return storages; + } + + public File getStorageRootFolder(final File file) { + String filepath; + try { + filepath = file.getCanonicalPath(); + } catch (Exception ignored) { + return null; + } + for (Pair storage : getStorages(false, true)) { + //noinspection ConstantConditions + if (filepath.startsWith(storage.first.getAbsolutePath())) { + return storage.first; + } + } + return null; + } + /** * Request the givens paths to be scanned by MediaScanner * * @param files Files and folders to scan */ - public void mediaScannerScanFile(File... files) { + public void mediaScannerScanFile(final File... files) { if (android.os.Build.VERSION.SDK_INT > 19) { String[] paths = new String[files.length]; for (int i = 0; i < files.length; i++) { @@ -556,8 +684,12 @@ public class ContextUtils { /** * Get a {@link Bitmap} out of a {@link DrawableRes} */ - public Bitmap drawableToBitmap(@DrawableRes int drawableId) { - return drawableToBitmap(ContextCompat.getDrawable(_context, drawableId)); + public Bitmap drawableToBitmap(@DrawableRes final int drawableId) { + try { + return drawableToBitmap(ContextCompat.getDrawable(_context, drawableId)); + } catch (Exception e) { + return null; + } } /** @@ -565,7 +697,7 @@ public class ContextUtils { * Specifying a {@code maxDimen} is also possible and a value below 2000 * is recommended, otherwise a {@link OutOfMemoryError} may occur */ - public Bitmap loadImageFromFilesystem(File imagePath, int maxDimen) { + public Bitmap loadImageFromFilesystem(final File imagePath, final int maxDimen) { BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeFile(imagePath.getAbsolutePath(), options); @@ -581,7 +713,7 @@ public class ContextUtils { * @param maxDimen Max size of the Bitmap (width or height) * @return the scaling factor that needs to be applied to the bitmap */ - public int calculateInSampleSize(BitmapFactory.Options options, int maxDimen) { + public int calculateInSampleSize(final BitmapFactory.Options options, final int maxDimen) { // Raw height and width of image int height = options.outHeight; int width = options.outWidth; @@ -597,7 +729,7 @@ public class ContextUtils { * Scale the bitmap so both dimensions are lower or equal to {@code maxDimen} * This keeps the aspect ratio */ - public Bitmap scaleBitmap(Bitmap bitmap, int maxDimen) { + public Bitmap scaleBitmap(final Bitmap bitmap, final int maxDimen) { int picSize = Math.min(bitmap.getHeight(), bitmap.getWidth()); float scale = 1.f * maxDimen / picSize; Matrix matrix = new Matrix(); @@ -605,44 +737,27 @@ public class ContextUtils { return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true); } - /** - * Write the given {@link Bitmap} to {@code imageFile}, in {@link CompressFormat#JPEG} format - */ - public boolean writeImageToFileJpeg(File imageFile, Bitmap image) { - return writeImageToFile(imageFile, image, Bitmap.CompressFormat.JPEG, 95); - } - /** * Write the given {@link Bitmap} to filesystem * * @param targetFile The file to be written in - * @param image The image as android {@link Bitmap} - * @param format One format of {@link CompressFormat}, null will determine based on filename - * @param quality Quality level, defaults to 95 + * @param image Android {@link Bitmap} * @return True if writing was successful */ - public boolean writeImageToFile(File targetFile, Bitmap image, CompressFormat format, Integer quality) { + public boolean writeImageToFile(final File targetFile, final Bitmap image, Integer... a0quality) { + final int quality = (a0quality != null && a0quality.length > 0 && a0quality[0] >= 0 && a0quality[0] <= 100) ? a0quality[0] : 70; + final String lc = targetFile.getAbsolutePath().toLowerCase(Locale.ROOT); + final CompressFormat format = lc.endsWith(".webp") ? CompressFormat.WEBP : (lc.endsWith(".png") ? CompressFormat.PNG : CompressFormat.JPEG); + + boolean ok = false; File folder = new File(targetFile.getParent()); - if (quality == null || quality < 0 || quality > 100) { - quality = 95; - } - if (format == null) { - format = CompressFormat.JPEG; - String lc = targetFile.getAbsolutePath().toLowerCase(Locale.ROOT); - if (lc.endsWith(".png")) { - format = CompressFormat.PNG; - } - if (lc.endsWith(".webp")) { - format = CompressFormat.WEBP; - } - } if (folder.exists() || folder.mkdirs()) { FileOutputStream stream = null; try { - stream = new FileOutputStream(targetFile); // overwrites this image every time + stream = new FileOutputStream(targetFile); image.compress(format, quality, stream); - return true; - } catch (FileNotFoundException ignored) { + ok = true; + } catch (Exception ignored) { } finally { try { if (stream != null) { @@ -652,14 +767,18 @@ public class ContextUtils { } } } - return false; + try { + image.recycle(); + } catch (Exception ignored) { + } + return ok; } /** * Draw text in the center of the given {@link DrawableRes} * This may be useful for e.g. badge counts */ - public Bitmap drawTextOnDrawable(@DrawableRes int drawableRes, String text, int textSize) { + public Bitmap drawTextOnDrawable(@DrawableRes final int drawableRes, final String text, final int textSize) { Resources resources = _context.getResources(); float scale = resources.getDisplayMetrics().density; Bitmap bitmap = drawableToBitmap(drawableRes); @@ -684,12 +803,16 @@ public class ContextUtils { * Try to tint all {@link Menu}s {@link MenuItem}s with given color */ @SuppressWarnings("ConstantConditions") - public void tintMenuItems(Menu menu, boolean recurse, @ColorInt int iconColor) { + public void tintMenuItems(final Menu menu, final boolean recurse, @ColorInt final int iconColor) { for (int i = 0; i < menu.size(); i++) { MenuItem item = menu.getItem(i); - tintDrawable(item.getIcon(), iconColor); - if (item.hasSubMenu() && recurse) { - tintMenuItems(item.getSubMenu(), recurse, iconColor); + try { + tintDrawable(item.getIcon(), iconColor); + if (item.hasSubMenu() && recurse) { + tintMenuItems(item.getSubMenu(), recurse, iconColor); + } + } catch (Exception ignored) { + // This should not happen at all, but may in bad menu.xml configuration } } } @@ -697,14 +820,14 @@ public class ContextUtils { /** * Loads {@link Drawable} by given {@link DrawableRes} and applies a color */ - public Drawable tintDrawable(@DrawableRes int drawableRes, @ColorInt int color) { + public Drawable tintDrawable(@DrawableRes final int drawableRes, @ColorInt final int color) { return tintDrawable(rdrawable(drawableRes), color); } /** * Tint a {@link Drawable} with given {@code color} */ - public Drawable tintDrawable(@Nullable Drawable drawable, @ColorInt int color) { + public Drawable tintDrawable(@Nullable Drawable drawable, @ColorInt final int color) { if (drawable != null) { drawable = DrawableCompat.wrap(drawable); DrawableCompat.setTint(drawable.mutate(), color); @@ -716,7 +839,10 @@ public class ContextUtils { * Try to make icons in Toolbar/ActionBars SubMenus visible * This may not work on some devices and it maybe won't work on future android updates */ - public void setSubMenuIconsVisiblity(Menu menu, boolean visible) { + public void setSubMenuIconsVisiblity(final Menu menu, final boolean visible) { + if (TextUtilsCompat.getLayoutDirectionFromLocale(Locale.getDefault()) == ViewCompat.LAYOUT_DIRECTION_RTL) { + return; + } if (menu.getClass().getSimpleName().equals("MenuBuilder")) { try { @SuppressLint("PrivateApi") Method m = menu.getClass().getDeclaredMethod("setOptionalIconsVisible", Boolean.TYPE); @@ -727,4 +853,165 @@ public class ContextUtils { } } } + + + public String getLocalizedDateFormat() { + return ((SimpleDateFormat) android.text.format.DateFormat.getDateFormat(_context)).toPattern(); + } + + public String getLocalizedTimeFormat() { + return ((SimpleDateFormat) android.text.format.DateFormat.getTimeFormat(_context)).toPattern(); + } + + public String getLocalizedDateTimeFormat() { + return getLocalizedDateFormat() + " " + getLocalizedTimeFormat(); + } + + /** + * A {@link InputFilter} for filenames + */ + @SuppressWarnings("Convert2Lambda") + public static final InputFilter INPUTFILTER_FILENAME = new InputFilter() { + public CharSequence filter(CharSequence src, int start, int end, Spanned dest, int dstart, int dend) { + if (src.length() < 1) return null; + char last = src.charAt(src.length() - 1); + String illegal = "|\\?*<\":>[]/'"; + if (illegal.indexOf(last) > -1) return src.subSequence(0, src.length() - 1); + return null; + } + }; + + /** + * A simple {@link Runnable} which does a touch event on a view. + * This pops up e.g. the keyboard on a {@link android.widget.EditText} + *

+ * Example: new Handler().postDelayed(new DoTouchView(editView), 200); + */ + public static class DoTouchView implements Runnable { + View _view; + + public DoTouchView(View view) { + _view = view; + } + + @Override + public void run() { + _view.dispatchTouchEvent(MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(), MotionEvent.ACTION_DOWN, 0, 0, 0)); + _view.dispatchTouchEvent(MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(), MotionEvent.ACTION_UP, 0, 0, 0)); + } + } + + + public String getMimeType(final File file) { + return getMimeType(Uri.fromFile(file)); + } + + /** + * Detect MimeType of given file + * Android/Java's own MimeType map is very very small and detection barely works at all + * Hence use custom map for some file extensions + */ + public String getMimeType(final Uri uri) { + String mimeType = null; + if (ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) { + ContentResolver cr = _context.getContentResolver(); + mimeType = cr.getType(uri); + } else { + String filename = uri.toString(); + if (filename.endsWith(".jenc")) { + filename = filename.replace(".jenc", ""); + } + String ext = MimeTypeMap.getFileExtensionFromUrl(filename); + mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(ext.toLowerCase()); + + // Try to guess if the recommended methods fail + if (TextUtils.isEmpty(mimeType)) { + switch (ext) { + case "md": + case "markdown": + case "mkd": + case "mdown": + case "mkdn": + case "mdwn": + case "rmd": + mimeType = "text/markdown"; + break; + case "yaml": + case "yml": + mimeType = "text/yaml"; + break; + case "json": + mimeType = "text/json"; + break; + case "txt": + mimeType = "text/plain"; + break; + } + } + } + + if (TextUtils.isEmpty(mimeType)) { + mimeType = "*/*"; + } + return mimeType; + } + + public Integer parseColor(final String colorstr) { + if (colorstr == null || colorstr.trim().isEmpty()) { + return null; + } + try { + return Color.parseColor(colorstr); + } catch (IllegalArgumentException ignored) { + return null; + } + } + + public boolean isDeviceGoodHardware() { + try { + ActivityManager activityManager = (ActivityManager) _context.getSystemService(Context.ACTIVITY_SERVICE); + return !ActivityManagerCompat.isLowRamDevice(activityManager) && + Runtime.getRuntime().availableProcessors() >= 4 && + activityManager.getMemoryClass() >= 128; + } catch (Exception ignored) { + return true; + } + } + + // Vibrate device one time by given amount of time, defaulting to 50ms + // Requires in AndroidManifest to work + @SuppressWarnings("UnnecessaryReturnStatement") + @SuppressLint("MissingPermission") + public void vibrate(final int... ms) { + int ms_v = ms != null && ms.length > 0 ? ms[0] : 50; + Vibrator vibrator = ((Vibrator) _context.getSystemService(VIBRATOR_SERVICE)); + if (vibrator == null) { + return; + } else if (Build.VERSION.SDK_INT >= 26) { + vibrator.vibrate(VibrationEffect.createOneShot(ms_v, VibrationEffect.DEFAULT_AMPLITUDE)); + } else { + vibrator.vibrate(ms_v); + } + } + + /* + Check if Wifi is connected. Requires these permissions in AndroidManifest: + + + */ + @SuppressLint("MissingPermission") + public boolean isWifiConnected(boolean... enabledOnly) { + final boolean doEnabledCheckOnly = enabledOnly != null && enabledOnly.length > 0 && enabledOnly[0]; + final ConnectivityManager connectivityManager = (ConnectivityManager) _context.getApplicationContext().getSystemService(Context.CONNECTIVITY_SERVICE); + final NetworkInfo wifiInfo = connectivityManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI); + return wifiInfo != null && (doEnabledCheckOnly ? wifiInfo.isAvailable() : wifiInfo.isConnected()); + } + + // Returns if the device is currently in portrait orientation (landscape=false) + public boolean isDeviceOrientationPortrait() { + final int rotation = ((WindowManager) _context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay().getOrientation(); + return (rotation == Surface.ROTATION_0) || (rotation == Surface.ROTATION_180); + } } + + diff --git a/app/src/main/java/net/gsantner/opoc/util/FileUtils.java b/app/src/main/java/net/gsantner/opoc/util/FileUtils.java index 5e02d17f..7dbd1658 100644 --- a/app/src/main/java/net/gsantner/opoc/util/FileUtils.java +++ b/app/src/main/java/net/gsantner/opoc/util/FileUtils.java @@ -1,21 +1,23 @@ /*####################################################### * - * Maintained by Gregor Santner, 2017- - * https://gsantner.net/ + * Maintained 2017-2023 by Gregor Santner * - * License: Apache 2.0 / Commercial - * https://github.com/gsantner/opoc/#licensing - * https://www.apache.org/licenses/LICENSE-2.0 + * 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; @@ -28,6 +30,7 @@ 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; @@ -35,20 +38,29 @@ import java.util.UUID; import java.util.concurrent.atomic.AtomicInteger; import java.util.regex.Pattern; -@SuppressWarnings({"WeakerAccess", "unused", "SameParameterValue", "SpellCheckingInspection", "deprecation"}) +@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(readCloseBinaryStream(new FileInputStream(file))); + 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)); @@ -230,6 +242,30 @@ public class FileUtils { } } + 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. // @@ -356,55 +392,70 @@ public class FileUtils { */ public static String getMimeType(File file) { String guess = null; - if (file != null && file.exists() && file.isFile()) { - InputStream is = null; - try { - is = new BufferedInputStream(new FileInputStream(file)); - guess = URLConnection.guessContentTypeFromStream(is); - } catch (IOException e) { - e.printStackTrace(); - } finally { - if (is != null) { - try { - is.close(); - } catch (IOException ignored) { + 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) { + } } } } - if (guess == null || guess.isEmpty()) { - guess = "*/*"; - int dot = file.getName().lastIndexOf(".") + 1; - if (dot > 0 && dot < file.getName().length()) { - switch (file.getName().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; - } + 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 guess; + + return TextUtils.isEmpty(guess) ? "*/*" : guess; } public static boolean isTextFile(File file) { - return getMimeType(file).startsWith("text/"); + 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) { + public static void retrieveTextFileSummary(File file, AtomicInteger numCharacters, AtomicInteger numLines, AtomicInteger numWords) { BufferedReader br = null; try { br = new BufferedReader(new FileReader(file)); @@ -412,11 +463,15 @@ public class FileUtils { 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 { @@ -437,7 +492,36 @@ public class FileUtils { } 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.#").format(size / Math.pow(1024, unit)) - + " " + units[unit]; + 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; } } diff --git a/app/src/main/java/net/gsantner/opoc/util/NetworkUtils.java b/app/src/main/java/net/gsantner/opoc/util/NetworkUtils.java index 6190188e..dc06674e 100644 --- a/app/src/main/java/net/gsantner/opoc/util/NetworkUtils.java +++ b/app/src/main/java/net/gsantner/opoc/util/NetworkUtils.java @@ -1,11 +1,10 @@ /*####################################################### * - * Maintained by Gregor Santner, 2017- - * https://gsantner.net/ + * Maintained 2017-2023 by Gregor Santner * - * License: Apache 2.0 / Commercial - * https://github.com/gsantner/opoc/#licensing - * https://www.apache.org/licenses/LICENSE-2.0 + * 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; @@ -77,7 +76,7 @@ public class NetworkUtils { int written = 0; final float invLength = 1f / connection.getContentLength(); - byte data[] = new byte[BUFFER_SIZE]; + byte[] data = new byte[BUFFER_SIZE]; while ((count = input.read(data)) != -1) { output.write(data, 0, count); if (invLength != -1f && progressCallback != null) { @@ -150,6 +149,7 @@ public class NetworkUtils { 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 @@ -160,7 +160,7 @@ public class NetworkUtils { if (data != null && !data.isEmpty()) { connection.setDoOutput(true); final OutputStream output = connection.getOutputStream(); - output.write(data.getBytes(Charset.forName(UTF8))); + output.write(data.getBytes(Charset.forName("UTF-8"))); output.flush(); output.close(); } @@ -220,4 +220,14 @@ public class NetworkUtils { return result; } + + public static void httpGetAsync(final String url, final Callback.a1 callback) { + new Thread(() -> { + try { + String c = NetworkUtils.performCall(url, GET); + callback.callback(c); + } catch (Exception ignored) { + } + }).start(); + } } diff --git a/app/src/main/java/net/gsantner/opoc/util/PermissionChecker.java b/app/src/main/java/net/gsantner/opoc/util/PermissionChecker.java index 36265218..be156745 100644 --- a/app/src/main/java/net/gsantner/opoc/util/PermissionChecker.java +++ b/app/src/main/java/net/gsantner/opoc/util/PermissionChecker.java @@ -1,11 +1,10 @@ /*####################################################### * - * Maintained by Gregor Santner, 2017- - * https://gsantner.net/ + * Maintained 2017-2023 by Gregor Santner * - * License: Apache 2.0 / Commercial - * https://github.com/gsantner/opoc/#licensing - * https://www.apache.org/licenses/LICENSE-2.0 + * 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; diff --git a/app/src/main/java/net/gsantner/opoc/util/ShareUtil.java b/app/src/main/java/net/gsantner/opoc/util/ShareUtil.java index 9b8d9fe8..dc467500 100644 --- a/app/src/main/java/net/gsantner/opoc/util/ShareUtil.java +++ b/app/src/main/java/net/gsantner/opoc/util/ShareUtil.java @@ -1,19 +1,26 @@ /*####################################################### * - * Maintained by Gregor Santner, 2017- - * https://gsantner.net/ + * Maintained 2017-2023 by Gregor Santner * - * License: Apache 2.0 / Commercial - * https://github.com/gsantner/opoc/#licensing - * https://www.apache.org/licenses/LICENSE-2.0 + * 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.Manifest; +import android.annotation.SuppressLint; import android.app.Activity; +import android.content.ActivityNotFoundException; +import android.content.BroadcastReceiver; import android.content.ClipData; import android.content.Context; import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.database.Cursor; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Paint; @@ -21,51 +28,89 @@ import android.net.Uri; import android.os.Build; import android.os.Environment; import android.os.Handler; +import android.os.ParcelFileDescriptor; import android.print.PrintAttributes; import android.print.PrintDocumentAdapter; import android.print.PrintJob; import android.print.PrintManager; +import android.provider.CalendarContract; +import android.provider.MediaStore; import android.support.annotation.DrawableRes; import android.support.annotation.Nullable; import android.support.annotation.RequiresApi; +import android.support.annotation.StringRes; +import android.support.v4.app.ActivityCompat; +import android.support.v4.content.ContextCompat; import android.support.v4.content.FileProvider; +import android.support.v4.content.LocalBroadcastManager; import android.support.v4.content.pm.ShortcutInfoCompat; import android.support.v4.content.pm.ShortcutManagerCompat; import android.support.v4.graphics.drawable.IconCompat; +import android.support.v4.provider.DocumentFile; +import android.support.v4.util.Pair; +import android.support.v7.app.AlertDialog; +import android.support.v7.preference.PreferenceManager; import android.text.TextUtils; import android.util.Log; import android.view.View; import android.webkit.WebView; +import android.widget.ImageView; +import android.widget.Toast; import java.io.File; +import java.io.FileDescriptor; +import java.io.FileInputStream; +import java.io.FileOutputStream; import java.io.IOException; import java.text.SimpleDateFormat; import java.util.ArrayList; +import java.util.Collection; +import java.util.Date; import java.util.List; import java.util.Locale; import java.util.Random; +import static android.app.Activity.RESULT_OK; + /** - * A utility class to ease information sharing on Android - * Also allows to parse/fetch information out of shared information + * A utility class to ease information sharing on Android. + * Also allows to parse/fetch information out of shared information. + * (M)Permissions are not checked, wrap ShareUtils methods if neccessary */ -@SuppressWarnings({"UnusedReturnValue", "WeakerAccess", "SameParameterValue", "unused", "deprecation", "ConstantConditions", "ObsoleteSdkInt", "SpellCheckingInspection"}) +@SuppressWarnings({"UnusedReturnValue", "WeakerAccess", "SameParameterValue", "unused", "deprecation", "ConstantConditions", "ObsoleteSdkInt", "SpellCheckingInspection", "JavadocReference", "ConstantLocale"}) public class ShareUtil { public final static String EXTRA_FILEPATH = "real_file_path_2"; - public final static SimpleDateFormat SDF_RFC3339_ISH = new SimpleDateFormat("yyyy-MM-dd'T'HH-mm", Locale.getDefault()); - public final static SimpleDateFormat SDF_SHORT = new SimpleDateFormat("yyMMdd-HHmm", Locale.getDefault()); + public final static SimpleDateFormat SDF_RFC3339_ISH = new SimpleDateFormat("yyyy-MM-dd'T'HH-mm-ss", Locale.getDefault()); + public final static SimpleDateFormat SDF_SHORT = new SimpleDateFormat("yyMMdd-HHmmss", Locale.getDefault()); + public final static SimpleDateFormat SDF_IMAGES = new SimpleDateFormat("yyyyMMdd-HHmmss", Locale.getDefault()); //20190511-230845 public final static String MIME_TEXT_PLAIN = "text/plain"; + public final static String PREF_KEY__SAF_TREE_URI = "pref_key__saf_tree_uri"; + public final static int REQUEST_CAMERA_PICTURE = 50001; + public final static int REQUEST_PICK_PICTURE = 50002; + public final static int REQUEST_SAF = 50003; + + public final static int MIN_OVERWRITE_LENGTH = 5; + + protected static String _lastCameraPictureFilepath; + protected static String _fileProviderAuthority; protected Context _context; - protected String _fileProviderAuthority; protected String _chooserTitle; - public ShareUtil(Context context) { + public ShareUtil(final Context context) { _context = context; _chooserTitle = "➥"; } + public void setContext(final Context c) { + _context = c; + } + + public void freeContextRef() { + _context = null; + } + public String getFileProviderAuthority() { if (TextUtils.isEmpty(_fileProviderAuthority)) { throw new RuntimeException("Error at ShareUtil.getFileProviderAuthority(): No FileProvider authority provided"); @@ -73,13 +118,12 @@ public class ShareUtil { return _fileProviderAuthority; } - public ShareUtil setFileProviderAuthority(String fileProviderAuthority) { + public static void setFileProviderAuthority(final String fileProviderAuthority) { _fileProviderAuthority = fileProviderAuthority; - return this; } - public ShareUtil setChooserTitle(String title) { + public ShareUtil setChooserTitle(final String title) { _chooserTitle = title; return this; } @@ -90,7 +134,7 @@ public class ShareUtil { * @param file the file * @return Uri for this file */ - public Uri getUriByFileProviderAuthority(File file) { + public Uri getUriByFileProviderAuthority(final File file) { return FileProvider.getUriForFile(_context, getFileProviderAuthority(), file); } @@ -100,9 +144,11 @@ public class ShareUtil { * @param intent Thing to be shared * @param chooserText The title text for the chooser, or null for default */ - public void showChooser(Intent intent, String chooserText) { - _context.startActivity(Intent.createChooser(intent, - chooserText != null ? chooserText : _chooserTitle)); + public void showChooser(final Intent intent, final String chooserText) { + try { + _context.startActivity(Intent.createChooser(intent, chooserText != null ? chooserText : _chooserTitle)); + } catch (Exception ignored) { + } } /** @@ -114,7 +160,7 @@ public class ShareUtil { * @param iconRes Icon resource for the item * @param title Title of the item */ - public void createLauncherDesktopShortcut(Intent intent, @DrawableRes int iconRes, String title) { + public void createLauncherDesktopShortcut(final Intent intent, @DrawableRes final int iconRes, final String title) { intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); if (intent.getAction() == null) { @@ -139,7 +185,7 @@ public class ShareUtil { * @param iconRes Icon resource for the item * @param title Title of the item */ - public void createLauncherDesktopShortcutLegacy(Intent intent, @DrawableRes int iconRes, String title) { + public void createLauncherDesktopShortcutLegacy(final Intent intent, @DrawableRes final int iconRes, final String title) { intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); if (intent.getAction() == null) { @@ -160,7 +206,7 @@ public class ShareUtil { * @param text The text to share * @param mimeType MimeType or null (uses text/plain) */ - public void shareText(String text, @Nullable String mimeType) { + public void shareText(final String text, @Nullable final String mimeType) { Intent intent = new Intent(Intent.ACTION_SEND); intent.putExtra(Intent.EXTRA_TEXT, text); intent.setType(mimeType != null ? mimeType : MIME_TEXT_PLAIN); @@ -173,13 +219,74 @@ public class ShareUtil { * @param file The file to share * @param mimeType The files mime type */ - public void shareStream(File file, String mimeType) { - Uri fileUri = FileProvider.getUriForFile(_context, getFileProviderAuthority(), file); + public boolean shareStream(final File file, final String mimeType) { Intent intent = new Intent(Intent.ACTION_SEND); - intent.putExtra(Intent.EXTRA_STREAM, fileUri); intent.putExtra(EXTRA_FILEPATH, file.getAbsolutePath()); intent.setType(mimeType); - showChooser(intent, null); + + try { + Uri fileUri = FileProvider.getUriForFile(_context, getFileProviderAuthority(), file); + intent.putExtra(Intent.EXTRA_STREAM, fileUri); + showChooser(intent, null); + return true; + } catch (Exception ignored) { // FileUriExposed(API24) / IllegalArgument + } + return false; + } + + /** + * Share the given files as stream with given mime-type + * + * @param files The files to share + * @param mimeType The files mime type. Usally * / * is the best option + */ + public boolean shareStreamMultiple(final Collection files, final String mimeType) { + ArrayList uris = new ArrayList<>(); + for (File file : files) { + File uri = new File(file.toString()); + uris.add(FileProvider.getUriForFile(_context, getFileProviderAuthority(), file)); + } + + try { + Intent intent = new Intent(Intent.ACTION_SEND_MULTIPLE); + intent.setType(mimeType); + intent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, uris); + showChooser(intent, null); + return true; + } catch (Exception e) { // FileUriExposed(API24) / IllegalArgument + return false; + } + } + + /** + * Start calendar application to add new event, with given details prefilled + */ + public boolean createCalendarAppointment(@Nullable final String title, @Nullable final String description, @Nullable final String location, @Nullable final Long... startAndEndTime) { + Intent intent = new Intent(Intent.ACTION_INSERT).setData(CalendarContract.Events.CONTENT_URI); + if (title != null) { + intent.putExtra(CalendarContract.Events.TITLE, title); + } + if (description != null) { + intent.putExtra(CalendarContract.Events.DESCRIPTION, (description.length() > 800 ? description.substring(0, 800) : description)); + } + if (location != null) { + intent.putExtra(CalendarContract.Events.EVENT_LOCATION, location); + } + if (startAndEndTime != null) { + if (startAndEndTime.length > 0 && startAndEndTime[0] > 0) { + intent.putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, startAndEndTime[0]); + } + if (startAndEndTime.length > 1 && startAndEndTime[1] > 0) { + intent.putExtra(CalendarContract.EXTRA_EVENT_END_TIME, startAndEndTime[1]); + } + } + + try { + _context.startActivity(intent); + return true; + } catch (ActivityNotFoundException e) { + return false; + } } /** @@ -187,26 +294,29 @@ public class ShareUtil { * * @param file The file to share */ - public void viewFileInOtherApp(File file, @Nullable String type) { - Uri fileUri = FileProvider.getUriForFile(_context, getFileProviderAuthority(), file); - Intent intent = new Intent(Intent.ACTION_VIEW); - intent.putExtra(Intent.EXTRA_STREAM, fileUri); - intent.setData(fileUri); - intent.putExtra(EXTRA_FILEPATH, file.getAbsolutePath()); - intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); - intent.setDataAndType(fileUri, type); - showChooser(intent, null); - } + public boolean viewFileInOtherApp(final File file, @Nullable final String type) { + // On some specific devices the first won't work + Uri fileUri = null; + try { + fileUri = FileProvider.getUriForFile(_context, getFileProviderAuthority(), file); + } catch (Exception ignored) { + try { + fileUri = Uri.fromFile(file); + } catch (Exception ignored2) { + } + } - /** - * Share the given bitmap with given format - * - * @param bitmap Image - * @param format A {@link Bitmap.CompressFormat}, supporting JPEG,PNG,WEBP - * @return if success, true - */ - public boolean shareImage(Bitmap bitmap, Bitmap.CompressFormat format) { - return shareImage(bitmap, format, 95, "SharedImage"); + if (fileUri != null) { + Intent intent = new Intent(Intent.ACTION_VIEW); + intent.putExtra(Intent.EXTRA_STREAM, fileUri); + intent.setData(fileUri); + intent.putExtra(EXTRA_FILEPATH, file.getAbsolutePath()); + intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); + intent.setDataAndType(fileUri, type); + showChooser(intent, null); + return true; + } + return false; } /** @@ -218,20 +328,36 @@ public class ShareUtil { * @param quality Quality of the exported image [0-100] * @return if success, true */ - public boolean shareImage(Bitmap bitmap, Bitmap.CompressFormat format, int quality, String imageName) { + public boolean shareImage(final Bitmap bitmap, final Integer... quality) { try { - String ext = format.name().toLowerCase(); - File file = File.createTempFile(imageName, "." + ext.replace("jpeg", "jpg"), _context.getExternalCacheDir()); - if (bitmap != null && new ContextUtils(_context).writeImageToFile(file, bitmap, format, quality)) { - shareStream(file, "image/" + ext); + File file = new File(_context.getCacheDir(), getFilenameWithTimestamp()); + if (bitmap != null && new ContextUtils(_context).writeImageToFile(file, bitmap, quality)) { + String x = FileUtils.getMimeType(file); + shareStream(file, FileUtils.getMimeType(file)); return true; } - } catch (IOException e) { - e.printStackTrace(); + } catch (Exception ignored) { } return false; } + /** + * Generate a filename based off current datetime in filename (year, month, day, hour, minute, second) + * Examples: Screenshot_20210208-184301_Trebuchet.png IMG_20190511-230845.jpg + * + * @param A0prefixA1postfixA2ext All arguments are optional and default values are taken for null + * [0] = Prefix [Screenshot/IMG] + * [1] = Postfix [Trebuchet] + * [2] = File extensions [jpg/png/txt] + * @return Filename + */ + public static String getFilenameWithTimestamp(String... A0prefixA1postfixA2ext) { + final String prefix = (((A0prefixA1postfixA2ext != null && A0prefixA1postfixA2ext.length > 0 && !TextUtils.isEmpty(A0prefixA1postfixA2ext[0])) ? A0prefixA1postfixA2ext[0] : "Screenshot") + "_").trim().replaceFirst("^_$", ""); + final String postfix = ("_" + ((A0prefixA1postfixA2ext != null && A0prefixA1postfixA2ext.length > 1 && !TextUtils.isEmpty(A0prefixA1postfixA2ext[1])) ? A0prefixA1postfixA2ext[1] : "")).trim().replaceFirst("^_$", ""); + final String ext = (A0prefixA1postfixA2ext != null && A0prefixA1postfixA2ext.length > 2 && !TextUtils.isEmpty(A0prefixA1postfixA2ext[2])) ? A0prefixA1postfixA2ext[2] : "jpg"; + return String.format("%s%s%s.%s", prefix.trim(), SDF_IMAGES.format(new Date()), postfix.trim(), ext.toLowerCase().replace(".", "").replace("jpeg", "jpg")); + } + /** * Print a {@link WebView}'s contents, also allows to create a PDF * @@ -240,18 +366,25 @@ public class ShareUtil { * @return {{@link PrintJob}} or null */ @RequiresApi(api = Build.VERSION_CODES.KITKAT) - @SuppressWarnings("deprecation") - public PrintJob print(WebView webview, String jobName) { + public PrintJob print(final WebView webview, final String jobName, final boolean... landscape) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { - PrintDocumentAdapter printAdapter; - PrintManager printManager = (PrintManager) webview.getContext().getSystemService(Context.PRINT_SERVICE); + final PrintDocumentAdapter printAdapter; + final PrintManager printManager = (PrintManager) _context.getSystemService(Context.PRINT_SERVICE); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { printAdapter = webview.createPrintDocumentAdapter(jobName); } else { printAdapter = webview.createPrintDocumentAdapter(); } + final PrintAttributes.Builder attrib = new PrintAttributes.Builder(); + if (landscape != null && landscape.length > 0 && landscape[0]) { + attrib.setMediaSize(new PrintAttributes.MediaSize("ISO_A4", "android", 11690, 8270)); + attrib.setMinMargins(new PrintAttributes.Margins(0, 0, 0, 0)); + } if (printManager != null) { - return printManager.print(jobName, printAdapter, new PrintAttributes.Builder().build()); + try { + return printManager.print(jobName, printAdapter, attrib.build()); + } catch (Exception ignored) { + } } } else { Log.e(getClass().getName(), "ERROR: Method called on too low Android API version"); @@ -264,8 +397,7 @@ public class ShareUtil { * See {@link #print(WebView, String) print method} */ @RequiresApi(api = Build.VERSION_CODES.KITKAT) - @SuppressWarnings("deprecation") - public PrintJob createPdf(WebView webview, String jobName) { + public PrintJob createPdf(final WebView webview, final String jobName) { return print(webview, jobName); } @@ -277,21 +409,21 @@ public class ShareUtil { * @return A {@link Bitmap} or null */ @Nullable - public static Bitmap getBitmapFromWebView(WebView webView) { + public static Bitmap getBitmapFromWebView(final WebView webView, final boolean... a0fullpage) { try { //Measure WebView's content - int widthMeasureSpec = View.MeasureSpec.makeMeasureSpec(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED); - int heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED); - webView.measure(widthMeasureSpec, heightMeasureSpec); - webView.layout(0, 0, webView.getMeasuredWidth(), webView.getMeasuredHeight()); + if (a0fullpage != null && a0fullpage.length > 0 && a0fullpage[0]) { + int widthMeasureSpec = View.MeasureSpec.makeMeasureSpec(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED); + int heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED); + webView.measure(widthMeasureSpec, heightMeasureSpec); + webView.layout(0, 0, webView.getMeasuredWidth(), webView.getMeasuredHeight()); + } //Build drawing cache and store its size webView.buildDrawingCache(); - int measuredWidth = webView.getMeasuredWidth(); - int measuredHeight = webView.getMeasuredHeight(); //Creates the bitmap and draw WebView's content on in - Bitmap bitmap = Bitmap.createBitmap(measuredWidth, measuredHeight, Bitmap.Config.ARGB_8888); + Bitmap bitmap = Bitmap.createBitmap(webView.getMeasuredWidth(), webView.getMeasuredHeight(), Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(bitmap); canvas.drawBitmap(bitmap, 0, bitmap.getHeight(), new Paint()); @@ -310,7 +442,7 @@ public class ShareUtil { * Replace (primary) clipboard contents with given {@code text} * @param text Text to be set */ - public boolean setClipboard(CharSequence text) { + public boolean setClipboard(final CharSequence text) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) { android.text.ClipboardManager cm = ((android.text.ClipboardManager) _context.getSystemService(Context.CLIPBOARD_SERVICE)); if (cm != null) { @@ -321,7 +453,10 @@ public class ShareUtil { android.content.ClipboardManager cm = ((android.content.ClipboardManager) _context.getSystemService(Context.CLIPBOARD_SERVICE)); if (cm != null) { ClipData clip = ClipData.newPlainText(_context.getPackageName(), text); - cm.setPrimaryClip(clip); + try { + cm.setPrimaryClip(clip); + } catch (Exception ignored) { + } return true; } } @@ -363,7 +498,7 @@ public class ShareUtil { * @param callback Callback after paste try * @param serverOrNothing Supply one or no hastebin server. If empty, the default gets taken */ - public void pasteOnHastebin(final String text, final Callback.a2 callback, String... serverOrNothing) { + public void pasteOnHastebin(final String text, final Callback.a2 callback, final String... serverOrNothing) { final Handler handler = new Handler(); final String server = (serverOrNothing != null && serverOrNothing.length > 0 && serverOrNothing[0] != null) ? serverOrNothing[0] : "https://hastebin.com"; @@ -385,7 +520,7 @@ public class ShareUtil { * @param body Body (content) text to be prefilled in the mail * @param to recipients to be prefilled in the mail */ - public void draftEmail(String subject, String body, String... to) { + public void draftEmail(final String subject, final String body, final String... to) { Intent intent = new Intent(Intent.ACTION_SENDTO); intent.setData(Uri.parse("mailto:")); if (subject != null) { @@ -406,7 +541,7 @@ public class ShareUtil { * @param receivingIntent The intent from {@link Activity#getIntent()} * @return A file or null if extraction did not succeed */ - public File extractFileFromIntent(Intent receivingIntent) { + public File extractFileFromIntent(final Intent receivingIntent) { String action = receivingIntent.getAction(); String type = receivingIntent.getType(); File tmpf; @@ -441,6 +576,17 @@ public class ShareUtil { fileStr = fileStr.substring(prefix.length()); } } + + // prefix for External storage (/storage/emulated/0 /// /sdcard/) --> e.g. "content://com.amaze.filemanager/storage_root/file.txt" = "/sdcard/file.txt" + for (String prefix : new String[]{"external/", "media/", "storage_root/"}) { + if (fileStr.startsWith((tmps = prefix))) { + File f = new File(Uri.decode(Environment.getExternalStorageDirectory().getAbsolutePath() + "/" + fileStr.substring(tmps.length()))); + if (f.exists()) { + return f; + } + } + } + // Next/OwnCloud Fileprovider for (String fp : new String[]{"org.nextcloud.files", "org.nextcloud.beta.files", "org.owncloud.files"}) { if (fileProvider.equals(fp) && fileStr.startsWith(tmps = "external_files/")) { @@ -455,6 +601,17 @@ public class ShareUtil { if (fileProvider.equals("com.mi.android.globalFileexplorer.myprovider") && fileStr.startsWith(tmps = "external_files")) { return new File(Uri.decode(Environment.getExternalStorageDirectory().getAbsolutePath() + fileStr.substring(tmps.length()))); } + + if (fileStr.startsWith(tmps = "external_files/")) { + for (String prefix : new String[]{Environment.getExternalStorageDirectory().getAbsolutePath(), "/storage", ""}) { + File f = new File(Uri.decode(prefix + "/" + fileStr.substring(tmps.length()))); + if (f.exists()) { + return f; + } + } + + } + // URI Encoded paths with full path after content://package/ if (fileStr.startsWith("/") || fileStr.startsWith("%2F")) { tmpf = new File(Uri.decode(fileStr)); @@ -473,4 +630,586 @@ public class ShareUtil { } return null; } + + /** + * Request a picture from gallery + * Result will be available from {@link Activity#onActivityResult(int, int, Intent)}. + * It will return the path to the image if locally stored. If retrieved from e.g. a cloud + * service, the image will get copied to app-cache folder and it's path returned. + */ + public void requestGalleryPicture() { + if (!(_context instanceof Activity)) { + throw new RuntimeException("Error: ShareUtil.requestGalleryPicture needs an Activity Context."); + } + Intent intent = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI); + try { + ((Activity) _context).startActivityForResult(intent, REQUEST_PICK_PICTURE); + } catch (Exception ex) { + Toast.makeText(_context, "No gallery app installed!", Toast.LENGTH_SHORT).show(); + } + } + + public String extractFileFromIntentStr(final Intent receivingIntent) { + File f = extractFileFromIntent(receivingIntent); + return f != null ? f.getAbsolutePath() : null; + } + + /** + * Request a picture from camera-like apps + * Result ({@link String}) will be available from {@link Activity#onActivityResult(int, int, Intent)}. + * It has set resultCode to {@link Activity#RESULT_OK} with same requestCode, if successfully + * The requested image savepath has to be stored at caller side (not contained in intent), + * it can be retrieved using {@link #extractResultFromActivityResult(int, int, Intent, Activity...)} + * returns null if an error happened. + * + * @param target Path to file to write to, if folder the filename gets app_name + millis + random filename. If null DCIM folder is used. + */ + @SuppressWarnings("RegExpRedundantEscape") + public String requestCameraPicture(final File target) { + if (!(_context instanceof Activity)) { + throw new RuntimeException("Error: ShareUtil.requestCameraPicture needs an Activity Context."); + } + String cameraPictureFilepath = null; + Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); + if (takePictureIntent.resolveActivity(_context.getPackageManager()) != null) { + File photoFile; + try { + // Create an image file name + if (target != null && !target.isDirectory()) { + photoFile = target; + } else { + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH-mm-ss", Locale.ENGLISH); + File storageDir = target != null ? target : new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM), "Camera"); + String imageFileName = ((new ContextUtils(_context).rstr("app_name")).replaceAll("[^a-zA-Z0-9\\.\\-]", "_") + "_").replace("__", "_") + sdf.format(new Date()); + photoFile = new File(storageDir, imageFileName + ".jpg"); + if (!photoFile.getParentFile().exists() && !photoFile.getParentFile().mkdirs()) { + photoFile = File.createTempFile(imageFileName + "_", ".jpg", storageDir); + } + } + + //noinspection StatementWithEmptyBody + if (!photoFile.getParentFile().exists() && photoFile.getParentFile().mkdirs()) ; + + // Save a file: path for use with ACTION_VIEW intents + cameraPictureFilepath = photoFile.getAbsolutePath(); + } catch (IOException ex) { + return null; + } + + // Continue only if the File was successfully created + if (photoFile != null) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + Uri uri = FileProvider.getUriForFile(_context, getFileProviderAuthority(), photoFile); + takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, uri); + } else { + takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(photoFile)); + } + ((Activity) _context).startActivityForResult(takePictureIntent, REQUEST_CAMERA_PICTURE); + } + } + _lastCameraPictureFilepath = cameraPictureFilepath; + return cameraPictureFilepath; + } + + /** + * Extract result data from {@link Activity#onActivityResult(int, int, Intent)}. + * Forward all arguments from activity. Only requestCodes from {@link ShareUtil} get analyzed. + * Also may forward results via local broadcast + */ + @SuppressLint("ApplySharedPref") + public Object extractResultFromActivityResult(final int requestCode, final int resultCode, final Intent data, final Activity... activityOrNull) { + Activity activity = greedyGetActivity(activityOrNull); + switch (requestCode) { + case REQUEST_CAMERA_PICTURE: { + String picturePath = (resultCode == RESULT_OK) ? _lastCameraPictureFilepath : null; + if (picturePath != null) { + sendLocalBroadcastWithStringExtra(REQUEST_CAMERA_PICTURE + "", EXTRA_FILEPATH, picturePath); + } + return picturePath; + } + case REQUEST_PICK_PICTURE: { + if (resultCode == RESULT_OK && data != null) { + Uri selectedImage = data.getData(); + String[] filePathColumn = {MediaStore.Images.Media.DATA}; + String picturePath = null; + + Cursor cursor = _context.getContentResolver().query(selectedImage, filePathColumn, null, null, null); + if (cursor != null && cursor.moveToFirst()) { + for (String column : filePathColumn) { + int curColIndex = cursor.getColumnIndex(column); + if (curColIndex == -1) { + continue; + } + picturePath = cursor.getString(curColIndex); + if (!TextUtils.isEmpty(picturePath)) { + break; + } + } + cursor.close(); + } + + // Try to grab via file extraction method + data.setAction(Intent.ACTION_VIEW); + picturePath = picturePath != null ? picturePath : extractFileFromIntentStr(data); + + // Retrieve image from file descriptor / Cloud, e.g.: Google Drive, Picasa + if (picturePath == null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + try { + ParcelFileDescriptor parcelFileDescriptor = _context.getContentResolver().openFileDescriptor(selectedImage, "r"); + if (parcelFileDescriptor != null) { + FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor(); + FileInputStream input = new FileInputStream(fileDescriptor); + + // Create temporary file in cache directory + picturePath = File.createTempFile("image", "tmp", _context.getCacheDir()).getAbsolutePath(); + FileUtils.writeFile(new File(picturePath), FileUtils.readCloseBinaryStream(input)); + } + } catch (IOException ignored) { + // nothing we can do here, null value will be handled below + } + } + + // Return path to picture on success, else null + if (picturePath != null) { + sendLocalBroadcastWithStringExtra(REQUEST_CAMERA_PICTURE + "", EXTRA_FILEPATH, picturePath); + } + return picturePath; + } + break; + } + + case REQUEST_SAF: { + if (resultCode == RESULT_OK && data != null && data.getData() != null) { + Uri treeUri = data.getData(); + PreferenceManager.getDefaultSharedPreferences(_context).edit().putString(PREF_KEY__SAF_TREE_URI, treeUri.toString()).commit(); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + activity.getContentResolver().takePersistableUriPermission(treeUri, Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); + } + return treeUri; + } + break; + } + } + return null; + } + + /** + * Send a local broadcast (to receive within app), with given action and string-extra+value. + * This is a convenience method for quickly sending just one thing. + */ + public void sendLocalBroadcastWithStringExtra(final String action, final String extra, final CharSequence value) { + Intent intent = new Intent(action); + intent.putExtra(extra, value); + LocalBroadcastManager.getInstance(_context).sendBroadcast(intent); + } + + /** + * Receive broadcast results via a callback method + * + * @param callback Function to call with received {@link Intent} + * @param autoUnregister wether or not to automatically unregister receiver after first match + * @param filterActions All {@link IntentFilter} actions to filter for + * @return The created instance. Has to be unregistered on {@link Activity} lifecycle events. + */ + public BroadcastReceiver receiveResultFromLocalBroadcast(final Callback.a2 callback, final boolean autoUnregister, final String... filterActions) { + IntentFilter intentFilter = new IntentFilter(); + for (String filterAction : filterActions) { + intentFilter.addAction(filterAction); + } + final BroadcastReceiver br = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (intent != null) { + if (autoUnregister) { + LocalBroadcastManager.getInstance(_context).unregisterReceiver(this); + } + try { + callback.callback(intent, this); + } catch (Exception ignored) { + } + } + } + }; + LocalBroadcastManager.getInstance(_context).registerReceiver(br, intentFilter); + return br; + } + + /** + * Request edit of image (by image editor/viewer - for example to crop image) + * + * @param file File that should be edited + */ + public void requestPictureEdit(final File file) { + Uri uri = getUriByFileProviderAuthority(file); + int flags = Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION; + + Intent intent = new Intent(Intent.ACTION_EDIT); + intent.setDataAndType(uri, "image/*"); + intent.addFlags(flags); + intent.putExtra(MediaStore.EXTRA_OUTPUT, uri); + intent.putExtra(EXTRA_FILEPATH, file.getAbsolutePath()); + + for (ResolveInfo resolveInfo : _context.getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY)) { + String packageName = resolveInfo.activityInfo.packageName; + _context.grantUriPermission(packageName, uri, flags); + } + _context.startActivity(Intent.createChooser(intent, null)); + } + + /** + * Get content://media/ Uri for given file, or null if not indexed + * + * @param file Target file + * @param mode 1 for picture, 2 for video, anything else for other + * @return Media URI + */ + @SuppressWarnings("TryFinallyCanBeTryWithResources") + public Uri getMediaUri(final File file, final int mode) { + Uri uri = MediaStore.Files.getContentUri("external"); + uri = (mode != 0) ? (mode == 1 ? MediaStore.Images.Media.EXTERNAL_CONTENT_URI : MediaStore.Video.Media.EXTERNAL_CONTENT_URI) : uri; + + Cursor cursor = null; + try { + cursor = _context.getContentResolver().query(uri, new String[]{MediaStore.Images.Media._ID}, MediaStore.Images.Media.DATA + "= ?", new String[]{file.getAbsolutePath()}, null); + if (cursor != null && cursor.moveToFirst()) { + int mediaid = cursor.getInt(cursor.getColumnIndex(MediaStore.Images.Media._ID)); + return Uri.withAppendedPath(uri, mediaid + ""); + } + } catch (Exception ignored) { + } finally { + if (cursor != null) { + cursor.close(); + } + } + return null; + } + + /** + * By default Chrome Custom Tabs only uses Chrome Stable to open links + * There are also other packages (like Chrome Beta, Chromium, Firefox, ..) + * which implement the Chrome Custom Tab interface. This method changes + * the customtab intent to use an available compatible browser, if available. + */ + public void enableChromeCustomTabsForOtherBrowsers(final Intent customTabIntent) { + String[] checkpkgs = new String[]{ + "com.android.chrome", "com.chrome.beta", "com.chrome.dev", "com.google.android.apps.chrome", "org.chromium.chrome", + "org.mozilla.fennec_fdroid", "org.mozilla.firefox", "org.mozilla.firefox_beta", "org.mozilla.fennec_aurora", + "org.mozilla.klar", "org.mozilla.focus", + }; + + // Get all intent handlers for web links + PackageManager pm = _context.getPackageManager(); + Intent urlIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("https://www.example.com")); + List browsers = new ArrayList<>(); + for (ResolveInfo ri : pm.queryIntentActivities(urlIntent, 0)) { + Intent i = new Intent("android.support.customtabs.action.CustomTabsService"); + i.setPackage(ri.activityInfo.packageName); + if (pm.resolveService(i, 0) != null) { + browsers.add(ri.activityInfo.packageName); + } + } + + // Check if the user has a "default browser" selected + ResolveInfo ri = pm.resolveActivity(urlIntent, 0); + String userDefaultBrowser = (ri == null) ? null : ri.activityInfo.packageName; + + // Select which browser to use out of all installed customtab supporting browsers + String pkg = null; + if (browsers.isEmpty()) { + pkg = null; + } else if (browsers.size() == 1) { + pkg = browsers.get(0); + } else if (!TextUtils.isEmpty(userDefaultBrowser) && browsers.contains(userDefaultBrowser)) { + pkg = userDefaultBrowser; + } else { + for (String checkpkg : checkpkgs) { + if (browsers.contains(checkpkg)) { + pkg = checkpkg; + break; + } + } + if (pkg == null && !browsers.isEmpty()) { + pkg = browsers.get(0); + } + } + if (pkg != null && customTabIntent != null) { + customTabIntent.setPackage(pkg); + } + } + + /*** + * Request storage access. The user needs to press "Select storage" at the correct storage. + * @param activity The activity which will receive the result from startActivityForResult + */ + public void requestStorageAccessFramework(final Activity... activity) { + Activity a = greedyGetActivity(activity); + if (a != null && android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) { + Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE); + intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION + | Intent.FLAG_GRANT_WRITE_URI_PERMISSION + | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION + | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION + ); + a.startActivityForResult(intent, REQUEST_SAF); + } + } + + /** + * Get storage access framework tree uri. The user must have granted access via {@link #requestStorageAccessFramework(Activity...)} + * + * @return Uri or null if not granted yet + */ + public Uri getStorageAccessFrameworkTreeUri() { + String treeStr = PreferenceManager.getDefaultSharedPreferences(_context).getString(PREF_KEY__SAF_TREE_URI, null); + if (!TextUtils.isEmpty(treeStr)) { + try { + return Uri.parse(treeStr); + } catch (Exception ignored) { + } + } + return null; + } + + /** + * Get mounted storage folder root (by tree uri). The user must have granted access via {@link #requestStorageAccessFramework(Activity...)} + * + * @return File or null if SD not mounted + */ + public File getStorageAccessFolder() { + Uri safUri = getStorageAccessFrameworkTreeUri(); + if (safUri != null) { + String safUriStr = safUri.toString(); + ContextUtils cu = new ContextUtils(_context); + for (Pair storage : cu.getStorages(false, true)) { + @SuppressWarnings("ConstantConditions") String storageFolderName = storage.first.getName(); + if (safUriStr.contains(storageFolderName)) { + return storage.first; + } + } + cu.freeContextRef(); + } + return null; + } + + /** + * Check whether or not a file is under a storage access folder (external storage / SD) + * + * @param file The file object (file/folder) + * @return Wether or not the file is under storage access folder + */ + public boolean isUnderStorageAccessFolder(final File file) { + if (file != null) { + // When file writeable as is, it's the fastest way to learn SAF isn't required + if (file.canWrite()) { + return false; + } + ContextUtils cu = new ContextUtils(_context); + for (Pair storage : cu.getStorages(false, true)) { + if (file.getAbsolutePath().startsWith(storage.first.getAbsolutePath())) { + cu.freeContextRef(); + return true; + } + } + cu.freeContextRef(); + } + return false; + } + + /** + * Greedy extract Activity from parameter or convert context if it's a activity + */ + private Activity greedyGetActivity(final Activity... activity) { + if (activity != null && activity.length != 0 && activity[0] != null) { + return activity[0]; + } + if (_context instanceof Activity) { + return (Activity) _context; + } + return null; + } + + /** + * Check whether or not a file can be written. + * Requires storage access framework permission for external storage (SD) + * + * @param file The file object (file/folder) + * @param isDir Wether or not the given file parameter is a directory + * @return Wether or not the file can be written + */ + public boolean canWriteFile(final File file, final boolean isDir) { + if (file == null) { + return false; + } else if (file.getAbsolutePath().startsWith(Environment.getExternalStorageDirectory().getAbsolutePath()) + || file.getAbsolutePath().startsWith(_context.getFilesDir().getAbsolutePath())) { + boolean s1 = isDir && file.getParentFile().canWrite(); + return !isDir && file.getParentFile() != null ? file.getParentFile().canWrite() : file.canWrite(); + } else { + DocumentFile dof = getDocumentFile(file, isDir); + return dof != null && dof.canWrite(); + } + } + + /** + * Get a {@link DocumentFile} object out of a normal java {@link File}. + * When used on a external storage (SD), use {@link #requestStorageAccessFramework(Activity...)} + * first to get access. Otherwise this will fail. + * + * @param file The file/folder to convert + * @param isDir Wether or not file is a directory. For non-existing (to be created) files this info is not known hence required. + * @return A {@link DocumentFile} object or null if file cannot be converted + */ + @SuppressWarnings("RegExpRedundantEscape") + public DocumentFile getDocumentFile(final File file, final boolean isDir) { + // On older versions use fromFile + if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) { + return DocumentFile.fromFile(file); + } + + // Get ContextUtils to find storageRootFolder + ContextUtils cu = new ContextUtils(_context); + File baseFolderFile = cu.getStorageRootFolder(file); + cu.freeContextRef(); + + String baseFolder = baseFolderFile == null ? null : baseFolderFile.getAbsolutePath(); + boolean originalDirectory = false; + if (baseFolder == null) { + return null; + } + + String relPath = null; + try { + String fullPath = file.getCanonicalPath(); + if (!baseFolder.equals(fullPath)) { + relPath = fullPath.substring(baseFolder.length() + 1); + } else { + originalDirectory = true; + } + } catch (IOException e) { + return null; + } catch (Exception ignored) { + originalDirectory = true; + } + Uri treeUri; + if ((treeUri = getStorageAccessFrameworkTreeUri()) == null) { + return null; + } + DocumentFile dof = DocumentFile.fromTreeUri(_context, treeUri); + if (originalDirectory) { + return dof; + } + String[] parts = relPath.split("\\/"); + for (int i = 0; i < parts.length; i++) { + DocumentFile nextDof = dof.findFile(parts[i]); + if (nextDof == null) { + try { + nextDof = ((i < parts.length - 1) || isDir) ? dof.createDirectory(parts[i]) : dof.createFile("image", parts[i]); + } catch (Exception ignored) { + nextDof = null; + } + } + dof = nextDof; + } + return dof; + } + + public void showMountSdDialog(@StringRes final int title, @StringRes final int description, @DrawableRes final int mountDescriptionGraphic, final Activity... activityOrNull) { + Activity activity = greedyGetActivity(activityOrNull); + if (activity == null) { + return; + } + + // Image viewer + ImageView imv = new ImageView(activity); + imv.setImageResource(mountDescriptionGraphic); + imv.setAdjustViewBounds(true); + + AlertDialog.Builder dialog = new AlertDialog.Builder(activity); + dialog.setView(imv); + dialog.setTitle(title); + dialog.setMessage(_context.getString(description) + "\n\n"); + dialog.setNegativeButton(android.R.string.cancel, null); + dialog.setPositiveButton(android.R.string.yes, (dialogInterface, i) -> requestStorageAccessFramework(activity)); + AlertDialog dialogi = dialog.create(); + dialogi.show(); + } + + @SuppressWarnings({"ResultOfMethodCallIgnored", "StatementWithEmptyBody"}) + public void writeFile(final File file, final boolean isDirectory, final Callback.a2 writeFileCallback) { + try { + FileOutputStream fileOutputStream = null; + ParcelFileDescriptor pfd = null; + final boolean existingEmptyFile = file.canWrite() && file.length() < MIN_OVERWRITE_LENGTH; + final boolean nonExistingCreatableFile = !file.exists() && file.getParentFile().canWrite(); + if (existingEmptyFile || nonExistingCreatableFile) { + if (isDirectory) { + file.mkdirs(); + } else { + fileOutputStream = new FileOutputStream(file); + } + } else { + DocumentFile dof = getDocumentFile(file, isDirectory); + if (dof != null && dof.getUri() != null && dof.canWrite()) { + if (isDirectory) { + // Nothing to do + } else { + pfd = _context.getContentResolver().openFileDescriptor(dof.getUri(), "rwt"); + fileOutputStream = new FileOutputStream(pfd.getFileDescriptor()); + } + } + } + if (writeFileCallback != null) { + writeFileCallback.callback(fileOutputStream != null || (isDirectory && file.exists()), fileOutputStream); + } + if (fileOutputStream != null) { + try { + fileOutputStream.close(); + } catch (Exception ignored) { + } + } + if (pfd != null) { + pfd.close(); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** + * Call telephone number. + * Non direct call, opens up the dialer and pre-sets the telephone number. User needs to press manually. + * Direct call requires M permission granted, also add permissions to manifest: + * + * + * @param telNo The telephone number to call + * @param directCall Direct call number if possible + */ + @SuppressWarnings("SimplifiableConditionalExpression") + public void callTelephoneNumber(final String telNo, final boolean... directCall) { + Activity activity = greedyGetActivity(); + if (activity == null) { + throw new RuntimeException("Error: ShareUtil::callTelephoneNumber needs to be contstructed with activity context"); + } + boolean ldirectCall = (directCall != null && directCall.length > 0) ? directCall[0] : true; + + + if (android.os.Build.VERSION.SDK_INT >= 23 && ldirectCall && activity != null) { + if (ContextCompat.checkSelfPermission(activity, Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED) { + ActivityCompat.requestPermissions(activity, new String[]{Manifest.permission.CALL_PHONE}, 4001); + ldirectCall = false; + } else { + try { + Intent callIntent = new Intent(Intent.ACTION_CALL); + callIntent.setData(Uri.parse("tel:" + telNo)); + activity.startActivity(callIntent); + } catch (Exception ignored) { + ldirectCall = false; + } + } + } + // Show dialer up with telephone number pre-inserted + if (!ldirectCall) { + Intent intent = new Intent(Intent.ACTION_DIAL, Uri.fromParts("tel", telNo, null)); + activity.startActivity(intent); + } + } } diff --git a/app/src/main/res/drawable-ldpi/ic_launcher.png b/app/src/main/res/drawable-ldpi/ic_launcher.png index 8e89a6a9..43583ec9 100644 Binary files a/app/src/main/res/drawable-ldpi/ic_launcher.png and b/app/src/main/res/drawable-ldpi/ic_launcher.png differ diff --git a/app/src/main/res/drawable/ic_open_yt_external_black_24px.xml b/app/src/main/res/drawable/ic_open_yt_external_black_24px.xml new file mode 100644 index 00000000..14e620e1 --- /dev/null +++ b/app/src/main/res/drawable/ic_open_yt_external_black_24px.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/layout/browser__fragment.xml b/app/src/main/res/layout/browser__fragment.xml index 96daf8b1..9f534a0a 100644 --- a/app/src/main/res/layout/browser__fragment.xml +++ b/app/src/main/res/layout/browser__fragment.xml @@ -5,11 +5,16 @@ xmlns:tools="http://schemas.android.com/tools" tools:context="com.github.dfa.diaspora_android.activity.MainActivity"> + - + + - \ No newline at end of file + diff --git a/app/src/main/res/raw/maintainers.md b/app/src/main/res/raw/maintainers.md index 999f1073..7b1bc7f9 100644 --- a/app/src/main/res/raw/maintainers.md +++ b/app/src/main/res/raw/maintainers.md @@ -1,5 +1,5 @@ * Gregor Santner (gsantner) -~° http://gsantner.net +~° https://github.com/gsantner * Paul Schaub (vanitasvitae) ~° https://github.com/vanitasvitae diff --git a/app/src/main/res/values-af/strings.xml b/app/src/main/res/values-af/strings.xml new file mode 100644 index 00000000..4e4a8035 --- /dev/null +++ b/app/src/main/res/values-af/strings.xml @@ -0,0 +1,157 @@ + + + + Maak navigasie balk oop + Sluit navigasie balk + Herlaai + Sluit + Kanselleer + + Instellings + Kennisgewings + Gesprekke + Stroom + Profiel + Aspekte + Aktiwiteite + Hou van + Opgemerk + Aantal keer genoem + Publiek + Soek + Kontakte + Verandering-log + Statistiek + + Alle kennisgewings + Ook Opgemerk + Lewer kommentaar op hierdie plasing + Hou van + Aantal keer genoem + Herdeel + Begin deel + + Fout: Kon nie lys van pods verkry nie! + Bevestiging + Wil jy uitgaan? + + Meer + Omtrent | Hulp + Gevolgde merkers + Publieke aktiwiteite + Verslae + Deel skakel as teks + Deel vriesbeeld van webbladsy + Neem vriesbeeld van webbladsy + Stoor beeld na + Stoor vriesbeeld as: + Skakel adres gekopieer… + Nuwe Plasing + Gaan tot bo + Soek met merkers of persone + Verlaat toep + Wissel Mobiele/Monitor oogpunt + Deel… + met merkers + met mense + Voer asseblief \'n naam in + Deel skakel adres + Stoor beeld + Deel beeld + Maak in eksterne webleser oop… + Kopieer skakel adres na knipbord + Kopieer beeld adres na knipbord + + Kon nie beeld laai nie + + Toestemming geweier. + Pod naam + Protokol + Pod adres + Vermiste waarde + Versteek status balk + Wys titel + + + Voorkoms + Netwerk + Pod verstellings + + + Gebruiker + Algemeen + Admin + + Tema en kleure + Primêre kleur + Kleur van die nutsbalke + Kontras kleur + Kleur van die vorderingsbalk + AMOLED modus + + Uitgebreide Kennisgewings + Taal + + Fontgrootte + Normaal + Groot + Masief + + Laai beelde + + Skerm Rotasie + Beheer outomatiese skermrotasie + Verstek + Portret + Landskap + + Laai Tor voorkeure + Volmag + Gasheer + Poort + Toep moet herlaai om volmag te deaktiveer + Orbot volmag voorkeure gelaai + + + Persoonlike instellings + Bestuur Hutsmerke + Onvolg Hutsmerke + Verander rekening + Vee Buffer + Vee WebBuffer skoon + + Diverse + Volle herstel + Blok advertensies + Omtrent + Lisensie + Ontfouting + Toepassing + Toestel + diaspora * Pod + Tope weergawe: %1$s + Android Weergawe%1$s + Toestel naam%1$s + Kodenaam: %1$s + Pod profiel naam: %1$s + Pod domein: %1$s + Verkry die bron + Vertaal hierdie Toep! + Die toepassing is nie in jou taal beskikbaar? Jy kan dit verander! Hoekom help jy ons nie met die vertaling nie? Ons gebruik Stringlate om enigiemand te help om toepassing te vertaal. + Laat ek vertaal + Gee terugvoer! + dandelion * is nog in ontwikkeling, so as jy voorstelle of enige soort terugvoer het, gebruik asseblief ons probleem volger laat ons weet! + Raporteer foute + Deel die toep + Haai! Loer na #dandelion! %1$s + + Handhawers + %1$s<br><br>baie dankie! + GNU GPLv3+ Lisensie + Derdeparty biblioteke + Vertel my meer + Bemagtig om Youtube links oop te maak in eksterne Toeps + Youtube links + Verander die tema van jou rekening + Trek om te verfris + diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml new file mode 100644 index 00000000..d09bf9c8 --- /dev/null +++ b/app/src/main/res/values-ar/strings.xml @@ -0,0 +1,41 @@ + + + + + إعدادات + بحث + سِجل التغييرات + + + + المزيد + مشاركة + + + اخفاء شريط الاشعارات + إظهار العنوان + + + المظهر + + + عام + + + تغيير لغة التطبيق. اعد فتح التطبيق لتفعيل التغيير + اللّغة + + + + افتراضي + + + + مسح ذاكرة التخزين المؤقت + + أخرى + عن التطبيق + ترخيص + + المساهمون + diff --git a/app/src/main/res/values-az/strings.xml b/app/src/main/res/values-az/strings.xml new file mode 100644 index 00000000..43d88f4b --- /dev/null +++ b/app/src/main/res/values-az/strings.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/values-bg/strings.xml b/app/src/main/res/values-bg/strings.xml new file mode 100644 index 00000000..43d88f4b --- /dev/null +++ b/app/src/main/res/values-bg/strings.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/values-bn/strings.xml b/app/src/main/res/values-bn/strings.xml new file mode 100644 index 00000000..43d88f4b --- /dev/null +++ b/app/src/main/res/values-bn/strings.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/values-bs/strings.xml b/app/src/main/res/values-bs/strings.xml new file mode 100644 index 00000000..51109f4f --- /dev/null +++ b/app/src/main/res/values-bs/strings.xml @@ -0,0 +1,41 @@ + + + + Otvori navigacijsku ladicu + Zatvorite navigacijsku ladicu + Zatvori + Otkaži + + Podešavanja + Traži + Popis izmjena + + + + Više + + + Sakrij statusnu traku + + + Izgled + + + Opće + + + Promijeni jezik programa. Iznova pokrenite program da aktivirate promjenu + Jezik + + + + Osnovni + + + + + Razno + O programu + + Saradnici + diff --git a/app/src/main/res/values-ca/strings.xml b/app/src/main/res/values-ca/strings.xml new file mode 100644 index 00000000..3ad40c44 --- /dev/null +++ b/app/src/main/res/values-ca/strings.xml @@ -0,0 +1,209 @@ + + + + Obre el calaix de navegació + Tanca el calaix de navegació + Recarrega + Tanca + Cancel·la + + Configuració + Notificacions + Conversacions + Transmissió + Perfil + Aspectes + Activitats + M\'ha agradat + Comentat + Mencions + Públic + Cerca + Contactes + Novetats + Estadí­stiques + + Totes les notificacions + També s\'ha comentat + Comenta sobre la publicació + M\'ha agradat + Mencionat + Recompartit + S\'ha iniciat la compartició + + Error: No s\'ha pogut recuperar la llista de pods! + Cal que us connecteu a Internet per continuar + Confirmació + Voleu sortir? + + Més + Quant a | Ajuda + Etiquetes seguides + Activitats públiques + Informes + Comparteix l\'enllaç com a text + Comparteix la captura de pantalla de la pàgina web + Feu una captura de pantalla de la pàgina web + S\'està desant la imatge per a + S\'està desant la captura de pantalla com: + S\'ha copiat l\'adreça de l\'enllaç… + Nova publicació + Vés a l\'inici + Cerca per etiquetes o persones + Surt de l\'aplicació + Commuta la vista de mòbils/escriptori + Comparteix… + per etiquetes + per persones + Afegiu un nom + Comparteix l\'adreça de l\'enllaç + Desa la imatge + Comparteix la imatge + Obre en un navegador extern… + Copia l\'adreça de l\'enllaç al porta-retalls + Copia l\'adreça de la imatge al porta-retalls + + No s\'ha pogut carregar la imatge + + Cal que concediu «Accés al permís d\'emmagatzematge» per desar captures de pantalla. Després, hauríeu de tancar l\'aplicació completament o reiniciar el dispositiu. Si no concediu l\'accés a l\'emmagatzematge, però voleu utilitzar la funció de captura de pantalla més endavant, podeu concedir el permís més endavant. +Aneu a: Configuració del sistema - Aplicacions - dandelion*. A la secció de permisos podeu concedir el «permís d\'emmagatzematge d\'escriptura». + Cal que concediu «Accés al permís d\'emmagatzematge» per desar/penjar imatges. Després, hauríeu de tancar l\'aplicació completament o reiniciar el dispositiu. Si no concediu l\'accés a l\'emmagatzematge, però voleu desar imatges més endavant, podeu concedir el permís més endavant. Aneu a: Configuració del sistema - Aplicacions - dandelion*. A la secció de permisos podeu concedir el «permís d\'emmagatzematge d\'escriptura». + Permís denegat. + Permís concedit. Torneu-ho a provar. + Pod personalitzat + Nom del pod + Protocol + Adreça del pod + Falta el valor + Voleu saltar a la última pàgina visitada? + Amaga la barra d\'estat a la vista principal + Amaga la barra d\'estat + Mostra el títol a la vista principal + Mostra el títol + Drecera del llançador + + La barra d\'eines superior carrega la transmissió + Feu clic a un espai buit a la barra d\'eines superior per obrir la transmissió + + Aparença + Xarxa + Configuració del pod + Operabilitat + + + Control lliscant de navegació + Controleu la visibilitat de les entrades al calaix de navegació + Usuari + General + Administrador/a + + Tema i colors + Controleu quins colors s\'utilitzen a tota l\'aplicació + Color primari + Color de les barres d\'eines + Color d\'èmfasi + Color de la barra de progrés + Mode AMOLED + Substitueix el color amb la pantalla AMOLED compatible amb el negre en moltes parts de l\'aplicació. Cal reiniciar per canviar aquesta configuració. Per explorar diaspora* en la foscor, també heu d\'activar el tema fosc, que es pot trobar en la configuració del compte personal de diaspora*. + + Notificacions ampliades + Amplieu la campana de notificacions amb un menú desplegable que mostra les categories de notificacions + Canvieu l\'idioma d\'aquesta aplicació. Reinicieu l\'aplicació perquè els canvis tinguin efecte + Idioma + Idioma del sistema + + Controla la mida del text de WebView + Mida de la lletra + Normal + Gran + Enorme + + Carrega imatges + Commuta la càrrega d\'imatges a p. ex. estalvia dades mòbils + + Rotació de pantalla + Control automàtic de la rotació de la pantalla + Per defecte + Sensor\n(ignora la configuració del sistema) + Vertical + Horitzontal + + Carrega Tor per defecte + Carrega la configuració del servidor intermediari per a Proxy HTTP (Orbot) + Servidor intermediari + Activa el servidor intermediari + Trànsit del servidor intermediari de dandelion* evadint el tallafocs.\nPot requerir reinici. Això podria no funcionar en alguns telèfons. + Amfitrió + Port + L\'aplicació necessita reiniciar-se per desactivar l\'ús del servidor intermediari + S\'ha carregat la configuració per defecte de l\'Orbot + + Obre els enllaços externs amb les pestanyes personalitzades de Chrome. S\'ha d\'instal·lar Chromium, Firefox o Google Chrome per poder utilitzar aquesta funció. \nNOTA IMPORTANT: Les pestanyes personalitzades de Chrome no utilitzen servidors intermediaris configurats! + + Configuració personal + Obre la configuració del compte diaspora* + Gestioneu la vostra llista de contactes + Gestioneu les etiquetes + Deixa de seguir les etiquetes seguides + Canvia de compte + Esborra les dades locals de la sessió i canvia a un altre pod /compte diaspora* + Això esborrarà totes les galetes i les dades de sessió. Esteu segur que voleu canviar el vostre compte? + Esborra la memòria cau + Esborra la memòria cau de WebView + Amaga automàticament les barres d\'eines superior i inferior mentre es desplaça + Barra d\'eines «Intellihide» + Afegeix compartit per avís + Afegiu una referència a aquesta aplicació als textos compartits: [via #dandelion] + + Divers + Reinici complet + Esborra localment totes les configuracions relacionades amb l\'aplicació i tanca les sessions de tots els comptes + Això restablirà tots els paràmetres modificats de l\'aplicació als valors per defecte i es desconnectarà de tots els pods. Les vostres imatges baixades romandran intactes. Esteu segur que voleu continuar? + Activa el bloqueig d\'anuncis bàsic. Anuncis que es poden incloure p. ex. en vistes integrades + Bloqueja anuncis + Quant a + Llicècia + Depuració + Aplicació + Dispositiu + Pod de diaspora* + Registre de depuració + Registre de depuració (detallat) + Versió de l\'aplicació: %1$s + Versió d\'Android: %1$s + Nom del dispositiu: %1$s + Nom en clau: %1$s + Nom de perfil del pod: %1$s + Domini del pod: %1$s + S\'ha copiat el registre de depuració al porta-retalls + dandelion* és el seu complement per a navegar per la xarxa social diapora*. Afegeix característiques com útils barres d\'eines i suport per a servidors intermediaris com la xarxa Tor a la seva experiència social. + Contribuïu amb codi! + dandelion* es desenvolupa lliurement, en el sentit de llibertat, i segueix les idees del projecte +diaspora*. Si voleu contribuir, endavant! Actualment som un equip molt petit, de manera que us agrairíem molt qualsevol tipus d\'ajuda! + Obtén el codi font + Traduïu l\'aplicació! + L\'aplicació no està disponible en el vostre idioma? Podeu canviar això! Per què no ens ajudeu a traduir-la? Utilitzem Stringlate perquè qualsevol persona pugui traduir l\'aplicació. + Permeteu-me traduir + Valoreu-nos! + dandelion* encara està en desenvolupament, així que si teniu suggeriments o qualsevol tipus de comentari, feu servir el nostre gestor d\'errors per informar-nos-en! + Informe d\'errors + Difoneu aquest projecte + Parleu als vostres amics i familiars sobre diaspora* i #dandelion! Per què no difoneu les vostres experiències? Ens encantaria escoltar-les! + Comparteix l\'aplicació + Ep! Doneu un cop d\'ull a #dandelion! %1$s + + Mantenidors + Aquesta aplicació està sent desenvolupada i mantinguda per <br><br>%1$s + Col·laboradors + ¡%1$s < br >< br > Gràcies! + Llicència GNU GPLv3+ + Biblioteques de tercers + S\'utilitzen les següents biblioteques: + Prenem alguna inspiració i codi de LeafPic. Aneu a comprovar-ho, també és programari gratuït! + Expliqueu-me més + Activeu-ho per obrir enllaços de Youtube en aplicacions externes + Enllaços de YouTube + Canvia el tema del vostre compte + Feu lliscar per actualitzar + S\'està lliscant cap avall a la part superior de la pàgina per actualitzar.\nCal que reinicieu l\'aplicació perquè els canvis tinguin efecte. + diff --git a/app/src/main/res/values-ckb/strings.xml b/app/src/main/res/values-ckb/strings.xml new file mode 100644 index 00000000..43d88f4b --- /dev/null +++ b/app/src/main/res/values-ckb/strings.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml new file mode 100644 index 00000000..63534efb --- /dev/null +++ b/app/src/main/res/values-cs/strings.xml @@ -0,0 +1,203 @@ + + + + Otevøít navigaèní panel + Zavøít navigaèní panel + Aktualizovat + Konec + Rakovina + + Nastavení + Oznámení + Konverzace + Stream + Profil + Aspekty + Aktivity + Líbilo se + Okomentované příspěvky + Zmínky + Veřejné + Hledat + Kontakty + Seznam změn + Statistika + + Všechna oznámení + Také komentováno + Okomentoval/a Tvůj příspěvek + Líbilo se + Zmíněn + Sdílen + Začalo sdílení + + Error: Nemohl získat seznam podů! + Omlouvám se, musíte být připojeni k internetu, abyste mohli pokračovat dál + Potvrzení + Chceš opustit aplikaci? + + Více + O aplikaci | Pomoc + Zlaté rány + Veřejné aktivity + Nahlášení + Sdílejte odkaz jako text + Šablony obrázku webových stránek + Podívejte se na snímek webové stránky + Uložit obrázek + Uložit snímky jako: + Zapnutá adresa … + Nový příspěvek + Jít nahoru + Hledat štítky nebo osoby + Ukončit aplikaci + Pohled na pracovní plochu + Sdílet… + tagy + lidmi + Prosím přidejte jméno + Sdílejte adresu odkazu + Uložit obrázek + Sdílet obrázek + Otevřít v externím prohlížeči… + Zkopírovat link do schránky + Kopírovat adresu obrázku do schránky + + Nemohl být načten obrázek + + Abys mohl ukládat/nahrávat obrázky, musíš nejprve aplikaci povolit přístup k datovému úložišti. Poté by měla být aplikace uzavřena, případně restartován celý přístroj. Pokud přístupu zabráníš, ale časem se rozhodneš obrázky ukládat, můžeš toto oprávnění povolit později. Otevři: Systémové nastavení - Aplikace - dandelion*. V oblasti oprávnění poté můžeš změnit vybraná nastavení. + Oprávnění odepřeno. + Oprávnění povoleno. Prosím, zkus to znovu. + Použij definovaný Pod + Název Podu + Protokol + Adresa Podu + Chybějící hodnota + Zavolat poslední navštívenou stránku ve streamu? + Hide bar at the main view at the view. + Schovat statusbar + Zobrazit titul v hlavním pohledu + Zobrazit titul + Launcher shortcut + + Horní nástrojová lišta načítá stream + Pro otevření streamu klikni na práznou plochu v horní nástrojové liště + + Vzhled + Síť + Nastavení Podu + Obsluha + + + Navigační šprýmař + Ovládejte viditelnost položek v navigačním šuplíku + Uživatel + Obecné + Administrátor + + Téma a zmatek + Ovládání, které barvy jsou používány v průběhu aplikace + Primární barva + Barva nástrojové lišty + Akcentová barva + Barva detailů + AMOLED model + Pro procházení barvy s AMOLED zobrazujeme příjemnou černou barvu v mnoha částech aplikace. + + Rozšířené oznámení + Rozšiř oznámení zvonku pomocí výběrového menu, které zobrazuje kategorie oznámení + Pokud chcete zmìnit jazyk této aplikace, bude vyžadován její restart. + Jazyk + Jazyk systému + + Konfigurovat velikost textu WebViewu + Velikost písma + Normální + Velké + Obrovské + + Načítej obrázky + Útok obrázku na např. uložte mobilní data + + Rotace obrazovky + Kontroluj automatickou rotaci + Standardní + Senzor\n(ignorovat systémové nastavení) + Na výšku + Na šířku + + Načti přednastavení Toru + Načti proxy nastavení pro Tor (Orbot) HTTP Proxy + Proxy + Aktivovat proxy + Veď datovou cestu dandelion*, aby se obešla brána Firewall.\nMůže být vyžadován restart. Toto nemusí fungovat na všech přístrojích. + Host + Port + Při restartu musíte zakázat používání proxy serveru + načtený panel proxy + + + Osobní nastavení + Otevřte nastavení účtu diaspory + Spravte Váš seznam kontaktů + Řídit Hashtagy + Unfollow již následoval hashts + Změna konta + Erase local session data and switch to another diaspora* pod/účet + Chcete změnit svůj účet? + Vyprázdnit cache + Smazat cache WebView + Automaticky skryjí horní a dolní lišty při každém hodnocení. + Lity rozumových nástrojů + Konečný společný přístup + Přidat referenci této aplikace ke sdílenému textu: [via #dandelion] + + Různé + Celkový reset + Podařilo se vám smazat všechna nastavení související s aplikací a přihlásit se ze všech účtů. + Toto přepíše všechna změněná nastavení aplikace do výchozích hodnot a vymaže vás ze všech podů. + Uživatelné základní Adcker může být součástí např. v embedded views. + Blokové reklamy + Informace + Licence + Debugging + Aplikace + Přístroj + Pod diaspora* + Protokol odstraňování chyb + Protokol odstraňování chyb (detailně) + Verze aplikace: %1$s + Android verze: %1$s + Název přístroje: %1$s + Kódový název: %1$s + Profilový název Podu: %1$s + Doména Podu: %1$s + Protokol odstraňování chyb byl zkopírován do schránky + dandelion* je Tvoje doprovodná aplikace pro procházení sociální sítí diaspora*. Nabízí features jako užitečné nástrojové lišty nebo podporu pro proxy servery (např. Tor Network). + Spolupracuj! + dandelion* je svobodný software (free as in Freedom) a řídí se myšlenkami projektu diaspora*. Pokud chceš přispívat, jen do toho! Momentálně jsme velmi malý tým, takže jsme velmi vděční za jakýkoli druh pomoci! + Ke zdrojovému kódu + Přelož aplikaci! + Pokud si nejste jistí, že jste se rozhodli pro něco, co potřebujete? + Chci překládat + Dej zpětnou vazbu! + dandelion* je stále ve vývoji, takže pokud máš jakékoli návrhy, neváhej nám zanechat zpětnou vazbu pomocí užití našeho bug trackeru! + Nahlásit chybu + Řekni o aplikaci ostatním! + Řekni svým přátelům a rodině o diaspora* a #dadelion! Proč nezačít blogovat o Tvých zkušenostech? Rádi o Tobě uslyšíme! + Sdílejte chuť + Hey! Podívejte se na #dandelion! %1$s + + Vývojáři + Spolupracovníci + %1$s<br><br>Děkujeme! + GNU GPLv3+ Licence + Knihovny třetí strany + Následující knihovny jsou využívány: + We took some inspiration and code from LeafPic. + Řekni mi více + Umožnit otevřít Youtube odkazy na externí aplikace + Youtube odkazy + Změňte téma vašeho účtu + Pull osvěžit + diff --git a/app/src/main/res/values-da/strings.xml b/app/src/main/res/values-da/strings.xml index ead188d6..6d783039 100644 --- a/app/src/main/res/values-da/strings.xml +++ b/app/src/main/res/values-da/strings.xml @@ -27,12 +27,40 @@ Gendelte + Mere + Del… Del billede Åben i ekstern browser… Kopier link-adresse til udklipsholder - + Gem statusbar + + Øverste værktøjslinje indlæser strøm + Klik på tom plads i den øverste værktøjslinje for at åbne strømme + + Udseende + Netværk + + + Generelt + + Primær farve + Værktøjslinjers farver + Markeringsfarve + + Ændre applikationens sprog. Genstart for at ændringen træder i kraft + Sprog + + + + Standard + + + + Ryd cache + + Diverse Om Licens Fejlsøgning @@ -60,24 +88,6 @@ Rapporter fejl Spred ordet! - - - - Øverste værktøjslinje indlæser strøm - Klik på tom plads i den øverste værktøjslinje for at åbne strømme - - - - - Primær farve - Værktøjslinjers farver - Markeringsfarve - - - - - - - - + Udviklere + Bidragsydere diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index a59044d1..54bc3bbe 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -1,8 +1,11 @@ - + NavDrawer öffnen + NavDrawer schließen Aktualisieren + Schließen + Abbrechen Einstellungen Benachrichtigungen @@ -49,7 +52,7 @@ Suche nach Tags oder Personen… App beenden Mobil-/Desktopansicht umschalten - Teilen… + Teilen… nach Tags nach Personen Füge einen Namen ein. @@ -79,48 +82,9 @@ nachträglich erteilen. Öffne dafür: Systemeinstellungen - Apps - dandelion*. Zuletzt besuchte Seite im Stream aufrufen? Statusleiste in Hauptansicht verstecken Statusleiste verstecken - - Über - Lizenz - Debugging - Anwendung - Gerät - diaspora* Pod - Debug-Protokoll - Debug-Protokoll (Verbose) - App Version: %1$s - Android Version: %1$s - Gerätename: %1$s - Codename: %1$s - Pod Profil Name: %1$s - Pod Domain: %1$s - Debug-Protokoll in Zwischenablage kopiert - dandelion* ist dein Begleiter auf deinen Streifzügen durch das soziale Netzwerk diaspora*. Es bietet dir zusätzliche Funktionen wie nützliche Werkzeugleisten und Unterstützung für Proxyserver wie das Tor-Netzwerk. - Mach mit! - dandelion* wird frei wie in Freiheit entwickelt und folgt den Ideen des diaspora* Projektes. Wenn du mithelfen willst, nur zu! Wir sind momentan nur ein sehr kleines Team und sind froh über jede Unterstützung! - Zum Quellcode - Übersetze die App! - Die Anwendung ist nicht in deiner Sprache verfügbar? Das kannst du ändern! Warum hilfst du uns nicht, indem du übersetzt? Wir benutzen die Plattform crowdin, um es jedem zu ermöglichen, die App zu übersetzen. - Ich möchte übersetzen - Feedback geben! - dandelion* befindet sich noch in Entwicklung, wenn du also Vorschläge oder etwas anderes auf dem Herzen hast besuche doch unseren Bug Tracker und lass es uns wissen! - Fehler melden - Anderen davon erzählen! - Erzähl deiner Familie und deinen Freunden von diaspora* und #dandelion! Warum bloggst du nicht über deine Erfahrungen mit der App? Wir würden gerne davon erfahren! - Weitersagen - Hallo! Schau dir #dandelion an! %1$s - - Entwickler - Diese Anwendung wird momentan entwickelt und betreut von <br><br>%1$s - Mitwirkende - %1$s<br><br>Vielen Dank! - GNU GPLv3+ Lizenz - Drittanbieter-Bibliotheken - Die folgenden Bibliotheken werden genutzt: - Wir haben ein wenig bei LeafPic gespickt. Schaut euch das mal an, es handelt sich dabei auch um freie Software! - Erzähl mir mehr - - + Titel in der Hauptansicht anzeigen + Titel anzeigen + Launcher Verknüpfung Obere Werkzeugleiste lädt Stream Klicks auf leere Flächen der oberen Werkzeugleiste öffnen den Stream @@ -201,4 +165,48 @@ nachträglich erteilen. Öffne dafür: Systemeinstellungen - Apps - dandelion*. Dies wird alle geänderten Einstellungen der Anwendung auf die Standardwerte zurücksetzen und alle Konten abmelden. Deine heruntergeladenen Medien bleiben unberührt. Bist du sicher, dass du fortfahren willst? Einfachen Werbeblocker aktivieren. Werbung könnte z.B. in eingebetteten Anzeigen enthalten sein Werbung blockieren + Über + Lizenz + Debugging + Anwendung + Gerät + diaspora* Pod + Debug-Protokoll + Debug-Protokoll (Verbose) + App Version: %1$s + Android Version: %1$s + Gerätename: %1$s + Codename: %1$s + Pod Profil Name: %1$s + Pod Domain: %1$s + Debug-Protokoll in Zwischenablage kopiert + dandelion* ist dein Begleiter auf deinen Streifzügen durch das soziale Netzwerk diaspora*. Es bietet dir zusätzliche Funktionen wie nützliche Werkzeugleisten und Unterstützung für Proxyserver wie das Tor-Netzwerk. + Mach mit! + dandelion* wird frei wie in Freiheit entwickelt und folgt den Ideen des diaspora* Projektes. Wenn du mithelfen willst, nur zu! Wir sind momentan nur ein sehr kleines Team und sind froh über jede Unterstützung! + Zum Quellcode + Übersetze die App! + Die Anwendung ist nicht in deiner Sprache verfügbar? Das kannst du ändern! Warum hilfst du uns nicht, indem du übersetzt? Wir benutzen die Plattform crowdin, um es jedem zu ermöglichen, die App zu übersetzen. + Ich möchte übersetzen + Feedback geben! + dandelion* befindet sich noch in Entwicklung, wenn du also Vorschläge oder etwas anderes auf dem Herzen hast besuche doch unseren Bug Tracker und lass es uns wissen! + Fehler melden + Anderen davon erzählen! + Erzähl deiner Familie und deinen Freunden von diaspora* und #dandelion! Warum bloggst du nicht über deine Erfahrungen mit der App? Wir würden gerne davon erfahren! + Weitersagen + Hallo! Schau dir #dandelion an! %1$s + + Entwickler + Diese Anwendung wird momentan entwickelt und betreut von <br><br>%1$s + Beitragende + %1$s<br><br>Vielen Dank! + GNU GPLv3+ Lizenz + Drittanbieter-Bibliotheken + Die folgenden Bibliotheken werden genutzt: + Wir haben ein wenig bei LeafPic gespickt. Schaut euch das mal an, es handelt sich dabei auch um freie Software! + Erzähl mir mehr + Einschalten um YouTube Links in einer externen App zu öffnen + YouTube Links + Thema des Accounts ändern + Pull-To-Refresh + In der Website von ganz oben nach unten ziehen um zu aktualisieren.\nDu musst die App neu starten damit die Änderungen wirksam werden. diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml new file mode 100644 index 00000000..43d88f4b --- /dev/null +++ b/app/src/main/res/values-el/strings.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/values-eo/strings.xml b/app/src/main/res/values-eo/strings.xml new file mode 100644 index 00000000..43d88f4b --- /dev/null +++ b/app/src/main/res/values-eo/strings.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 0f782b30..603085a5 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -1,7 +1,11 @@ + Abrir el panel de navegación + Cerrar el panel de navegación Refrescar + Cerrar + Cancelar Ajustes Notificaciones @@ -48,7 +52,7 @@ Buscar por etiquetas o por personas Salir de la aplicación Alternar vista de móvil/escritorio - Compartir… + Compartir… por etiquetas por personas Por favor, añade un nombre @@ -75,48 +79,7 @@ Ocultar barra de estado Mostrar título en la vista princial Mostrar título - - Acerca de - Licencia - Depurando - Aplicación - Dispositivo - Pod de diaspora* - Depurar registro - Depurar registro (Detallado) - Versión aplicación:%1$s - Versión de Android: %1$s - Nombre del dispositivo: %1$s - Nombre clave: %1$s - Nombre de perfil del pod: %1$s - Dominio del pod:%1$s - Registro de depuración copiado al portapapeles - dandelion* es su complemento para navegar por la red social diapora*. Añade características como útiles barras de herramientas y soporte para servidores proxy como la red Tor a su experiencia social. - ¡Contribuir con código! - dandelion* se desarrolla libre, en el sentido de libertad, y sigue las ideas del proyecto diaspora*. Si usted quiere contribuir, ¡adelante! Actualmente somos un equipo muy pequeño, por lo que ¡agradeceríamos mucho cualquier tipo de ayuda! - Obtener el código fuente - ¡Traducir la aplicación! - ¿La aplicación no está disponible en su idioma? ¡Usted puede cambiar eso! ¿Por qué no nos ayuda traduciéndola? Utilizamos la plataforma crowdin para permitir a cualquiera traducir la aplicación. - Permítanme traducir - Danos tu opinión! - dandelion* todavía está en desarrollo, así que si tiene sugerencias o cualquier tipo de comentarios, ¡utilice nuestro gestor de errores para hacérnoslo saber! - Reporte de errores - ¡Corre la voz! - ¡Hable a sus amigos y familiares acerca de diaspora* y #dandelion! ¿Por qué no difunde sus experiencias? ¡Nos encantaría escucharle! - Compartir la aplicación - ¡Hey! ¡Mira #dandelion! %1$s - - Mantenimiento - Esta aplicación está siendo desarrollada y mantenida por < br >< br >%1$s - Colaboradores - ¡%1$s < br >< br > Gracias! - Licencia GNU GPLv3 + - Bibliotecas de terceros - Se utilizan las siguientes bibliotecas: - Tomamos algo de inspiración y código de LeafPic. ¡Venga, tomad prestado, es software libre también! - Saber más - - + Atajo de lanzador Barra de herramientas superior carga la portada Haga clic en un espacio vacío en la barra de herramientas superior para abrir portada @@ -144,7 +107,7 @@ Notificaciones extendidas Extender la campana de notificaciones con un menú desplegable que muestra categorías de notificación - Cambiar el idioma de esta aplicación. Reinicie la aplicación para que los cambios surtan efecto + Cambiar el idioma de esta aplicación. Reiniciar la aplicación para que se apliquen los cambios Idioma Idioma del sistema @@ -197,4 +160,48 @@ Esto restablecerá todas las modificaciones en la configuración a sus valores predeterminados y se desconectará de todos los pods. Las imágenes descargadas permanecerán intactas. ¿Está seguro de que desea continuar? Habilitar bloqueador de publicidad básico. Publicidad podrá ser incluida p.ej. en vistas incrustadas Bloquear publicidad + Acerca de + Licencia + Depurando + Aplicación + Dispositivo + Pod de diaspora* + Depurar registro + Depurar registro (Detallado) + Versión aplicación:%1$s + Versión de Android: %1$s + Nombre del dispositivo: %1$s + Nombre clave: %1$s + Nombre de perfil del pod: %1$s + Dominio del pod:%1$s + Registro de depuración copiado al portapapeles + dandelion* es su complemento para navegar por la red social diapora*. Añade características como útiles barras de herramientas y soporte para servidores proxy como la red Tor a su experiencia social. + ¡Contribuir con código! + dandelion* se desarrolla libre, en el sentido de libertad, y sigue las ideas del proyecto diaspora*. Si usted quiere contribuir, ¡adelante! Actualmente somos un equipo muy pequeño, por lo que ¡agradeceríamos mucho cualquier tipo de ayuda! + Obtener el código fuente + ¡Traducir la aplicación! + ¿La aplicación no está disponible en su idioma? ¡Usted puede cambiar eso! ¿Por qué no nos ayuda traduciéndola? Utilizamos la plataforma crowdin para permitir a cualquiera traducir la aplicación. + Permítanme traducir + Danos tu opinión! + dandelion* todavía está en desarrollo, así que si tiene sugerencias o cualquier tipo de comentarios, ¡utilice nuestro gestor de errores para hacérnoslo saber! + Reporte de errores + ¡Corre la voz! + ¡Hable a sus amigos y familiares acerca de diaspora* y #dandelion! ¿Por qué no difunde sus experiencias? ¡Nos encantaría escucharle! + Compartir la aplicación + ¡Hey! ¡Mira #dandelion! %1$s + + Mantenimiento + Esta aplicación está siendo desarrollada y mantenida por < br >< br >%1$s + Contribuidores + ¡%1$s < br >< br > Gracias! + Licencia GNU GPLv3 + + Bibliotecas de terceros + Se utilizan las siguientes bibliotecas: + Tomamos algo de inspiración y código de LeafPic. ¡Venga, tomad prestado, es software libre también! + Saber más + Habilitar para abrir enlaces de Youtube en aplicaciones externas + Enlaces de YouTube + Cambiar el tema de tu cuenta + Tirar para refrescar + Deslizar hacia abajo la parte superior de la página para refrescar.\n Necesita reiniciar la aplicación para que los cambios surtan efecto. diff --git a/app/src/main/res/values-et/strings.xml b/app/src/main/res/values-et/strings.xml new file mode 100644 index 00000000..43d88f4b --- /dev/null +++ b/app/src/main/res/values-et/strings.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/values-fa/strings.xml b/app/src/main/res/values-fa/strings.xml new file mode 100644 index 00000000..f5cf3963 --- /dev/null +++ b/app/src/main/res/values-fa/strings.xml @@ -0,0 +1,35 @@ + + + + + تنظیمات + جستجو + لاگ تغییرات + + + + بیشتر + + + مخفی کردن نوار وضعیت + + + + + عمومی + + + تغییر زبان برنامه. لازم است برنامه را از نو اجرا کنید تا تغییرات را ببینید + زبان + + + + پیش‌فرض + + + + + درباره مارکور + + همکاران + diff --git a/app/src/main/res/values-fi/strings.xml b/app/src/main/res/values-fi/strings.xml new file mode 100644 index 00000000..43d88f4b --- /dev/null +++ b/app/src/main/res/values-fi/strings.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/values-fil/strings.xml b/app/src/main/res/values-fil/strings.xml new file mode 100644 index 00000000..04cb116a --- /dev/null +++ b/app/src/main/res/values-fil/strings.xml @@ -0,0 +1,27 @@ + + + + + Mga Tanawin + Hanapin + + + + + + + + Ayos + + + + + + + + + + + + + diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 9162948b..b039f5c3 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -1,7 +1,11 @@ + Ouvrir le tiroir de navigation + Fermer le tiroir de navigation Rafraîchir + Fermer + Annuler Paramètres Notifications @@ -48,7 +52,7 @@ Recherche par tags ou par personnes Quitter l\'application Activer/désactiver la vue bureau/mobile - Partager… + Partager… par tags par personne Veuillez ajouter un nom @@ -75,51 +79,7 @@ Cacher la barre de statut Afficher le titre dans la vue principale Afficher le titre - Ouvrir le tiroir de navigation - Fermer le tiroir de navigation Raccourci de l\'écran d\'accueil - - À propos - Licence - Déboguer - Application - Appareil - Pod diaspora* - Journal de débogage - Log de débogage (Verbeux) - Version app : %1$s - Version Android : %1$s - Nom de l\'appareil : %1$s - Nom de code : %1$s - Pod alias : %1$s - Adresse du pod : %1$s - Journal de débogage copié dans le presse-papiers - dandelion* est votre application compagnon pour naviguer sur le réseau social diaspora*. Il ajoute des fonctionnalités telles que des barres d’outils utiles et la prise en charge pour les serveurs proxy comme le réseau Tor à votre expérience sociale. - Contribuez au code ! - dandelion* est libre, et suit les idées du projet diaspora*. Si vous voulez contribuer, allez-y ! Actuellement, nous sommes une toute petite équipe, donc nous apprécierions tout type d\'aide ! - Obtenir la source - Traduire l\'application ! - L’application n’est pas disponible dans votre langue ? Vous pouvez changer cela ! Pourquoi ne pas nous aider à la traduire ? Nous utilisons la plate-forme crowdin pour permettre à quiconque de traduire l’application. - Je voudrais traduire - Donnez votre avis ! - dandelion* est encore en développement, donc si vous avez des suggestions ou n’importe quel genre de retour, veuillez utiliser notre traqueur de bogues pour nous le faire savoir ! - Signaler un bug - Faîtes passer le mot ! - Parlez de diaspora* et #dandelion à vos amis et votre famille ! Pourquoi ne pas bloguer à propos de votre expérience ? Nous aimerions la lire ! - Partager cette application - Hé ! Découvrez #dandelion ! %1$s - - Mainteneurs - Cette application est actuellement développée et maintenue par <br><br>%1$s - Contributeurs - %1$s<br><br>Merci ! - Licence GNU GPLv3+ - Bibliothèques tierces - Les bibliothèques suivantes sont utilisées : - Nous avons pris des inspirations et du code de LeafPic. Allez voir, c\'est aussi un logiciel libre ! - En savoir plus - - La barre du haut charge le flux Cliquez sur un espace vide sur la barre du haut pour ouvrir le flux @@ -147,7 +107,7 @@ Notifications étendues Étendre la cloche de notifications avec un menu déroulant qui affiche les catégories de notification - Change la langue de l\'application. Redémarrez l\'application pour que les changements prennent effet + Changer la langue de l\'interface. Vous devrez redémarrer l\'application pour que ce changement soit pris en compte Langue Langue du système @@ -200,4 +160,48 @@ Ceci va réinitialiser tous les changements à leurs valeurs par défaut et vous déconnecter de tous les pods. Vos images téléchargées resteront intactes. Êtes-vous sûr de vouloir continuer ? Activer un bloqueur de pub. Des pubs peuvent être incluses, dans des vues embarquées par exemple Bloquer les publicités + À propos + Licence + Déboguer + Application + Appareil + Pod diaspora* + Journal de débogage + Log de débogage (Verbeux) + Version app : %1$s + Version Android : %1$s + Nom de l\'appareil : %1$s + Nom de code : %1$s + Pod alias : %1$s + Adresse du pod : %1$s + Journal de débogage copié dans le presse-papiers + dandelion* est votre application compagnon pour naviguer sur le réseau social diaspora*. Il ajoute des fonctionnalités telles que des barres d’outils utiles et la prise en charge pour les serveurs proxy comme le réseau Tor à votre expérience sociale. + Contribuez au code ! + dandelion* est libre, et suit les idées du projet diaspora*. Si vous voulez contribuer, allez-y ! Actuellement, nous sommes une toute petite équipe, donc nous apprécierions tout type d\'aide ! + Obtenir la source + Traduire l\'application ! + L’application n’est pas disponible dans votre langue ? Vous pouvez changer cela ! Pourquoi ne pas nous aider à la traduire ? Nous utilisons la plate-forme crowdin pour permettre à quiconque de traduire l’application. + Je voudrais traduire + Donnez votre avis ! + dandelion* est encore en développement, donc si vous avez des suggestions ou n’importe quel genre de retour, veuillez utiliser notre traqueur de bogues pour nous le faire savoir ! + Signaler un bug + Faîtes passer le mot ! + Parlez de diaspora* et #dandelion à vos amis et votre famille ! Pourquoi ne pas bloguer à propos de votre expérience ? Nous aimerions la lire ! + Partager cette application + Hé ! Découvrez #dandelion ! %1$s + + Mainteneurs + Cette application est actuellement développée et maintenue par <br><br>%1$s + Contributeurs + %1$s<br><br>Merci ! + Licence GNU GPLv3+ + Bibliothèques tierces + Les bibliothèques suivantes sont utilisées : + Nous avons pris des inspirations et du code de LeafPic. Allez voir, c\'est aussi un logiciel libre ! + En savoir plus + Autoriser l\'ouverture des liens Youtube par une appli externe + Liens Youtube + Changer le thème de votre compte + Tirer vers le bas pour mettre à jour + Tirez depuis le haut de la page vers le bas pour l\'actualiser.\nVous devez redémarrer l\'application pour que ces changements prennent effet. diff --git a/app/src/main/res/values-gl/strings.xml b/app/src/main/res/values-gl/strings.xml index 1046e470..86bd9019 100644 --- a/app/src/main/res/values-gl/strings.xml +++ b/app/src/main/res/values-gl/strings.xml @@ -1,15 +1,16 @@ - -Abrir cadro de navegación -Pechar cadro de navegación + Abrir cadro de navegación + Pechar cadro de navegación Actualizar + Pechar + Cancelar Axustes Notificacións - Conversa - Fío de comentarios + Conversas + Cronoloxía Perfil Aspecto Actividades @@ -28,10 +29,10 @@ Gústame Mencionado Compartido - Comezou a compartir + Xa compartes Problema: non se obtivo a lista de nodos! - Desculpe, precisa unha conexión a internet para esa tarefa + Desculpa, tes que ter conexión a internet para esto Confirmación Quere saír? @@ -49,12 +50,12 @@ Nova mensaxe Ir arriba Buscar persoas ou etiquetas - Saír do app + Saír da app Vista móbil/escritorio - Compartir… + Compartir… Etiquetas Persoas - Por favor, engada un nome + Por favor, engade un nome Compartir ligazón Gardar imaxe Compartir imaxe @@ -64,69 +65,27 @@ Non se cargou a imaxe - Debe permitir \"Permiso de acceso a almacenamento\" para gardar capturas. Pode - pechar a aplicación ou reiniciar o dispositivo. Si non permite acceder ao almacenamento pero que utilizar máis tarde a captura de pantalla, poderá permitir posteriormente o acceso na sección de permisos do dispositivo onde pode activar o \"permiso de acceso a almacenamento\" para dandelion*. - Ten que permitir \"Permiso de acceso a almacenamento\" para gardar/subir imaxes. Despois de iso debería - pechar a aplicación ou reiniciar o dispositivo. Se non permite acceder ao almacenamento, para poder gardar imaxes posteriormente, deberá abrir: preferencias do sistema - apps - dandelion* no dispositivo. - Na sección de permisos pode activar o \"permiso de escritura no almacenamento\". + Debes permitir \"Permiso de acceso a almacenamento\" para gardar capturas. Podes + pechar a aplicación ou reiniciar o dispositivo. Se non permites acceder ao almacenamento pero queres utilizar máis tarde a captura de pantalla, poderás permitir posteriormente o acceso na sección de permisos do dispositivo onde podes activar o \"permiso de acceso a almacenamento\" para dandelion*. + Tes que permitir \"Permiso de acceso a almacenamento\" para gardar/subir imaxes. Despois de iso deberías + pechar a aplicación ou reiniciar o dispositivo. Se non permites acceder ao almacenamento, para poder gardar imaxes posteriormente, deberás abrir: preferencias do sistema - apps - dandelion* no dispositivo. + Na sección de permisos podes activar o \"permiso de escritura no almacenamento\". Permiso denegado. - Permiso concedido. Inténteo de novo. + Permiso concedido. Inténtao de novo. Nodo personalizado Nome do nodo Protocolo Enderezo do nodo Faltan datos - Ir a última paxina lida na conversa? + Ir a última páxina lida na conversa? Agochar a barra de estado na vista principal Agochar barra de estado Mostrar título na vista principal Mostrar título - Atallo do lanzador - - Sobre - Licenza - Depurando - Aplicación - Dispositivo - Nodo diaspora* - Rexistro de depuración - Rexistro de depuración (polo miúdo) - Versión do App: %1$s - Versión Android: %1$s - Nome do dispositivo: %1$s - Alcume: %1$s - Nome do perfil do nodo: %1$s - Dominio do nodo: %1$s - Ficheiro de depuración copiado ao portapapeis - dandelion* é a súa aplicación para a rede social diaspora*. Engade características como barras de ferramentas e soporte para servidores proxy como a rede Tor para a súa experiencia social. - Contribúa ao código! - dandelion* é desenvolto libre, libre de Liberdade, e segue o espíritu que marca o proxecto diaspora*. Se quere contribuír, adiante! Por agora somos un equipo pequeno, así que agradecemos calquer tipo de axuda! - Obteña as fontes - Traduza o app! - Non está a aplicación no seu idioma? Pode cambiar eso! Por qué non nos axuda traducíndoa? Utilizamos a plataforma github para que calquera poida traducir a app. - Deixame traducir - Qué lle parece! - dandelion* aínda está en desenvolvemento, asi que si ten suxerencias de calquer tipo o valoración, por favor utilice o noso xestor de erros para facérnolo saber! - Reporte erros - Difunda! - Dígalle aos seus amigos e familiares que utiliza diaspora* e #dandelion! Por qué no escribir sobre a experiencia? Encantaríanos saber de vostede! - Comparta a aplicación - Ei!! Olla a #dandelion! %1$s - - Mantedores - Esta aplicación está a ser desenvolta e mantida por <br><br>%1$s - Contribúen - %1$s<br><br>Grazas! - Licenza GNU GPLv3+ - Código de terceiras partes - Utilízase o seguinte código: - Inspirámonos e collemos código de LeafPic. Bótalle un ollo, tamén é software libre! - Cóntame máis - - + Atallo do lanzador A barra superior carga a conversa - Pulse nun espazo baldeiro na barra superior para abrir a conversa + Preme nun espazo baleiro na barra superior para abrir a conversa Aparencia Rede @@ -135,13 +94,13 @@ Cadro de navegación - Controle a visibiidade das entradas no cadro de navegación + Controla a visibiidade das entradas no cadro de navegación Usuaria Xeral Admin Decorado e cores - Estableza qué cores se utilizan na aplicación + Escolle qué cores se utilizan na aplicación Cor primaria Cor das barras de ferramentas Cor de énfase @@ -151,7 +110,7 @@ Notificacións extendidas Extender a icona da campá de notificación con un menú desplegable que mostre a categoría das notificacións - Cambiar o idioma de esta aplicación. Reinicie para que se aplique o troco + Cambiar o idioma de esta aplicación. Reinicia para que se aplique o troco Idioma Idioma do sistema @@ -175,22 +134,22 @@ Cargar axustes proxy para Tor (Orbot) HTTP Proxy Proxy Habilitar Proxy - Proxy para o tráfico de dandelion* para saltar cortalumes.\nPodería precisar reinicio. Esto podería non funcionar en algúns móbiles. + Proxy para o tráfico de dandelion* para saltar cortalumes.\nPodería precisar reinicio. Esto podería non funcionar nalgúns móbiles. Servidor Porto - Precisa reiniciar o app para deshabilitar o uso do proxy + Precisa reiniciar a app para desactivar o uso do proxy Cargadas as preferencias do proxy Orbot Abrir ligazóns externas con Chrome Custom Tabs. Chromium ou Google Chrome ten que estar instalado para utilizar esta característica.\nIMPORTANTE: Chrome Custom Tabs non utiliza os servidores proxy configurados! Axustes personais Abrir os axustes da conta diaspora* - Xestione a súa lista de contactos + Xestiona a lista de contactos Xestionar etiquetas - Deixar de seguir etiquetas que segue + Deixar de seguir etiquetas que segues Mudar de conta Eliminar os datos locais da sesión e cambiar a outro nodo/conta de diaspora* - Esto eliminará todas as cookies e datos de sesión. Seguro que quere mudar de conta? + Esto eliminará todas as cookies e datos de sesión. Seguro que queres mudar de conta? Limpar cache Limpar a cache da VistaWeb Agochar automáticamente as barras superior e inferior mentras desplaza @@ -200,8 +159,52 @@ Varios Restablecer completamente - Eliminar todas os axustes locais do app e desconectar todas as contas - Esto restablecerá todos os axustes da aplicación ao valor por omisión e desconectarao de todos os nodos. As súas imaxes descargadas permanecerán. Seguro que quere proceder? - Habilitar un AdBlocker básico. Poderían verse anuncios por exemplo en vistas incrustadas + Eliminar todolos axustes locais da app e desconectar todas as contas + Esto restablecerá todos os axustes da aplicación ao valor por omisión e desconectarate de todolos nodos. As imaxes descargadas permanecerán. Seguro que queres proceder? + Activar un AdBlocker básico. Poderían verse anuncios por exemplo en vistas incrustadas Bloquear publicidade + Sobre + Licenza + Depurando + Aplicación + Dispositivo + Nodo diaspora* + Rexistro de depuración + Rexistro de depuración (polo miúdo) + Versión da App: %1$s + Versión Android: %1$s + Nome do dispositivo: %1$s + Alcume: %1$s + Nome do perfil do nodo: %1$s + Dominio do nodo: %1$s + Ficheiro de depuración copiado ao portapapeis + dandelion* é a túa aplicación para a rede social diaspora*. Engade características como barras de ferramentas e soporte para servidores proxy como a rede Tor. + Contribúa ao código! + dandelion* é desenvolta libre, libre de Liberdade, e segue o espíritu que marca o proxecto diaspora*. Se queres contribuír, adiante! Por agora somos un equipo pequeno, así que agradecemos calquer tipo de axuda! + Aquí as fontes + Traduce a app! + Non está a aplicación no teu idioma? Podes cambiar eso! Por qué non nos axudas traducíndoa? Utilizamos a plataforma Crowdin para que calquera poida traducir a app. + Deixame traducir + Danos a túa opinión! + dandelion* aínda está en desenvolvemento, asi que se tes suxestións de calquer tipo ou valoración, por favor usa o noso xestor de errros para facérnolo saber! + Reporte erros + Difunde! + Dille aos teus amigos e familiares que utilizas diaspora* e #dandelion! Por qué non escribir sobre a experiencia? Encantaríanos saber de ti! + Comparte a aplicación + Ei!! Olla #dandelion! %1$s + + Mantedores + Esta aplicación está a ser desenvolta e mantida por <br><br>%1$s + Contribúen + %1$s<br><br>Grazas! + Licenza GNU GPLv3+ + Código de terceiras partes + Utilízase o seguinte código: + Inspirámonos e collemos código de LeafPic. Bótalle un ollo, tamén é software libre! + Cóntame máis + Activar para abrir vídeos YouTube nunha app externa + Ligazóns YouTube + Cambiar o decorado da túa conta + Tira para actualizar + Tirar hacia abaixo na parte superior da páxina.\nDebes reiniciar a app para que os cambios se apliquen. diff --git a/app/src/main/res/values-hi/strings.xml b/app/src/main/res/values-hi/strings.xml new file mode 100644 index 00000000..07facc0c --- /dev/null +++ b/app/src/main/res/values-hi/strings.xml @@ -0,0 +1,41 @@ + + + + नेविगेशन ड्रॉवर खोलें + नेविगेशन ड्रॉवर को बंद करें + + सेटिंग्स + खोजें + बदलाव लॉग + + + + अधिक + साझा करें... + + + स्थिति पट्टी छुपाएँ + शीर्षक दिखाएं + + + दिखावट + + + + + इस ऐप की भाषा बदलें परिवर्तन प्रभावी होने के लिए ऐप को पुन: प्रारंभ करें + भाषा + + + + + + + कैश को साफ़ करें + + विविध + जानकारी + लाइसेंस + + योगदान कर्ता + diff --git a/app/src/main/res/values-hr/strings.xml b/app/src/main/res/values-hr/strings.xml new file mode 100644 index 00000000..43d88f4b --- /dev/null +++ b/app/src/main/res/values-hr/strings.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml index 47d407fe..36ef8efa 100644 --- a/app/src/main/res/values-hu/strings.xml +++ b/app/src/main/res/values-hu/strings.xml @@ -48,7 +48,7 @@ Címkék vagy emberek keresése Kilépés az alkalmazásból Mobil-/asztali mód - Megosztás… + Megosztás… címkékre emberekre Kérlek, adj meg egy nevet @@ -71,49 +71,6 @@ Pod címe Hiányzó érték Ugorjunk a legutóbb meglátogatott oldalra a hírfolyamon belül? - - Névjegy - Licensz - Hibakeresés - Alkalmazás - Eszköz - diaspora*-pod - Hibakeresési napló - Hibakeresési napló (részletes) - Alkalmazásverzió: %1$s - Android verzió: %1$s - Eszköznév: %1$s - Kódnév: %1$s - Podprofilnév: %1$s - Pod domain: %1$s - Hibakeresési napló átmásolva a vágólapra - A dandelion* a te segédalkalmazásod a diaspora* közösségi hálózat böngészésére. Olyan funkciókat ad hozzá a közösségi élményedhez, mint hasznos eszköztárak és proxyszerver-támogatás (pl. Tor). - Járulj hozzá a fejlesztéshez! - A dandelion* szabad szoftver és a diaspora* projekt elképzeléseit követi. Ha hozzá akarsz járulni, csak rajta! Jelenleg egy nagyon kis csapat vagyunk, így nagyra értékelünk bármilyen segítséget! - - A program forrása - Fordítsd le az alkalmazást! - Nem érhető el az alkalmazás a nyelveden? Ezen változtathatsz! Mit szólnál ahhoz, ha segítenél nekünk a lefordításában? A crowdin platformot használjuk, hogy bárki le tudja fordítani az alkalmazást. - Hadd fordítsak - Adj visszajelzést! - A dandelion* még mindig fejlesztés alatt áll, szóval ha vannak javaslataid vagy bármilyen visszajelzésed, kérlek, használd a hibanyomkövetőt! - Hibajelentés - Ajánld másoknak! - Beszélj a barátaiddal és családtagjaiddal a diaspora*-ról és a #dandelionról! Mi lenne, ha blogolnál a tapasztalataidról? Szeretnénk hallani tőled valamit! - Az alkalmazás megosztása - Szia! Próbáld ki a #dandeliont! %1$s - - Karbantartók - Ezt az alkalmazást jelenleg <br><br>%1$s fejleszti és tartja karban. - Hozzájárulók - %1$s<br><br>Köszönjük! - GNU GPLv3+ Licenc - 3. személytől származó könyvtárak - A következő könyvtárak vannak használatban: - A LeafPicből merítettünk némi inspirációt és programkódot. Próbáld ki, ez is szabad szoftver! - Tudj meg többet - - A fenti eszköztár betölti a hírfolyamot Kattints egy üres helyre a fenti eszköztáron a hírfolyam megnyitásához @@ -194,4 +151,43 @@ Ez az összes módosított beállítás értékét visszaállítja alapra és kijelentkeztet téged az összes podról. A letöltött képeid érintetlenül maradnak. Biztos, hogy folytatod? Alap hirdetésblokkoló (AdBlocker) engedélyezése. Reklámok jelenhetnek meg pl. a beépített megjelenítéseknél Hirdetések blokkolása + Névjegy + Licenc + Hibakeresés + Alkalmazás + Eszköz + diaspora*-pod + Hibakeresési napló + Hibakeresési napló (részletes) + Alkalmazásverzió: %1$s + Android verzió: %1$s + Eszköznév: %1$s + Kódnév: %1$s + Podprofilnév: %1$s + Pod domain: %1$s + Hibakeresési napló átmásolva a vágólapra + A dandelion* a te segédalkalmazásod a diaspora* közösségi hálózat böngészésére. Olyan funkciókat ad hozzá a közösségi élményedhez, mint hasznos eszköztárak és proxyszerver-támogatás (pl. Tor). + Járulj hozzá a fejlesztéshez! + A dandelion* szabad szoftver és a diaspora* projekt elképzeléseit követi. Ha hozzá akarsz járulni, csak rajta! Jelenleg egy nagyon kis csapat vagyunk, így nagyra értékelünk bármilyen segítséget! + A program forrása + Fordítsd le az alkalmazást! + Nem érhető el az alkalmazás a nyelveden? Ezen változtathatsz! Mit szólnál ahhoz, ha segítenél nekünk a lefordításában? A crowdin platformot használjuk, hogy bárki le tudja fordítani az alkalmazást. + Hadd fordítsak + Adj visszajelzést! + A dandelion* még mindig fejlesztés alatt áll, szóval ha vannak javaslataid vagy bármilyen visszajelzésed, kérlek, használd a hibanyomkövetőt! + Hibajelentés + Ajánld másoknak! + Beszélj a barátaiddal és családtagjaiddal a diaspora*-ról és a #dandelionról! Mi lenne, ha blogolnál a tapasztalataidról? Szeretnénk hallani tőled valamit! + Az alkalmazás megosztása + Szia! Próbáld ki a #dandeliont! %1$s + + Karbantartók + Ezt az alkalmazást jelenleg <br><br>%1$s fejleszti és tartja karban. + Hozzájárulók + %1$s<br><br>Köszönjük! + GNU GPLv3+ Licenc + 3. személytől származó könyvtárak + A következő könyvtárak vannak használatban: + A LeafPicből merítettünk némi inspirációt és programkódot. Próbáld ki, ez is szabad szoftver! + Tudj meg többet diff --git a/app/src/main/res/values-in/strings.xml b/app/src/main/res/values-in/strings.xml new file mode 100644 index 00000000..43d88f4b --- /dev/null +++ b/app/src/main/res/values-in/strings.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index d8ce0984..8271dda5 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -1,7 +1,11 @@ + Apri barra di navigazione + Chiudi barra di navigazione Ricarica + Chiudi + Annulla Impostazioni Notifiche @@ -48,7 +52,7 @@ Cerca tag o persone Esci dall\'app Attiva visualizzazione mobile/desktop - Condividi… + Condividi… per tag per persona Inserisci un nome @@ -78,51 +82,10 @@ Valore mancante Salta all\'ultima pagina visitata nello stream? Nascondi barra di stato nella schermata principale - Nascondi barra di stato + Nascondi la barra di stato Mostra titolo nella schermata principale Mostra titolo - - Informazioni - Licenza - Debug - Applicazione - Dispositivo - Pod diaspora* - Log di debug - Log di debug (dettagliato) - Versione app: %1$s - Versione Android: %1$s - Nome dispositivo: %1$s - Codename: %1$s - Pod alias: %1$s - Dominio pod: %1$s - Log di debug copiato negli appunti - dandelion* è la tua app per l\'esplorazione del social network diaspora*. Aggiunge funzionalità, quali un\'utile barra degli strumenti e il supporto per i server proxy come la rete Tor, alla vostra esperienza social. - Contribuisci al codice! - dandelion* è sviluppato liberamente e segue le idee del progetto diaspora*. Se vuoi contribuire sei il benvenuto! Attualmente siamo un team molto piccolo, quindi apprezziamo qualsiasi tipo di aiuto! - Ottieni il codice sorgente - Traduci l\'app! - L\'app non è disponibile nella tua lingua? Si può cambiare! Perché non ci aiuti a tradurlo? Utilizziamo la piattaforma Crowdin per permettere a chiunque di tradurre l\'app. - Fammi tradurre - Lascia un feedback! - dandelion* è ancora in sviluppo, quindi se avete suggerimenti o qualsiasi tipo di feedback, utilizza lo strumento di segnalazione dei bug per dirci la tua! - Segnala un bug - Passaparola! - Dì ai tuoi amici e familiari di diaspora* e di #dandelion! Perché non scrivi un post sulla tua esperienza? Ci piacerebbe sentire cosa ne pensate! - Condividi l\'app - Ehi! Scopri #dandelion! %1$s - - Manutentori - Quest\'app è attualmente sviluppata e mantenuta da <br><br>%1$s - Contributori - %1$s<br><br>Grazie! - Licenza GNU GPL versione 3 o superiore - Librerie di terze parti - Sono utilizzate le seguenti librerie: - Abbiamo preso ispirazione e parte del codice da LeafPic. Dagli un\'occhiata, anch\'esso è software libero! - Dimmi di più - - + Avvio rapido La barra superiore apre lo stream Premi su uno spazio vuoto nella barra degli strumenti superiore per aprire lo stream @@ -150,7 +113,7 @@ Notifiche estese Estendi il bottone delle notifiche con un menù che mostra le categorie di notifiche - Cambia la lingua di questa app. Riavvia l\'app per rendere effettive le modifiche + Cambia lingua dell\'app. Riavvia affinchè le modifiche abbiano effetto Lingua Lingua di sistema @@ -197,10 +160,54 @@ Aggiungi avviso dell\'app Aggiungere un riferimento a questa applicazione ai testi condivisi: [via #dandelion] - Vario + Altro Reset completo Elimina localmente tutte le impostazioni relative all\'app e disconnette da tutti gli account Questo cancellerà tutte le impostazioni dell\'app che sono state cambiate ai loro valori predefiniti e ti disconnetterà da tutti i pod. Le immagini scaricate non verranno toccate. Sei sicuro di voler procedere? Abilitare AdBlocker base. Gli annunci possono essere inclusi ad es. nelle visualizzazioni incorporate Blocca le pubblicità + Informazioni + Licenza + Debug + Applicazione + Dispositivo + Pod diaspora* + Log di debug + Log di debug (dettagliato) + Versione app: %1$s + Versione Android: %1$s + Nome dispositivo: %1$s + Codename: %1$s + Pod alias: %1$s + Dominio pod: %1$s + Log di debug copiato negli appunti + dandelion* è la tua app per l\'esplorazione del social network diaspora*. Aggiunge funzionalità, quali un\'utile barra degli strumenti e il supporto per i server proxy come la rete Tor, alla vostra esperienza social. + Contribuisci al codice! + dandelion* è sviluppato liberamente e segue le idee del progetto diaspora*. Se vuoi contribuire sei il benvenuto! Attualmente siamo un team molto piccolo, quindi apprezziamo qualsiasi tipo di aiuto! + Ottieni il codice sorgente + Traduci l\'app! + L\'app non è disponibile nella tua lingua? Si può cambiare! Perché non ci aiuti a tradurlo? Utilizziamo la piattaforma Crowdin per permettere a chiunque di tradurre l\'app. + Fammi tradurre + Lascia un feedback! + dandelion* è ancora in sviluppo, quindi se avete suggerimenti o qualsiasi tipo di feedback, utilizza lo strumento di segnalazione dei bug per dirci la tua! + Segnala un bug + Passaparola! + Dì ai tuoi amici e familiari di diaspora* e di #dandelion! Perché non scrivi un post sulla tua esperienza? Ci piacerebbe sentire cosa ne pensate! + Condividi l\'app + Ehi! Scopri #dandelion! %1$s + + Manutentori + Quest\'app è attualmente sviluppata e mantenuta da <br><br>%1$s + Collaboratori + %1$s<br><br>Grazie! + Licenza GNU GPL versione 3 o superiore + Librerie di terze parti + Sono utilizzate le seguenti librerie: + Abbiamo preso ispirazione e parte del codice da LeafPic. Dagli un\'occhiata, anch\'esso è software libero! + Dimmi di più + Abilita l\'apertura dei link Youtube su app esterna + Links Youtube + Cambia il tema del tuo account + Trascina per aggiornare + Trascina dall\'alto al basso per aggiornare la pagina.\nDevi riavviare l\'app affinchè le modifiche abbiano effetto. diff --git a/app/src/main/res/values-iw-rIL/strings.xml b/app/src/main/res/values-iw-rIL/strings.xml new file mode 100644 index 00000000..cd8d0245 --- /dev/null +++ b/app/src/main/res/values-iw-rIL/strings.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/values-iw/strings.xml b/app/src/main/res/values-iw/strings.xml new file mode 100644 index 00000000..9626eba5 --- /dev/null +++ b/app/src/main/res/values-iw/strings.xml @@ -0,0 +1,37 @@ + + + + + הגדרות + חפש + רשימת שינויים + + + + עוד + + + הסתר שורת מצב + + + מראה + + + כללי + + + שנה את שפת האפליקציה. אתחל את האפליקציה בכדי שהשינויים יכנסו לתוקף + שפה + + + + ברירת מחדל + + + + + שונות + אודות + + תורמים + diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index e6969344..5c8c52ea 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -1,7 +1,11 @@ + ナビゲーションドロワーを開く + ナビゲーションドロワーを閉じる 再読み込み + 閉じる + キャンセル 設定 通知 @@ -48,7 +52,7 @@ タグまたは人で検索 アプリを終了 モバイル/デスクトップ表示の切り替え - シェア… + シェア… タグ別 人別 名前を追加してください @@ -75,48 +79,7 @@ ステータスバーを非表示 メインビューにタイトルを表示します タイトルを表示 - - アプリについて - ライセンス - デバッグ - アプリケーション - デバイス - ダイアスポラ* ポッド - デバッグログ - デバッグ ログ (詳細) - アプリバージョン: %1$s - Android バージョン: %1$s - デバイス名: %1$s - コード名: %1$s - ポッドプロファイル名: %1$s - ポッドドメイン: %1$s - デバッグログをクリップボードにコピーしました - dandelion* は、ソーシャル ネットワーク diaspora* をブラウジングするためのコンパニオン アプリです。便利なツールバーや Tor のようなプロキシ サーバーのサポートなどの機能を追加して、ソーシャル体験することができます。 - コードを貢献! - dandelion* は自由にフリーで開発され、diaspora* プロジェクトのアイデアをフォローしています。貢献したい場合は、どうぞ! 現在私たちは、非常に小規模なチームなので、あらゆる種類の支援に感謝申し上げます! - ソースを取得 - アプリを翻訳! - アプリがお使いの言語で利用できませんか? それを変更することができます! 翻訳して私たちを支援しませんか? Crowdin プラットフォームを使用して、誰でもアプリケーションを翻訳できるようにしています。 - 翻訳させてください - フィードバックする! - dandelion* はまだ開発中なので、提案や何かフィードバックがあれば、私たちのバグ追跡システムにご連絡ください! - バグを報告 - みんなに広めよう! - diaspora* と #dandelion について家族や友人に広めてください! あなたの体験についてブログしませんか? 私たちにあなたからの話を聞かせてください! - アプリを共有 - やあ! #dandelion をチェックしてください! %1$s - - メンテナンススタッフ - 現在、このアプリは以下の人によって開発およびメンテナンスされています <br><br>%1$s - 貢献者 - %1$s<br><br>ありがとうございます! - GNU GPLv3+ ライセンス - サードパーティ ライブラリー - 以下のライブラリーが使用されます: - LeafPic からいくつかのインスピレーションとコードを得ました。チェックしてみてください。同様のフリーソフトウェアです! - さらに詳しく - - + ランチャーのショートカット 上部ツールバーでストリームを読み込み 上部ツールバーの空白部分をクリックして、ストリームを開きます。 @@ -144,7 +107,7 @@ 拡張通知 通知のカテゴリーを示すドロップダウン・メニューで通知ベルを拡張します - このアプリの言語を変更します。アプリを再起動すると変更を反映します + このアプリの言語を変更します。 変更を反映するにはアプリを再起動します 言語 システム言語 @@ -197,4 +160,48 @@ これはアプリの変更された設定をすべてデフォルトにリセットして、すべてのポッドからログアウトします。ダウンロードした画像はそのまま残ります。続行してもよろしいですか? 基本 AdBlocker を有効にします。広告は、埋め込みビューなどに含まれることがあります 広告をブロック + 情報 + ライセンス + デバッグ + アプリケーション + デバイス + ダイアスポラ* ポッド + デバッグログ + デバッグ ログ (詳細) + アプリバージョン: %1$s + Android バージョン: %1$s + デバイス名: %1$s + コード名: %1$s + ポッドプロファイル名: %1$s + ポッドドメイン: %1$s + デバッグログをクリップボードにコピーしました + dandelion* は、ソーシャル ネットワーク diaspora* をブラウジングするためのコンパニオン アプリです。便利なツールバーや Tor のようなプロキシ サーバーのサポートなどの機能を追加して、ソーシャル体験することができます。 + コードを貢献! + dandelion* は自由にフリーで開発され、diaspora* プロジェクトのアイデアをフォローしています。貢献したい場合は、どうぞ! 現在私たちは、非常に小規模なチームなので、あらゆる種類の支援に感謝申し上げます! + ソースを取得 + アプリを翻訳! + アプリがお使いの言語で利用できませんか? それを変更することができます! 翻訳して私たちを支援しませんか? Crowdin プラットフォームを使用して、誰でもアプリケーションを翻訳できるようにしています。 + 翻訳させてください + フィードバックする! + dandelion* はまだ開発中なので、提案や何かフィードバックがあれば、私たちのバグ追跡システムにご連絡ください! + バグを報告 + みんなに広めよう! + diaspora* と #dandelion について家族や友人に広めてください! あなたの体験についてブログしませんか? 私たちにあなたからの話を聞かせてください! + アプリを共有 + やあ! #dandelion をチェックしてください! %1$s + + メンテナンススタッフ + 現在、このアプリは以下の人によって開発およびメンテナンスされています <br><br>%1$s + 貢献者 + %1$s<br><br>ありがとうございます! + GNU GPLv3+ ライセンス + サードパーティ ライブラリー + 以下のライブラリーが使用されます: + LeafPic からいくつかのインスピレーションとコードを得ました。チェックしてみてください。同様のフリーソフトウェアです! + さらに詳しく + 外部アプリで Youtube のリンクを開くことができます + Youtube のリンク + アカウントのテーマを変更 + 引き下げて更新 + ページの上から下に引き下げて更新します。\n変更を反映するため、アプリを再起動する必要があります。 diff --git a/app/src/main/res/values-jw/strings.xml b/app/src/main/res/values-jw/strings.xml new file mode 100644 index 00000000..43d88f4b --- /dev/null +++ b/app/src/main/res/values-jw/strings.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/values-kab/strings.xml b/app/src/main/res/values-kab/strings.xml index a0be52a2..5d824099 100644 --- a/app/src/main/res/values-kab/strings.xml +++ b/app/src/main/res/values-kab/strings.xml @@ -1,6 +1,8 @@ + Ldi umuɣ n tunigin + Ffer umuɣ n tunigin Smiren Iɣewwaṛen @@ -48,7 +50,7 @@ Anadi s tebqzimin neɣ s imdanen Ffeɣ seg usnas Rmed/Sens timeẓri aselkim/aziraz - Bḍu… + Bḍu… s tebzimin s imdanen Rnu isem @@ -76,48 +78,9 @@ Ddu ar usebter aneggaru yettwarzan deg usuddem? Ffer afeggag n waddad di tmeẓri tagejdant Ffer afeggag n waddad - - Γef - Turagt - Tamseɣtayt - Asnas - Ibenk - diaspora* Pod - Aɣmis n temseɣtayt - Aɣmis n temseɣtayt (Verbose) - Lqem n usnas: %1$s - Lqem Android: %1$s - Isem n yibenk: %1$s - Isem n tengalt: %1$s - Isem n umaɣnu n upud: %1$s - Tansa n upud: %1$s - Aɣmis n temseqtayt yettwaneɣlen ɣef afus - dandelion* d asnas-ik amdakel i tunigin deg uẓeṭṭa n tmetti diaspora*. Ad yernu timahilin am ifecka iwulmen akked usefrek n iqeddacen ipṛuksiyen am uẓeṭṭa Tor ar tarimit-ik n tmetti. - Ttekki di tengalt! - dandelion* d ilelli, udiɣ yeṭṭafaṛ tiktiwin n useɣzan diaspora*. Ma tebɣiḍ ad tettekkiḍ, Iya-d! Akka tura, nekni d tarbaɛt meẓẓiyen, ihi ad nebɣu yal anaw n tallelt! - Awi aɣbalu - Suqel asnas! - Asnas ulac-it s tutlayt-ik? Tzemreḍ ad tbeddleḍ ayagi! Acuɣer ur tettmudduḍ ara afus di tsuqilt-is? nseqdac asmel Crowdin akken ad nermed i yal yiwen ad isuqel asnas. - Eǧǧ-iyi ad suqleɣ - Mudd-d tikti! - dandelion* yezga di tneflit, ihi ma ɣur-k isumar neɣ yal tikti, seqdec aneḍfaṛ nneɣ n ibugen akken ad tazneḍ! - Azen ibugen - Siqeḍ awal! - Meslay i yimdukal-ik akked twacult-ik ɣef diaspora* d #dandelion! Acuɣer ur tettmaeslayeḍ ara deg iblugen ɣef aya? Nebɣa ad nsel kra sɣur-k! - Bḍu asnas - Hey! Senqed #dandelion! %1$s - - Wid yettseggimen - Asnas-agi yezga di tneflit akked useggem sɣru <br><br>%1$s - Iwiziwen - %1$s<br><br>Tanemmirt! - Turagt GNU GPLv3+ - Tinedlisin tis kraḍ - Tinedlisin-agi ttwaseqdacent: - Newwi kra n tiktiwin akked tengat si LeafPic. Duu ad ten-twaliḍ. D aseɣẓan ilellil! - Ini-yid ugar - - + Sken azwel deg iẓri agejdan. + Sken azwel + Anegzum n wagdil n umager. Afeggag n ufella yessalay-d asuddem Sit ɣef tallunt tilemt n ufella akken ad d-ldiḍ asuddem @@ -145,7 +108,7 @@ Ilɣa n usiɣzef Siɣzef nnaqus n ilɣa s umuɣ n udrurem ara d-iseknen taggayin n ulɣu - Snifel tutlayt n usnas-agi. Ales tanekra n usnas akken ad tbeddel tutlayt + Beddel isem n tutlayt n ugrudem. Yessefk ad talseḍ tanekra n usnas akken teddu tutlaut tamaynut Tutlayt Tutlayt n unagraw @@ -198,4 +161,43 @@ Ayagi ad yales awennez n ibeddilen meṛṛa ar wazalen-nsen n tazwara sakin ad isuffeɣ si tuqqna akk ipuden. Tugniwin-ik yudren ad qqiment akken llant. Tebɣiḍ ad tkemmleḍ? Rmd amsewḥal n udellel. Adelle yezmer ad ddunt, m.d. deg tmeẓriyin tusliɣin Sewḥel adellel + Ɣef + Turagt + Tamseɣtayt + Asnas + Ibenk + diaspora* Pod + Aɣmis n temseɣtayt + Aɣmis n temseɣtayt (Verbose) + Lqem n usnas: %1$s + Lqem Android: %1$s + Isem n yibenk: %1$s + Isem n tengalt: %1$s + Isem n umaɣnu n upud: %1$s + Tansa n upud: %1$s + Aɣmis n temseqtayt yettwaneɣlen ɣef afus + dandelion* d asnas-ik amdakel i tunigin deg uẓeṭṭa n tmetti diaspora*. Ad yernu timahilin am ifecka iwulmen akked usefrek n iqeddacen ipṛuksiyen am uẓeṭṭa Tor ar tarimit-ik n tmetti. + Ttekki di tengalt! + dandelion* d ilelli, udiɣ yeṭṭafaṛ tiktiwin n useɣzan diaspora*. Ma tebɣiḍ ad tettekkiḍ, Iya-d! Akka tura, nekni d tarbaɛt meẓẓiyen, ihi ad nebɣu yal anaw n tallelt! + Awi aɣbalu + Suqel asnas! + Asnas ulac-it s tutlayt-ik? Tzemreḍ ad tbeddleḍ ayagi! Acuɣer ur tettmudduḍ ara afus di tsuqilt-is? nseqdac asmel Crowdin akken ad nermed i yal yiwen ad isuqel asnas. + Eǧǧ-iyi ad suqleɣ + Mudd-d tikti! + dandelion* yezga di tneflit, ihi ma ɣur-k isumar neɣ yal tikti, seqdec aneḍfaṛ nneɣ n ibugen akken ad tazneḍ! + Azen ibugen + Siqeḍ awal! + Meslay i yimdukal-ik akked twacult-ik ɣef diaspora* d #dandelion! Acuɣer ur tettmaeslayeḍ ara deg iblugen ɣef aya? Nebɣa ad nsel kra sɣur-k! + Bḍu asnas + Hey! Senqed #dandelion! %1$s + + Wid yettseggimen + Asnas-agi yezga di tneflit akked useggem sɣru <br><br>%1$s + Imttekkiyen + %1$s<br><br>Tanemmirt! + Turagt GNU GPLv3+ + Tinedlisin tis kraḍ + Tinedlisin-agi ttwaseqdacent: + Newwi kra n tiktiwin akked tengat si LeafPic. Duu ad ten-twaliḍ. D aseɣẓan ilellil! + Ini-yid ugar diff --git a/app/src/main/res/values-kmr/strings.xml b/app/src/main/res/values-kmr/strings.xml new file mode 100644 index 00000000..43d88f4b --- /dev/null +++ b/app/src/main/res/values-kmr/strings.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/values-kn/strings.xml b/app/src/main/res/values-kn/strings.xml new file mode 100644 index 00000000..2deb9a26 --- /dev/null +++ b/app/src/main/res/values-kn/strings.xml @@ -0,0 +1,40 @@ + + + + + ಮತ್ತೋಮ್ಮೆ ಲೋಡ್ ಮಾಡಿ + + ಸೆಟ್ಟಿಂಗ್‌ಗಳು + ಇಷ್ಟವಾದ + ಸಾರ್ವಜನಿಕ + ಹುಡುಕಿ + + ಇಷ್ಟವಾದ + + + ಹೊಸ ಪೊಸ್ಟ್ + ಮೇಲಕ್ಕೆ ಹೋಗಿ + ಹಂಚಿಕೊಳ್ಳಿ… + ಕೊಂಡಿಯನ್ನು ಹಂಚಿಕೊಳ್ಳಿ + ಚಿತ್ರವನ್ನು ಉಳಿಸಿ + ಚಿತ್ರವನ್ನು ಹಂಚಿಕೊಳ್ಳಿ + ಕ್ಲಿಪ್‌ಬೋರ್ಡ್‌ಗೆ ಕೊಂಡಿಯನ್ನು ನಕಲಿಸಿ + + + + + + + + + + + + + + + + ಪರವಾನಿಗೆ + Debug Log (Verbose) + + diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml new file mode 100644 index 00000000..a7989702 --- /dev/null +++ b/app/src/main/res/values-ko/strings.xml @@ -0,0 +1,196 @@ + + + + 사이드 메뉴 열기 + 사이드 메뉴 닫기 + 새로고침 + + 설정 + 알림 + 대화 + 스트림 + 프로필 + 화면비율 + 활동 + 좋아하는 + 댓글단 + 멘션 + 공개 + 검색 + 연락처 + 변경사항 + 상태 + + 모든 알림 + 다른 코멘트 + 게시물에 댓글 달기 + 좋아하는 + 멘션한 + 재공유 + 공유가 시작됨 + + 오류: 포드 목록을 검색하지 못했습니다! + 해당 작업을 실행하려면 인터넷에 연결되어 있어야 합니다. + 확인 + 종료하시겠어요? + + 더 보기 + 도움말 + 팔로우한 태그 + 공개 활동 + 리포트 + 텍스트로 링크 공유 + 웹페이지 스크린샷으로 공유 + 웝페이지 스크린샷 찍기 + 이미지 저장하기 + 다음으로 스크린샷 저장: + 링크 주소를 복사했습니다 … + 새 게시물 + 상단으로 이동 + 태그 또는 사람으로 검색 + 어플리케이션 종료 + 모바일/데스크톱 보기 전환 + 공유… + 태그로 + 사람으로 + 이름을 추가 하세요 + 링크 주소 공유 + 이미지 저장하기 + 이미지 공유 + 다른 브라우저에서 열기… + 링크 주소를 클립보드에 복사 + 이미지 주소를 클립보드에 복사 + + 이미지를 불러올 수 없습니다 + + 스크린샷을 저장하려면 파일 저장소 접근 권한이 필요합니다. 권한을 승인하신 후 + 앱을 완전히 종료하시거나 재부팅하세요. + 나중에 해당 기능을 사용하시려면 시스템 세팅 - 어플리케이션 - + dandelion* 에 들어가 저장소 쓰기 권한을 부여해주십시오. + 이미지를 업로드하거나 저장하려면 파일 저장소 접근 권한이 필요합니다. 권한을 승인하신 후 + 앱을 완전히 종료하시거나 재부팅하세요. + 나중에 해당 기능을 사용하시려면 시스템 세팅 - 어플리케이션 - + dandelion* 에 들어가 저장소 쓰기 권한을 부여해주십시오. + 권한 거부됨. + 권한이 승인되었습니다. 다시 시도해보세요. + 사용자 정의 포드 + 포드 이름 + 프로토콜 + 포드 주소 + 값이 없습니다 + 스트림의 마지막 방문 페이지로 이동합니까? + + 상단 툴바에서 스트림 읽기 + 상단 툴바의 빈 부분을 클릭해 스트림 열기 + + 모양 + 네트워크 + 포드 설정 + 운용성 + + + 탐색 슬라이더 + 탐색 서랍에서 항목의 컨트롤 표시 + 유저 + 기본 설정 + 관리자 + + 테마와 색상 + 전체적인 앱의 색상을 선택하세요 + 기본 색상 + 툴바 색상 + 강조 색상 + 진행바 색상 + + 확장된 알림 + 알림 카테고리를 나타내는 드롭 다운 메뉴에서 알림 벨을 확장합니다 + 언어 변경하기. 재시작이 필요합니다. + 언어 + 시스템 언어 + + 웹뷰 글자 크기 변경 + 폰트 사이즈 + 일반 + 크게 + 더 크게 + + 이미지 로드 여부 + 이미지 로드 여부를 변경합니다. 예) 모바일 데이터를 아끼기 위해 + + 화면 회전 + 자동 화면회전 여부를 변경합니다 + 기본값 + 센서\n(시스템 설정 무시) + 세로 + 가로 + + 토르 프리셋 로드 + 토르 (Orbot) 프록시 설정을 로드 + 프록시 + 프록시를 사용하도록 설정 + Dandelion* 트래픽이 방화벽을 우회하도록 설정합니다.\n재시작이 필요합니다. 몇몇 기기에서는 동작하지 않을 수 있습니다. + 호스트 + 포트 + 프록시 사용을 해제 하려면 다시 시작해야 합니다 + Orbot 프록시 프리셋 로드 + + + 개인 설정 + Diaspora* 계정 설정 열기 + 연락처 리스트 관리 + 해시태그 관리 + 이미 팔로우된 해시태그 언팔로우 + 계정 변경 + 로컬 세션 데이터를 지우고, 다른 diaspora* 포드/계정으로 전환 + 모든 쿠키 및 세션 데이터를 지웁니다. 계정을 변경합니까? + 캐시 삭제 + WebView 캐시 지우기 + 스크롤 하는 동안 위쪽 및 아래쪽 툴바 자동으로 숨기기 + Intellihide 툴바 + 꼬릿말 붙이기 + 어플리케이션 꼬릿말을 붙입니다: [via #dandelion] + + 기타 + 전체 리셋 + 모든 계정에서 로그아웃 하고 응용 프로그램에 관련된 모든 로컬 설정을 삭제 + 이 어플리케이션의 모든 변경 된 설정은 기본값으로 재설정 되며 모든 포드에서 로그아웃 됩니다. 다운로드 된 이미지는 그대로 있을 것입니다. 계속합니까? + 이 앱에 대해서.. + 라이선스 + 디버깅 + 어플리케이션 + 기기 + diaspora* 포드 + 디버그 로그 + 디버그 로그 (자세한 정보) + 앱 버전: %1$s + 안드로이드 버전: %1$s + 기기 이름: %1$s + 코드네임: %1$s + 포드 프로필 이름: %1$s + 포드 도메인: %1$s + 디버그 로그가 클립보드에 복사되었습니다 + dandelion* 은 소셜 네트워크인 diaspora* 의 어플리케이션입니다. 툴바, 토르 등의 프록시서버 지원 등의 유용한 기능들을 포함하고 있습니다. + 개발에 도움을 주세요! + dandelion* 은 diaspora* 의 자유로움과 아이디어를 따라 개발됩니다. 기여하고 싶으세요? 그럼 해주십시오! 저희는 소규모의 팀으로 운영되고 있으므로 모든 기여활동을 감사히 생각합니다! + 소스코드 + 앱 번역하기 + 번역하기 + 피드백 + dandelion* 은 아직 개발단계입니다. 제안사항이나 피드백이 있으시면 버그 트래커를 이용해 알려주세요! + 버그 신고 + 당신은 홍보대사 + Diaspora* 와 #dandelion 에 대해 가족들과 친구들에게 이야기해보세요. 혹은 블로그나 인터넷 커뮤니티에 글을 써보시고요. 여러분의 의견을 듣고싶습니다! + 앱 공유 + #dandelion 이거 완전 물건인데요! %1$s + + 관리자 + 현재 개발 및 관리 주체: <br><br>%1$s + 기여자 + %1$s<br><br> 감사합니다! + GNU GPLv3+ License + 서드파티 라이브러리 + 다음의 라이브러리들이 사용됬습니다: + LeafPic에서 약간의 영감을 받았으며, 코드들도 일부 차용했습니다. +한번 사용해보세요! 무료 소프트웨어 입니다! + 더 자세히 보기 + diff --git a/app/src/main/res/values-mk/strings.xml b/app/src/main/res/values-mk/strings.xml new file mode 100644 index 00000000..43d88f4b --- /dev/null +++ b/app/src/main/res/values-mk/strings.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/values-ml/strings.xml b/app/src/main/res/values-ml/strings.xml index a38bf313..2f9811b6 100644 --- a/app/src/main/res/values-ml/strings.xml +++ b/app/src/main/res/values-ml/strings.xml @@ -2,6 +2,7 @@ വീണ്ടും ലോഡ് ചെയ്യുക + റദ്ദാക്കുക ക്രമീകരണങ്ങൾ അറിയിപ്പുകൾ @@ -48,7 +49,7 @@ ടാഗുകളെയോ വ്യക്തികളെയോ തിരയുക ആപ്പിന് പുറത്തുകടക്കുക മൊബൈൽ/ഡെസ്ക്ടോപ്പ് വ്യൂവിലേക്ക് മാറുക - പങ്കുവയ്ക്കുക… + പങ്കുവയ്ക്കുക… ടാഗുകളെ ആളുകളെ പേര് ചേർക്കുക @@ -71,25 +72,6 @@ പോഡ് വിലാസം കാണാതായ മൂല്യം സ്ട്രീമിലെ അവസാനമായി സന്ദർശിച്ച പേജിലേക്ക് പോകുക? - - കുറിച്ച് - ലൈസൻസ് - തെറ്റു കണ്ടുപിടിക്കുക - ആപ്പ്ളിക്കേഷൻ - ഉപകരണം - diaspora* പോഡ് - തിരുത്തിയ തെറ്റുകളുടെ പട്ടിക - തിരുത്തിയ തെറ്റുകൾ (വേർബോസ്) - ആപ്പ് വേർഷൻ: %1$s - ആൻഡ്രോയ്ഡ് വേർഷൻ: %1$s - ഉപകരണത്തിന്റെ പേര്:%1$s - കോഡ്നെയിം: %1$s - - താഴെ പറഞ്ഞിരിക്കുന്ന ലൈബ്രറികൾ ഉപയോഗിച്ചിരിക്കുന്നു: - ഞങ്ങൾ ചില കോഡുകൾ കടമെടുത്തതും പ്രചോദനമായതും ലീഫ്പിക് ഇൽ നിന്നാണ്. ഒന്നു പരിശോധിക്കൂ.. അതും സ്വതന്ത്ര സോഫ്റ്റ്‌വേർ ആണ്! - വിശദീകരിക്കുക - - മുകളിലെ ടൂൾബാർ സ്‌ട്രീം ലോഡ് ചെയ്യുന്നു ടൂൾബാറിലെ ഒഴിഞ്ഞ സ്ഥലത്തു ക്ലിക്ക് ചെയ്തു സ്‌ട്രീം തുറക്കുക @@ -156,8 +138,30 @@ പിന്തുടർന്ന് വന്ന ഹാഷ്ടാഗുകൾ പിന്തുടരാതിരിക്കുക അക്കൗണ്ട് മാറ്റുക പ്രാദേശികമായ സെഷൻ ഡാറ്റ മായ്ച്ച ശേഷം മറ്റൊരു ഡയസ്പോറ* പോഡ്/അക്കൗണ്ട് ലേക്ക് മാറുക + ഇത് താങ്കളുടെ കുക്കികളും സെഷൻ ഡാറ്റയും തുടച്ചുനീക്കും. താങ്കൾക്ക് അക്കൗണ്ട് മാറ്റണമെന്ന് ഉറപ്പുണ്ടോ? + കാഷ് തുടച്ചുനീക്കുക + വെബ്വ്യൂ കാഷ് തുടച്ചുനീക്കുക മുകളിലെയും ചുവട്ടിലെയും ടൂൾബാറുകൾ സ്ക്രോൾ ചെയ്യുമ്പോൾ താനേ അപ്രത്യക്ഷമാക്കുക + ടൂൾബാറുകൾ ഇന്റലിഹൈഡ് ചെയ്യുക + അറിയിപ്പാൽ പങ്കുവച്ചത് എന്ന് കൂട്ടിച്ചേർക്കുക ലളിതമായ ആഡ്ബ്ലോക്കർ സജ്ജമാക്കുക. പരസ്യങ്ങൾ ചിലപ്പോൾ ഉൾപ്പെടാം. ഉദാ: എംബെഡ്ഡ് ചെയ്‌ത കാഴ്ചയിൽ പരസ്യങ്ങൾ തടയുക + കുറിച്ച് + ലൈസൻസ് + തെറ്റു കണ്ടുപിടിക്കുക + ആപ്പ്ളിക്കേഷൻ + ഉപകരണം + diaspora* പോഡ് + തിരുത്തിയ തെറ്റുകളുടെ പട്ടിക + തിരുത്തിയ തെറ്റുകൾ (വേർബോസ്) + ആപ്പ് വേർഷൻ: %1$s + ആൻഡ്രോയ്ഡ് വേർഷൻ: %1$s + ഉപകരണത്തിന്റെ പേര്:%1$s + കോഡ്നെയിം: %1$s + പോഡിന്റെ അഡ്രസ്:%1$s + + താഴെ പറഞ്ഞിരിക്കുന്ന ലൈബ്രറികൾ ഉപയോഗിച്ചിരിക്കുന്നു: + ഞങ്ങൾ ചില കോഡുകൾ കടമെടുത്തതും പ്രചോദനമായതും ലീഫ്പിക് ഇൽ നിന്നാണ്. ഒന്നു പരിശോധിക്കൂ.. അതും സ്വതന്ത്ര സോഫ്റ്റ്‌വേർ ആണ്! + വിശദീകരിക്കുക diff --git a/app/src/main/res/values-mr/strings.xml b/app/src/main/res/values-mr/strings.xml new file mode 100644 index 00000000..43d88f4b --- /dev/null +++ b/app/src/main/res/values-mr/strings.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/values-nb-rNO/strings.xml b/app/src/main/res/values-nb-rNO/strings.xml new file mode 100644 index 00000000..19084ead --- /dev/null +++ b/app/src/main/res/values-nb-rNO/strings.xml @@ -0,0 +1,40 @@ + + + + Åpne navigasjonsskuff + Lukk navigasjonsskuff + + Innstillinger + Søk + Endringslogg + + + + Mor + + + Skjul statusfelt + Vis tittel + + + Utseende + + + Hovedinnstillinger + + + Endre programmets språk. Omstart kreves før endringer trer i effekt + Språk + + + + Forvalg + + + + + Ymse + Om + + Bidragsytere + diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index dd618cc7..41ef2f93 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -1,7 +1,11 @@ + Open navigatiemenu + Sluit navigatiemenu Herladen + Sluit + Annuleren Instellingen Meldingen @@ -43,12 +47,12 @@ Afbeelding opslaan Screenshot oplaan als: Link adres gekopieerd… - Nieuw Bericht + Nieuw bericht Terug naar boven Zoeken op tags of personen - App afsluiten + Afsluiten Desktop weergave in-/uitschakelen - Delen… + Delen… viaTags via mensen Voeg een naam toe @@ -71,48 +75,11 @@ Pod adres Ontbrekende waarde Ga naar laatste bezochte pagina in de stream? - - Over - Licentie - Foutopsporing - Applicatie - Apparaat - diaspora* Pod - Foutoplossing - Foutoplossing (Verbose) - App Versie: %1$s - Android Versie: %1$s - Apparaatnaam: %1$s - Codenaam: %1$s - Pod Profielnaam: %1$s - Pod Domein: %1$s - Debuglog gekopieerd naar klembord - dandelion* is jouw bedrijfsapp om te surfen op het sociale netwerk diaspora*. Het voegt functies toe zoals nuttige werkbalken en ondersteuning voor proxyservers zoals het Tor Netwerk aan uw sociale ervaringen. - Code bijdragen! - dandelion* is vrij zoals in vrijheid ontwikkeld en volgt de ideeën van het diaspora* project. Als u iets wilt bijdragen, gaat uw gang! Op dit moment zijn we een erg klein team, dus we waarderen elke vorm van hulp! - Krijg de bron - Vertaal deze App! - De app niet beschikbaar in jouw taal? Jij kan dat veranderen! Waarom zou u niet helpen met vertalen? We gebruiken het crowdin platform zodat iedereen kan helpen met vertalen. - Laat me vertalen - Geef Feedback! - dandelion* is nog in ontwikkeling, dus heeft u suggesties of enige vorm van feedback, gebruik dan onze bug tracker om ons te laten weten! - Bugs melden - Vertel het verder! - Vertel uw vrienden en familie over diaspora* en #dandelion! Waarom blogt u niet over uw ervaringen? We willen graag uw ervaringen horen! - Deel deze app - Hey! Bekijk #dandelion! %1$s - - Onderhouders - Deze app wordt momenteel ontwikkeld en onderhouden door < br >< br >%1$s - Bijdragers - %1$s < br >< br > dank u! - GNU GPLv3 + licentie - 3rd Party Bibliotheken - De volgende bibliotheken worden gebruikt: - We zijn geïnspireerd door LeafPic en lenen er code van. Ga kijken, deze vrije software is het proberen waard! - Vertel me meer - - + Verberg statusbalk op hoofdweergave + Verberg statusbalk + Toon titel in de hoofdweergave + Toon titel + Launcher snelkoppeling Bovenste werkbalk laadt stream Klik op een lege ruimte in de bovenste werkbalk om de stream te openen @@ -193,4 +160,48 @@ Dit zal alle instellingen terugzetten en je uitloggen bij elke pod. Je gedownloade afbeeldingen blijven onaangeraakt. Weet je zeker dat je door wilt gaan? Simpele AdBlocker inschakelen. Advertenties kunnen bijvoorbeeld bij ingesloten weergaven erbij zitten Blokkeer advertenties + Over + Licentie + Foutopsporing + Applicatie + Apparaat + diaspora* Pod + Foutoplossing + Foutoplossing (Verbose) + App Versie: %1$s + Android Versie: %1$s + Apparaatnaam: %1$s + Codenaam: %1$s + Pod Profielnaam: %1$s + Pod Domein: %1$s + Debuglog gekopieerd naar klembord + dandelion* is jouw bedrijfsapp om te surfen op het sociale netwerk diaspora*. Het voegt functies toe zoals nuttige werkbalken en ondersteuning voor proxyservers zoals het Tor Netwerk aan uw sociale ervaringen. + Code bijdragen! + dandelion* is vrij zoals in vrijheid ontwikkeld en volgt de ideeën van het diaspora* project. Als u iets wilt bijdragen, gaat uw gang! Op dit moment zijn we een erg klein team, dus we waarderen elke vorm van hulp! + Krijg de bron + Vertaal deze App! + De app niet beschikbaar in jouw taal? Jij kan dat veranderen! Waarom zou u niet helpen met vertalen? We gebruiken het crowdin platform zodat iedereen kan helpen met vertalen. + Laat me vertalen + Geef Feedback! + dandelion* is nog in ontwikkeling, dus heeft u suggesties of enige vorm van feedback, gebruik dan onze bug tracker om ons te laten weten! + Bugs melden + Vertel het verder! + Vertel uw vrienden en familie over diaspora* en #dandelion! Waarom blogt u niet over uw ervaringen? We willen graag uw ervaringen horen! + Deel deze app + Hey! Bekijk #dandelion! %1$s + + Onderhouders + Deze app wordt momenteel ontwikkeld en onderhouden door < br >< br >%1$s + Bijdragers + %1$s < br >< br > dank u! + GNU GPLv3 + licentie + 3rd Party Bibliotheken + De volgende bibliotheken worden gebruikt: + We zijn geïnspireerd door LeafPic en lenen er code van. Ga kijken, deze vrije software is het proberen waard! + Vertel me meer + Inschakelen om Youtube links te openen op externe app + Youtube links + Wijzig het thema van uw account + Trek om te vernieuwen + Trek omlaag op de bovenkant van de pagina om te vernieuwen.\nU moet de app opnieuw opstarten om wijzigingen door te voeren. diff --git a/app/src/main/res/values-no-rNO/strings.xml b/app/src/main/res/values-no/strings.xml similarity index 64% rename from app/src/main/res/values-no-rNO/strings.xml rename to app/src/main/res/values-no/strings.xml index 40dd0a60..e56fc1d8 100644 --- a/app/src/main/res/values-no-rNO/strings.xml +++ b/app/src/main/res/values-no/strings.xml @@ -1,6 +1,17 @@ - + + + Innstillinger + Søk + Endringslogg + + + + Mer + Del… + + Den øvre verktøylinjen laster inn strømmen Klikk på en tom plass i den øvre verktøylinjen for å åpne strømmen @@ -25,11 +36,19 @@ Fremgangslinjefarge AMOLED-modus + Bytt språket for denne appen og omstart slik at endringen kan ta sted + Språk + Tøm hurtigminne + Forskjellig + Om + Lisens + + Medvirkende diff --git a/app/src/main/res/values-or/strings.xml b/app/src/main/res/values-or/strings.xml new file mode 100644 index 00000000..91176dc2 --- /dev/null +++ b/app/src/main/res/values-or/strings.xml @@ -0,0 +1,118 @@ + + + + ନେଭିଗେସନ୍ ଡ୍ରୟର୍ ଖୋଲନ୍ତୁ + ନେଭିଗେସନ୍ ଡ୍ରୟର୍ ବନ୍ଦ କରନ୍ତୁ + ପୁନଃଲୋଡ୍ କରନ୍ତୁ + ବନ୍ଦ କରନ୍ତୁ + ବାତିଲ୍ କର + + ସେଟିଂସମୂହ + ବିଜ୍ଞପ୍ତି + ବାର୍ତ୍ତାଳାପ + ପ୍ରୋଫାଇଲ୍ + ସନ୍ଧାନ କରନ୍ତୁ + ପରିବର୍ତ୍ତନ ଲଗ୍ + ପରିସଂଖ୍ୟାନ + + ସବୁ ବିଜ୍ଞପ୍ତି + + ନିଶ୍ଚିତକରଣ + + ଅଧିକ + ବିଷୟରେ | ସହାୟତା + ପାଠ୍ୟ ଭାବରେ ଲିଙ୍କ୍ ଅଂଶୀଦାର କରନ୍ତୁ + ୱେବପୃଷ୍ଠାର ସ୍କ୍ରିନସଟ୍ ଅଂଶୀଦାର କରନ୍ତୁ + ୱେବପୃଷ୍ଠାର ସ୍କ୍ରିନସଟ୍ ନିଅନ୍ତୁ + ଏଥିରେ ପ୍ରତିଛବି ସଞ୍ଚୟ କରୁଛି + ଲିଙ୍କ୍ ଠିକଣା କପି ହୋଇଛି … + ଶୀର୍ଷକୁ ଯାଆନ୍ତୁ + ଆପ୍ ବାହାରକୁ ଯାଆନ୍ତୁ + ମୋବାଇଲ୍/ଡେସ୍କଟପ୍ ଦର୍ଶନ ଟୋଗଲ୍ କରନ୍ତୁ + ଅଂଶୀଦାର… + ଦୟାକରି ଏକ ନାମ ଯୋଡ଼ନ୍ତୁ + ଲିଙ୍କ୍ ଠିକଣା ଅଂଶୀଦାର କରନ୍ତୁ + ପ୍ରତିଛବି ସଞ୍ଚୟ କରନ୍ତୁ + ପ୍ରତିଛବି ଅଂଶୀଦାର କରନ୍ତୁ + + ପ୍ରତିଛବି ଲୋଡ୍ କରିବାରେ ଅସମର୍ଥ + + ପ୍ରୋଟୋକଲ୍ + ମୁଖ୍ୟ ଦର୍ଶନରେ ସ୍ଥିତି ଦଣ୍ଡିକା ଲୁଚାନ୍ତୁ + ସ୍ଥିତି ଦଣ୍ଡିକା ଲୁଚାଅ + ମୁଖ୍ୟ ଦର୍ଶନରେ ଆଖ୍ୟା ଦେଖାନ୍ତୁ + ଆଖ୍ୟା ଦେଖାନ୍ତୁ + ଉନ୍ମୋଚକ ସର୍ଟକଟ୍ + + + ରୂପ + ନେଟୱର୍କ + + + ଉପଭୋକ୍ତା + ସାଧାରଣ + ବ୍ୟବସ୍ଥାପକ + + ଥିମ୍ ଏବଂ ରଙ୍ଗ + + ଏହି ଆପ୍ ର ଭାଷା ବଦଳାନ୍ତୁ। ପରିବର୍ତ୍ତନଗୁଡ଼ିକ କାର୍ଯ୍ୟକାରୀ ହେବା ପାଇଁ ଆପ୍ ପୁନଃଆରମ୍ଭ କରନ୍ତୁ + ଭାଷା + ସିଷ୍ଟମ୍ ଭାଷା + + ଫଣ୍ଟ ଆକାର + ସାଧାରଣ + ବଡ଼ + ବିରାଟ + + ପ୍ରତିଛବିଗୁଡ଼ିକୁ ଧାରଣ କରନ୍ତୁ + + ସ୍କ୍ରିନ୍ ଘୂର୍ଣ୍ଣନ + ଡିଫଲ୍ଟ + ପୋର୍ଟ୍ରେଟ୍ + ଲ୍ୟାଣ୍ଡସ୍କେପ୍ + + ପ୍ରକ୍ସି + ପ୍ରକ୍ସି ସକ୍ଷମ କରନ୍ତୁ + + + ଆକାଉଣ୍ଟ୍ ବଦଳାନ୍ତୁ + ଏହା ସମସ୍ତ କୁକୀ ଏବଂ ଅଧିବେଶନ ଡାଟା ଲିଭାଇଦେବ। ଆପଣ ପ୍ରକୃତରେ ଆପଣଙ୍କ ଆକାଉଣ୍ଟ୍ ପରିବର୍ତ୍ତନ କରିବାକୁ ଚାହାଁନ୍ତି କି? + + ଵିଵିଧ + ପୂର୍ଣ୍ଣ ପୁନଃସେଟ୍ + ମୌଳିକ AdBlocker ସକ୍ଷମ କରନ୍ତୁ। ବିଜ୍ଞାପନଗୁଡ଼ିକ ଗ୍ରଥିତ ଦର୍ଶନରେ ଅନ୍ତର୍ଭୁକ୍ତ ହୋଇପାରେ + ବିଜ୍ଞାପନଗୁଡ଼ିକୁ ଅବରୋଧ କରନ୍ତୁ + ବିଷୟରେ + ଲାଇସେନ୍ସ + ଆପ୍ଲିକେସନ୍ + ଡିଭାଇସ୍ + ଆପ୍ ସଂସ୍କରଣ: %1$s + ଆଣ୍ଡ୍ରଏଡ୍ ସଂସ୍କରଣ: %1$s + ଡିଭାଇସ୍ ନାମ: %1$s + କୋଡ୍ ନାମ: %1$s + ଉତ୍ସ ପ୍ରାପ୍ତ କରନ୍ତୁ + ଆପ୍ ଅନୁବାଦ କରନ୍ତୁ! + ଆପ୍ ଆପଣଙ୍କ ଭାଷାରେ ଉପଲବ୍ଧ ନାହିଁ କି? ଆପଣ ଏହା ବଦଳାଇପାରିବେ! ଆପଣ ଏହାକୁ ଅନୁବାଦ କରି ଆମକୁ କାହିଁକି ସାହାଯ୍ୟ କରୁନାହାଁନ୍ତି? ଆପ୍ ଅନୁବାଦ କରିବାରେ ଯେକୌଣସି ବ୍ୟକ୍ତିଙ୍କୁ ସକ୍ଷମ କରିବା ପାଇଁ ଆମେ Crowdin ବ୍ୟବହାର କରୁ। + ମୋତେ ଅନୁବାଦ କରିବାକୁ ଦିଅ + ମତାମତ ଦିଅନ୍ତୁ! + dandelion* ଏପର୍ଯ୍ୟନ୍ତ ବିକାଶରେ ଅଛି, ତେଣୁ ଯଦି ଆପଣଙ୍କର ପରାମର୍ଶ କିମ୍ୱା କୌଣସି ପ୍ରକାରର ମତାମତ ରହିଛି, ତେବେ ଦୟାକରି ଆମକୁ ଜଣାଇବା ପାଇଁ ଆମର ବଗ୍ ଟ୍ରାକର୍ ବ୍ୟବହାର କରନ୍ତୁ! + ବଗ୍‌ ରିପୋର୍ଟ୍‌ କରନ୍ତୁ + ଶବ୍ଦ ବିସ୍ତାର କରନ୍ତୁ! + diaspora* ଏବଂ #dandelion ବିଷୟରେ ଆପଣଙ୍କ ବନ୍ଧୁ ଏବଂ ପରିବାରକୁ କୁହନ୍ତୁ! ଆପଣ ଆପଣଙ୍କର ଅନୁଭୂତି ବିଷୟରେ କାହିଁକି ବ୍ଲଗ୍ କରୁନାହଁ? ଆମେ ଆପଣଙ୍କଠାରୁ ଶୁଣିବାକୁ ପସନ୍ଦ କରିବୁ! + ଆପ୍ ଅଂଶୀଦାର କରନ୍ତୁ + ଆଜ୍ଞା! #dandelion କୁ ଥରେ ଦେଖିଯାଆନ୍ତୁ! %1$s + + ରକ୍ଷଣାବେକ୍ଷଣକାରୀ + ଏହି ଆପ୍ ବର୍ତ୍ତମାନ <br><br>%1$sଙ୍କ ଦ୍ୱାରା ବିକାଶ ଓ ରକ୍ଷଣାବେକ୍ଷଣ କରାଯାଉଛି + ଯୋଗଦାନକାରୀ + %1$s<br><br>ଧନ୍ୟବାଦ! + GNU GPLv3+ ଲାଇସେନ୍ସ + ତୃତୀୟ-ପକ୍ଷ ଲାଇବ୍ରେରୀ + ନିମ୍ନଲିଖିତ ଲାଇବ୍ରେରୀଗୁଡ଼ିକ ବ୍ୟବହାର ହୋଇଛି: + ଆମେ LeafPic ରୁ କିଛି ପ୍ରେରଣା ଏବଂ କୋଡ୍ ନେଇଛୁ। ଯାଆନ୍ତୁ ଏହାକୁ ଥରେ ଦେଖି ଆସନ୍ତୁ, ଏହା ବି ଏକ ମାଗଣା ସଫ୍ଟୱେର୍! + ମୋତେ ଆହୁରି କୁହନ୍ତୁ + ବାହ୍ୟ ଆପରେ YouTube ଲିଙ୍କ୍ ଖୋଲିବା ପାଇଁ ସକ୍ଷମ କରନ୍ତୁ + YouTube ଲିଙ୍କଗୁଡ଼ିକ + ଆପଣଙ୍କ ଆକାଉଣ୍ଟର ଥିମ୍ ପରିବର୍ତ୍ତନ କରନ୍ତୁ + ସତେଜ କରିବାକୁ ଟାଣନ୍ତୁ + diff --git a/app/src/main/res/values-pa/strings.xml b/app/src/main/res/values-pa/strings.xml new file mode 100644 index 00000000..43d88f4b --- /dev/null +++ b/app/src/main/res/values-pa/strings.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index 92373216..520b2632 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -1,7 +1,11 @@ + Otwórz panel nawigacyjny + Zamknij panel nawigacyjny Przeładuj + Zamknij + Anuluj Ustawienia Powiadomienia @@ -48,7 +52,7 @@ Szukaj tagów lub osób Zamknij aplikację Przełącz na widok mobilny/standardowy - Udostępnij… + Udostępnij… w tagach w osobach Podaj nazwę użytkownika lub tagu @@ -73,48 +77,9 @@ Przejść do ostatnio odwiedzonej strony w strumieniu? Ukryj pasek stanu w głównym widoku Ukryj pasek stanu - - O aplikacji - Licencja - Debugowanie - Aplikacja - Urządzenie - Pod diaspora* - Log debugowania - Log debugowania (rozszerzony) - Wersja aplikacji: %1$s - Wersja systemu Android: %1$s - Nazwa urządzenia: %1$s - Nazwa kodowa: %1$s - Alias Poda: %1$s - Domena Poda: %1$s - Log debugowania został skopiowany do schowka - dandelion* to Twój cyfrowy towarzysz umożliwiający korzystanie z sieci społecznościowej diaspora*. Aplikacja posiada użyteczne paski narzędzi oraz obsługę serwerów proxy takich jak Tor, aby korzystanie z diaspory* było jeszcze przyjemniejsze. - Wesprzyj nas kodem! - dandelion* to wolne, w sensie wolności, oprogramowanie, zgodne z ideami projektu diaspora*. Jeśli chcesz nam pomóc w rozwoju, zapraszamy! Jesteśmy bardzo małym zespołem i ogromnie doceniamy każdą formę pomocy! - Pobierz kod źródłowy - Przetłumacz aplikację! - Aplikacja nie jest dostępna w Twoim języku? Możesz to zmienić, pomagając nam w tłumaczeniu! Dzięki platformie crowdin każdy może pomóc w tłumaczeniu aplikacji. - Chcę pomóc w tłumaczeniu - Wyślij feedback! - dandelion* jest ciągle w fazie rozwoju, więc jeśli masz jakiekolwiek sugestie lub chciałbyś podzielić się swoją opinią zrób to za pomocą naszego systemu zgłaszania błędów! - Zgłoś błąd - Powiedz innym o aplikacji! - Powiedz swoim znajomym i rodzinie o diaspora* oraz #dandelion! Dlaczego miałbyś nie opisać swoich doświadczeń na blogu? Byłoby nam miło usłyszeć, co masz do powiedzenia! - Udostępnij aplikację - Cześć! Wypróbuj #dandelion! %1$s - - Twórcy aplikacji - Rozwojem i utrzymaniem aplikacji aktualnie zajmują się <br><br>%1$s - Wnieśli wkład - %1$s<br><br>Wielkie dzięki! - Licencja GNU GPLv3+ - Biblioteki zewnętrzne - Zostały użyte następujące biblioteki zewnętrzne: - Zaczerpnęliśmy kilka pomysłów oraz trochę kodu z aplikacji LeafPic. Wypróbuj ją, to także wolne oprogramowanie! - Chcę wiedzieć więcej - - + Pokaż tytuł w widoku głównym + Pokaż tytuł + Skrót dla launchera Górny pasek narzędzi wczytuje strumień Aby otworzyć strumień, kliknij na pustą przestrzeń w górnym pasku narzędziowym @@ -142,7 +107,7 @@ Rozszerzone powiadomienia Dodaj do ikony powiadomień menu wyboru kategorii powiadomień - Zmień język aplikacji. Uruchom ponownie aplikację, aby zobaczyć zmiany + Zmienia język aplikacji. Zrestartuj żeby wprowadzić zmiany Język Język systemowy @@ -195,4 +160,47 @@ Ta operacja przywróci wszystkie zmodyfikowane przez Ciebie ustawienia aplikacji do ich domyślnych wartości i wyloguje Cię ze wszystkich Podów. Na pewno chcesz to zrobić? Włącz prosty AdBlocker. Reklamy mogą znajdować się m.in. w osadzonych widokach Blokuj reklamy + Informacje + Licencja + Debugowanie + Aplikacja + Urządzenie + Pod diaspora* + Log debugowania + Log debugowania (rozszerzony) + Wersja aplikacji: %1$s + Wersja systemu Android: %1$s + Nazwa urządzenia: %1$s + Nazwa kodowa: %1$s + Alias Poda: %1$s + Domena Poda: %1$s + Log debugowania został skopiowany do schowka + dandelion* to Twój cyfrowy towarzysz umożliwiający korzystanie z sieci społecznościowej diaspora*. Aplikacja posiada użyteczne paski narzędzi oraz obsługę serwerów proxy takich jak Tor, aby korzystanie z diaspory* było jeszcze przyjemniejsze. + Wesprzyj nas kodem! + dandelion* to wolne, w sensie wolności, oprogramowanie, zgodne z ideami projektu diaspora*. Jeśli chcesz nam pomóc w rozwoju, zapraszamy! Jesteśmy bardzo małym zespołem i ogromnie doceniamy każdą formę pomocy! + Pobierz kod źródłowy + Przetłumacz aplikację! + Aplikacja nie jest dostępna w Twoim języku? Możesz to zmienić, pomagając nam w tłumaczeniu! Dzięki platformie crowdin każdy może pomóc w tłumaczeniu aplikacji. + Chcę pomóc w tłumaczeniu + Wyślij feedback! + dandelion* jest ciągle w fazie rozwoju, więc jeśli masz jakiekolwiek sugestie lub chciałbyś podzielić się swoją opinią zrób to za pomocą naszego systemu zgłaszania błędów! + Zgłoś błąd + Powiedz innym o aplikacji! + Powiedz swoim znajomym i rodzinie o diaspora* oraz #dandelion! Dlaczego miałbyś nie opisać swoich doświadczeń na blogu? Byłoby nam miło usłyszeć, co masz do powiedzenia! + Udostępnij aplikację + Cześć! Wypróbuj #dandelion! %1$s + + Twórcy aplikacji + Rozwojem i utrzymaniem aplikacji aktualnie zajmują się <br><br>%1$s + Współautorzy + %1$s<br><br>Wielkie dzięki! + Licencja GNU GPLv3+ + Biblioteki zewnętrzne + Zostały użyte następujące biblioteki zewnętrzne: + Zaczerpnęliśmy kilka pomysłów oraz trochę kodu z aplikacji LeafPic. Wypróbuj ją, to także wolne oprogramowanie! + Chcę wiedzieć więcej + Włącz aby otwierać linki do YouTube w zewnętrznej aplikacji + Linki YouTube + Zmień motyw konta + Pociągnij aby odświeżyć diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index 83c6738f..682ddefd 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -4,11 +4,13 @@ Abra o painel de navegação Fechar painel de navegação Atualizar + Fechar + Cancelar Configurações Notificações Conversas - Stream + Transmissão Perfil Aspectos Atividades @@ -48,9 +50,9 @@ Nova postagem Ir para o topo Procurar por tags ou pessoas - Sair do aplicativo + Sair do app Alternar a exibição telefone/área de trabalho - Compartilhar… + Compartilhar… por tags por pessoas Por favor, adicione um nome @@ -72,23 +74,8 @@ Sem dado Voltar para a última página visitada no fluxo? Ocultar a barra de estado na janela principal - Ocultar barra de status - - Sobre - Licença - Depurando - Aplicação - Dispositivo - diaspora* Pod - Histórico de Depuração - Histórico de depuração (detalhado) - Versão do App: %1$s - Versão do Android: %1$s - Nome do dispositivo: %1$s - Domínio do Pod: %1$s - - - + Esconder barra de status + Mostrar título Barra de ferramentas superior abre o fluxo Clique em um espaço vazio na barra de ferramentas superior para abrir o fluxo @@ -103,7 +90,7 @@ Visibilidade do controle de entradas na gaveta de navegação Usuário Geral - Admin + Administrador Tema e cores Controlar quais cores são usadas em todo o aplicativo @@ -114,7 +101,7 @@ Modo AMOLED Notificações estendidas - Alterar o idioma deste aplicativo. Reinicie o aplicativo para as mudanças terem efeito + Muda o idioma do aplicativo. Reinicie o aplicativo para que as alterações entrem em vigor Idioma Idioma do sistema @@ -154,7 +141,10 @@ Apagar dados de sessão local e mudar para outro usuário de outro Pod diaspora* Isto apagará todos os dados de sessão e cookies. Você quer realmente mudar sua conta? Limpar cache + Limpar o cache do WebView Ocultar as barras de ferramentas superior e inferior automaticamente durante a rolagem + Barras de ferramentas auto ocultante + Acrescentar compartilhado-por-aviso Acrescentar referência a este aplicativo aos textos compartilhados: [via #dandelion] Diversos @@ -163,4 +153,25 @@ Isto irá retornar todas as configurações alteradas do aplicativo para seus valores padrão e encerrar as sessões de todos os pods. Suas imagens baixadas permanecerão intactas. Tem certeza que deseja prosseguir? Habilite AdBlocker básico. Anúncios incorporados, p.ex., podem ser vistos Bloquear anúncios + Sobre + Licença + Depurando + Aplicação + Dispositivo + diaspora* Pod + Histórico de Depuração + Histórico de depuração (detalhado) + Versão do App: %1$s + Versão do Android: %1$s + Nome do dispositivo: %1$s + Codinome: %1$s + Domínio do Pod: %1$s + + Mantenedores + Colaboradores + %1$s<br><br>Obrigado! + Licença GNU GPLv3+ + Bibliotecas de Terceiros + São usadas as seguintes bibliotecas: + Conte-me mais diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml new file mode 100644 index 00000000..a0b80c09 --- /dev/null +++ b/app/src/main/res/values-pt/strings.xml @@ -0,0 +1,205 @@ + + + + Abrir menu de navegação + Fechar menu de navegação + Recarregar + Fechar + Cancelar + + Definições + Notificações + Conversas + Fluxo + Perfil + Aspetos + Atividades + Gostou + Comentou + Menções + Público + Pesquisar + Contactos + Alterações + Estatísticas + + Todas as notificações + Também comentou + Comentar publicação + Gostou + Mencionou + Partilhou + Partilha iniciada + + Erro: não foi possível obter a lista de pods! + Tem que haver uma ligação à Internet para continuar + Confirmação + Deseja sair? + + Mais + Acerca | Ajuda + Etiquetas seguidas + Atividade pública + Relatórios + Partilhar ligação como texto + Partilhar imagem da página web + Obter imagem da página web + Guardar imagem em + Guardar imagem como: + Ligação copiada… + Nova publicação + Ir para o topo + Pesquisar por etiquetas ou por pessoas + Sair + Alternar entre vista móvel/desktop + Partilhar… + por etiquetas + por pessoa + Por favor adicione um nome + Partilhar ligação + Guardar imagem + Partilhar imagem + Abrir no navegador externo… + Copiar ligação para área de transferência + Copiar endereço da imagem para a área de transferência + + Não foi possível carregar a imagem + + Permissão recusada. + Permissão concedida. Tente novamente. + Pod personalizado + Nome do Pod + Protocolo + Endereço do Pod + Valor em falta + Ir para a última página visitada no fluxo? + Ocultar barra de estado na vista principal + Ocultar barra de estado + Mostrar título na vista principal + Mostrar título + Atalho para o lançador + + Barra de ferramentas superior carrega o fluxo + Clique num espaço vazio da barra de ferramentas superior para abrir o fluxo + + Aparência + Rede + Definições do Pod + Operacionalidade + + + Controlo de navegação + Controlar exibição das entradas no menu de navegação + Utilizador + Geral + Administrador + + Tema e cores + Controlar as cores utilizadas na aplicação + Cor principal + Cor das barras de ferramentas + Cor secundária + Cor da barra de progresso + Modo AMOLED + Substitua as cores com o AMOLED exibe preto amigável em muitas partes do aplicativo. Você precisa reiniciar para alternar esta configuração. Para navegar em diaspora* no escuro, você também precisa ativar o tema escuro, que pode ser encontrado nas configurações da sua conta pessoal diaspora*. + + Notificações expandidas + Estenda o sinal de notificações com um menu suspenso que mostra as categorias de notificação + Altera o idioma da aplicação. Tem que reiniciar a aplicação para aplicar as alterações + Idioma + Idioma do sistema + + Controlar tamanho do texto na WebView + Tamanho do tipo de letra + Normal + Grande + Enorme + + Carregar imagens + Alternar carregamento de imagens para poupar dados móveis + + Rotação do ecrã + Controlar automaticamente a rotação do ecrã + Padrão + Sensor\n(ignorar definições do sistema) + Vertical + Horizontal + + Carregar predefinição Tor + Carregar definições de proxy para Tor (Orbot) Proxy HTTP + Proxy + Ativar proxy + Proxy dandelion*\'s para contornar firewalls.\nPode ser necessário reiniciar e pode não funcionar em alguns telefones. + Servidor + Porta + Tem que reiniciar a aplicação para aplicar a alteração + Predefinição de proxy Orbot carregada + + Abra links externos com abas personalizadas do Chrome. Chromium, Firefox ou Google Chrome precisa ser instalado para usar este recurso. \nNOTA IMPORTANTE: Guias personalizadas do Chrome não usam servidores proxy configurados! + + Definições pessoais + Abra suas definições da sua conta diaspora* + Gerir lista de contactos + Gerir \'hashtags\' + Não seguir hashtags já seguidas + Mudar de conta + Apagar dados de sessão local e mudar para outro pod/conta diaspora* + Isto irá apagar todos os dados de cookies e da sessão. Tem a certeza de que deseja alterar sua conta? + Limpar cache + Limpar cache WebView + Ocultar automaticamente as barras de ferramentas superior e inferior durante a rolagem + Barra Intellihide + Anexar compartilhado-por-aviso + Acrescentar uma referência a este aplicativo a textos compartilhados: [via #dandelion] + + Diversos + Resetar tudo + Localmente limpar todas as configurações relacionadas ao aplicativo e sair de todas as contas + Isto irá redefinir todas as configurações alteradas do aplicativo para seus valores padrão e sair de todos os servidores. Suas imagens baixadas permanecerão intactas. Tem certeza que deseja continuar? + Habilitar o AdBlocker básico. Anúncios podem ser incluídos, por exemplo, em visualizações incorporadas + Bloquear anúncios + Acerca + Licença + Depuração + Aplicação + Dispositivo + diaspora* Pod + Registo de depuração + Registo de depuração (detalhado) + Versão: %1$s + Versão Android: %1$s + Nome do dispositivo: %1$s + Nome de código: %1$s + Nome do perfil Pod: %1$s + Domínio Pod: %1$s + Dados copiados para a área de transferência + dandelion* é seu aplicativo companheiro para navegar pela diaspora*. Ele adiciona recursos como barras de ferramentas úteis e suporte para servidores de proxy como a Tor Network para sua experiência social. + Contribua com código! + dandelion* é desenvolvido gratuitamente como na Liberdade e segue as ideias do projeto diaspora*. Se você quiser contribuir, vá em frente! Atualmente somos uma equipe muito pequena, então nós apreciamos muito qualquer tipo de ajuda! + Obter o código fonte + Traduzir a aplicação! + O aplicativo não está disponível em seu idioma? Você pode alterar isso! Por que você não nos ajuda traduzindo-lo? Nós usamos Stringlate para permitir que alguém traduza o aplicativo. + Quero participar + Dar Feedback! + dandelion* ainda está em desenvolvimento, então se você tiver sugestões ou algum tipo de feedback, por favor, use nosso bug tracker para nos informar! + Reportar erros + Passe a palavra! + Conte aos seus amigos e familiares sobre diaspora* e #dandelion! Por que você não blog sobre suas experiências? Nós adoraríamos ouvir de você! + Partilhar plicação + Ei! Confira #dandelion! %1$s + + Desenvolvimento + Esta aplicação está a ser desenvolvida e mantida por <br><br>%1$s + Colaboradores + %1$s<br><br>Obrigado! + Licença GNU GPLv3+ + Bibliotecas de terceiros + Utilizamos as seguintes bibliotecas: + Demos alguma inspiração e código do LeafPic. Vá conferir, é também software livre! + Saber mais + Ative para abrir as ligações YouTube na aplicação externa + Ligações YouTube + Altera o tema da sua conta + Puxar para atualizar + Deslize de cima para baixo para recarregar.\nTem que reiniciar a aplicação para aplicar as alterações. + diff --git a/app/src/main/res/values-ro/strings.xml b/app/src/main/res/values-ro/strings.xml new file mode 100644 index 00000000..b14cad56 --- /dev/null +++ b/app/src/main/res/values-ro/strings.xml @@ -0,0 +1,95 @@ + + + + Reîncarcă + + Setări + Notificări + Conversații + Stream + Profil + Aspecte + Activități + Apreciate + Comentate + Mențiuni + Public + Căutare + Contacte + Jurnalul modificărilor + Statistici + + Toate notificările + Si comentate + Comentariu pe Post + Apreciate + Menţionate + Repartajate + Început Partajare + + Eroare: Lista de Pod-uri nu poate fi preluată! + Ne pare rău, trebuie să fi conectat la Internet pentru a continua + Confirmare + Doriţi să ieşiţi? + + Extra + Despre | Ajutor + Tag-uri urmarite + Activităţi publice + Rapoarte + Partajează adresa ca text + Share screenshot a paginii web + Ia screenshot a paginii web + Imagine salvată în + Screenshot salvat ca: + Adresa copiată … + Postare nouă + Du-te la partea de sus + Căutare după Tag-uri sau persoane + Ieșire din Aplicatie + Comuta Vezi mobil/desktop + Distribuire… + după etichete + dupa persoane + Adauga nume + Partajează adresa + Salvează imaginea + Partajează imaginea + + + + + + + + + Schimbă limba acestei aplicații. Restartează aplicația pentru ca schimbările să ia efect + Limbă + + + + + + + Goliți memoria cache + + Diverse + Despre + Licenţă + Depanare + Aplicaţie + Dispozitiv + Pod diaspora* + Jurnal Depanare + Jurnal Depanare (Detaliat) + Versiune aplicație: %1$s + Versiune Android: %1$s + Nume Dispozitiv: %1$s + Nume de cod: %1$s + Nume Profil Pod: %1$s + Domeniu Pod: %1$s + Jurnal Depanare copiat în clipboard + dandelion* este companionul tau pentru navigarea reţelei sociale diaspora *. Adaugă caracteristici cum ar fi utilele bare de instrumente şi suport pentru proxy servere ca Tor Network pentru experienţa ta socială. + + Contribuitori + diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 8d81bdc5..4aa33db5 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -1,7 +1,11 @@ + Открыть панель навигации + Закрыть панель навигации Обновить + Закрыть + Отмена Настройки Уведомления @@ -42,13 +46,13 @@ Сделать скриншот страницы Сохранение изображения в Сохранение скриншота как: - Адрес ссылки скопирован… + Адрес ссылки скопирован … Новый пост В начало Искать по тегам и людям Выйти из приложения Переключить мобильный вид - Поделиться… + Поделиться… по тегам по людям Пожалуйста, добавьте имя @@ -74,49 +78,8 @@ Скрыть строку состояния в главном окне Скрыть строку состояния Показывать заголовок на главном экране - Показывать заголовок - - О приложении - Лицензия - Отладка - Приложение - Устройство - Под diaspora* - Журнал отладки - Журнал отладки (подробный) - Версия приложения: %1$s - Версия Android: %1$s - Имя устройства: %1$s - Кодовое имя: %1$s - Имя профиля пода: %1$s - Домен пода: %1$s - Журнал отладки скопирован в буфер обмена - dandelion* - это приложение-компаньон для социальной сети Диаспора*. Приложение добавляет такие функции как полезные панели инструментов и поддержку прокси-серверов (таких как Tor). - Сделайте свой вклад! - dandelion* разрабатывается как свободное ПО и следует идеям проекта Диаспора*. Если вы хотите помочь - вперёд! В настоящее время мы является очень небольшой командой, поэтому мы высоко ценим любые виды помощи! - Получить исходный код - Переведите приложение! - Приложение недоступно на вашем языке? Вы можете это изменить! Почему бы вам не помочь нам перевести его? Мы используем платформу crowdin, чтобы позволить любому сделать это. - Перейти к переводу - Оставьте отзыв! - dandelion* всё ещё находится в разработке, так что, если у вас есть предложения или пожелания, пожалуйста, воспользуйтесь нашим трекером ошибок, чтобы сообщить нам об этом! - Сообщить об ошибках - Расскажите друзьям! - Расскажите вашим друзьям и семье о Диаспоре* и #dandelion! Почему бы вам не разместить запись о вашем опыте в блоге? Мы будем рады услышать об этом! - Рассказать о приложении - Эй! Зацените #dandelion! %1$s - - Мейнтейнеры - Это приложение в настоящее время разрабатывается и поддерживается следующими людьми: <br><br>%1$s - Участники - %1$s<br><br>Спасибо вам! - Лицензия GNU GPLv3+ - Сторонние библиотеки - Используются следующие библиотеки: - Мы вдохновлялись и взяли немного кода из LeafPic. Попробуйте это приложение, оно тоже является свободным ПО! - Расскажите мне больше - - + Показать заголовок + Ярлык лаунчера Верхняя панель инструментов загружает Поток Нажмите на пустое пространство на верхней панели инструментов, чтобы открыть Поток @@ -197,4 +160,48 @@ Это сбросит все изменённые параметры приложения на значения по умолчанию и выйдет из аккаунтов на всех подах. Ваши загруженные изображения останутся нетронутыми. Вы уверены, что хотите продолжить? Включить простой блокировщик рекламы. Реклама может быть во встроенных элементах страницы Блокировать рекламу + О программе + Лицензия + Отладка + Приложение + Устройство + Под diaspora* + Журнал отладки + Журнал отладки (подробный) + Версия приложения: %1$s + Версия Android: %1$s + Имя устройства: %1$s + Кодовое имя: %1$s + Имя профиля пода: %1$s + Домен пода: %1$s + Журнал отладки скопирован в буфер обмена + dandelion* - это приложение-компаньон для социальной сети Диаспора*. Приложение добавляет такие функции как полезные панели инструментов и поддержку прокси-серверов (таких как Tor). + Сделайте свой вклад! + dandelion* разрабатывается как свободное ПО и следует идеям проекта Диаспора*. Если вы хотите помочь - вперёд! В настоящее время мы является очень небольшой командой, поэтому мы высоко ценим любые виды помощи! + Получить исходный код + Переведите приложение! + Приложение недоступно на вашем языке? Вы можете это изменить! Почему бы вам не помочь нам перевести его? Мы используем платформу crowdin, чтобы позволить любому сделать это. + Перейти к переводу + Оставьте отзыв! + dandelion* всё ещё находится в разработке, так что, если у вас есть предложения или пожелания, пожалуйста, воспользуйтесь нашим трекером ошибок, чтобы сообщить нам об этом! + Сообщить об ошибках + Расскажите друзьям! + Расскажите вашим друзьям и семье о Диаспоре* и #dandelion! Почему бы вам не разместить запись о вашем опыте в блоге? Мы будем рады услышать об этом! + Рассказать о приложении + Эй! Зацените #dandelion! %1$s + + Мейнтейнеры + Это приложение в настоящее время разрабатывается и поддерживается следующими людьми: <br><br>%1$s + Поддержавшие проект + %1$s<br><br>Спасибо вам! + Лицензия GNU GPLv3+ + Сторонние библиотеки + Используются следующие библиотеки: + Мы вдохновлялись и взяли немного кода из LeafPic. Попробуйте это приложение, оно тоже является свободным ПО! + Расскажите мне больше + Открывать ссылки на Youtube во внешних приложениях + Ссылки на Youtube + Изменить тему вашей учётной записи + Потяните для обновления + Потяните вниз, чтобы обновить страницу.\nВам нужно перезапустить приложение, чтобы изменения вступили в силу. diff --git a/app/src/main/res/values-sc/strings.xml b/app/src/main/res/values-sc/strings.xml index 52cbce59..d16b663e 100644 --- a/app/src/main/res/values-sc/strings.xml +++ b/app/src/main/res/values-sc/strings.xml @@ -4,7 +4,8 @@ Aberi su pannellu de nàvigu Serra su pannellu de nàvigu Torra a carrigare - + Serra + Annulla Impostatziones Notìficas @@ -21,7 +22,6 @@ Cuntatos Registru de sas modìficas Istatìsticas - Totu sas notìficas Àteros cummentos @@ -30,54 +30,40 @@ Numenada Torrada a cumparzire At incumintzadu a cumpartzire - - Errore: Impossìbile recuperare sa lista de sos pods! - Depes èssere connessu a ìnternet pro sighire Cunfirma Cheres essire? - - Àteru Informatziones |Agiudu Etichetas sighidas Atividades pùblicas Sinnalatziones - Cumpartzi unu ligàmene comente testu Cumpartzi s\'ischermada de sa pàgina web Faghe un\'ischermada de sa pàgina web Sarbende s\'immàgine in Sarbende s\'ischermada in: Ligàmene copiadu… - - Publicatzione noa Torra a s\'incumintzu Chirca etichetas o persones Essi dae s\'aplicatzione Allughe/istuda sa versione mòbile Cumpartzi… - - pro eticheta pro persone Pro praghere annanghe unu nùmene - Cumpartzi ligàmene Sarva s\'immàgine Cumpartzi s\'immàgine Aberi in un\'esploradore (browser) esternu… Còpia su ligàmenes in sos apuntos Còpia s\'indiritzu de s\'immàgine in sos apuntos - - Impossìbile carrigare s\'immàgine - Depes frunire \"Permissu de atzessu a sa memòria\" pro sarvare sas ischermadas. A pustis dias dèpere serrare de su totu s\'aplicatzione o torrare a allùghere su dispositivu. Si no as a frunire su permissu a sa memòria ma cheres impreare sa funtzionalidade de ischermada, prus a tardu, l\'as a pòdere fàghere tando. Pro praghere aberi: systemsettings - apps - dandelion*. In sa setzione pro sos permissosas a pòdere frunire su \"permissu de atzessu a sa memòria\". Depes frunire \"Permissu de atzessu a sa memòria\" pro sarvare e carrigare sas immàgines. A pustis dias dèpere serrare de su totu s\'aplicatzione o torrare a allùghere su dispositivu. Si no as a frunire su permissu a sa memòria ma cheres impreare sa funtzionalidade de ischermada, prus a tardu, l\'as a pòdere fàghere tando. Pro praghere aberi: systemsettings - apps - dandelion*. In sa setzione pro sos permissosas a pòdere frunire su \"permissu de atzessu a sa memòria\". @@ -94,8 +80,89 @@ Ammustra su tìtulu in sa vista printzipale Ammustra tìtulu Incurtzada de s\'allughidore - - + + Sa barra superiore aberit su flussu + Incarca in unu tretu bòidu in sa barra superiore de sas ainas pro abèrrere su flussu + + Aparèntzia + Retza + Impostatziones de su pod + Operabilidade + + + Menù de nàvigu + Controlla sa visibilidade de sos elementos in su menù de nàvigu + Impreadore + Generales + Amministratzione + + Temas e colores + Controlla cales colores sunt impreados in s\'aplicatzione + Colore primàriu + Colore de sas barras de sas ainas + Colore secondàriu + Colore de sa barra de progressu + Modalidade AMOLED + Remplasa sos colores cun su nigheddu pro ischermos AMOLED in medas tretos de s\'aplicatzione. As a dèpere torrare a allùghere s\'aplicatzione pro ativare custa impostatzione. Pro navigare in diaspora* cun su tema iscuru lu depes fintzas allùghere dae sas optziones personales de su contu de diaspora* tuo. + + Notìficas ismanniadas + Ismànnia su butone de sas notìficas cun unu menù chi ammustrat sas categorias de sas notìficas + Càmbia sa limba de custa aplicatzione. Torra·la a allùghere pro fàghere tènnere efetu a su cambiamentu + Limba + Limba de sistema + + Controlla sa mannària de su testu de sa vista web + Mannària de su testu + Normale + Mannu + Mannu meda + + Iscàrriga sas immàgines + Allughe/istuda su carrigamentu de sas immàgines pro risparmiare sos datos mòbiles + + Rotatzione de s\'ischermu + Controlla sa rotatzione automàtica de s\'ischermu + Predefinidu + Sensore\n + (ignora sas impostatziones de sistema) + Verticale + Orizontale + + Càrriga sos valores predefinidos de Tor + Càrriga sas impostatziones pro su sèrver intermèdiu HTTP de Tor (Orbot) + Server intermèdiu (Proxy) + Abìlita unu server intermèdiu + Faghe colare su tràficu de dandelion* in unu server intermèdiu pro isfrancare sos firewalls.\n + Diat pòdere èssere netzessàriu a torrare a allùghere s\'aplicatzione. Diat pòdere non funtzionare pro carchi telèfono. + Host + Ghenna + Depes torrare a allùghere s\'aplicatzione pro istudare s\'impreu de su server intermediàriu + Impostatziones de su server de Orbot carrigadas + + Aberi sos ligàmenes esternos cun sas ischedas personalizadas de Chrome. Chromium o Google Chrome depent èssere installados pro lu pòdere fàghere. \n + NOTA DE IMPORTU: sas ischedas personalizadas de Chrome no impreant sos servers intermediàrios configurados! + + Impostatziones personales + Aberi sas impostatziones de su contu de diaspora* tuo + Amministra sa lista de sos contatos tuos + Amministra sas etichetas + Acaba·la de sighire sas etichetas sighidas + Càmbia contu + Iscantzella sos datos de sa sessione locale e cola a un\'àteru contu o pod de diaspora* + Custu at a iscantzellare totu sos cookies e sos datos de sa sessione. Seguru ses de chèrrere cambiare su contu tuo? + Iscantzella sa cache + Iscantzella sa cache de sa vista web + Istichi automaticamente sas barras superiores e inferiores cando ses iscurrende sa pàgina + Barras chi s\'istichint intelligentemente + Annanghe un\'avisu de s\'aplicatzione + Annanghe unu riferimentu a custa aplicatzione a sos testos cumpartzidos: [via #dandelion] + + Vàriu + Reimpostatzione intrea + Iscantzella localmente totu sos datos relativos a s\'aplicatzione e essi dae totu sos contos + Custu at a fàghere torrare totu sas impostatziones de s\'aplicatzione a sos valores predefinidos e t\'at a fàghere essire dae totu sos pods. Sas immàgines iscarrigadas dae tie no ant a èssere tocadas. Seguru ses de chèrrere sighire? + Abìlita su blocadore de publitzidade de base. Sa publitzidade diat pòdere èssere incluida, pro esèmpiu, in sas visualizatziones incorporadas + Bloca sa publitzidade Informatziones Litzèntzia Depuratzione dae sos errores @@ -111,27 +178,20 @@ Nùmene de profilu de su pod: %1$s Domìniu de su pod: %1$s Registru de depuratzione copiadu in sos apuntos - dandelion* est s\'aplicatzione tua pro s\'esploratzione de sa retza sotziale diaspora*. Annanghet funtzionalidades comente a barras de ainas utilosas e a su suportu pro server intermediàrios (proxies) comente a sa retza Tor pro s\'esperièntzia sotziale tua. - Contribui a su còdighe! dandelion* est iscvilupada liberamente e sighit sas idea de su progetu diaspora*. Si cheres contribuire, faghe·lu! In custu momentu semus unu grupu minore meda, nos diat agradare a tènnere cale si siat casta de agiudu! Otenne su còdighe mitza - Tradui s\'aplicatzione! S\'aplicatzione no est disponìbile in sa limba tua? Lu podes cambiare! Proite non nos agiudas traduinde·la? Nois impreamus sa prataforma crowdin pro permìtere a totus de traduire s\'aplicatzione. Faghe·mi traduire - Lassa·nos s\'opinione tua! dandelion* est galu in isvilupu. Si tenes impòsitos o cale si siat casta de cosa da sinnalare, pro praghere, faghe·la·nos ischire impreende s\'aina de sinnalatziones de errores nostra! Sinnala errores - Cumpartzi sa paràula! Faghe connòschere diaspora* e #dandelion a sos amigos e a sa familia tua! Proite no iscries carchi cosa a pitzu de sas esperièntzias tuas? Noas diat agradare meda a ischire ite nde pensas! Cumpartzi s\'aplicatzione Ei! Dae un\'ograda a #dandelion! %1$s - - Manutentores Custa aplicatzione est beninde isvilupada e manutentada dae <br><br>%1$s @@ -142,113 +202,9 @@ Sunt impreadas custas librerias: Amus pigadu ispiratzione e parte de su còdighe dae LeafPic. Abbistade·bos·lu, est fintzas cussu unu programma lìberu! Àteras informatziones - - - - - Sa barra superiore aberit su flussu - Incarca in unu tretu bòidu in sa barra superiore de sas ainas pro abèrrere su flussu - - - - Aparèntzia - Retza - Impostatziones de su pod - Operabilidade - - - - Menù de nàvigu - Controlla sa visibilidade de sos elementos in su menù de nàvigu - Impreadore - Generales - Amministratzione - - - Temas e colores - Controlla cales colores sunt impreados in s\'aplicatzione - Colore primàriu - Colore de sas barras de sas ainas - Colore secondàriu - Colore de sa barra de progressu - Modalidade AMOLED - Remplasa sos colores cun su nigheddu pro ischermos AMOLED in medas tretos de s\'aplicatzione. As a dèpere torrare a allùghere s\'aplicatzione pro ativare custa impostatzione. Pro navigare in diaspora* cun su tema iscuru lu depes fintzas allùghere dae sas optziones personales de su contu de diaspora* tuo. - - - Notìficas ismanniadas - Ismànnia su butone de sas notìficas cun unu menù chi ammustrat sas categorias de sas notìficas - - Càmbia sa limba de custa aplicatzione. Torra·la a allùghere pro fàghere tènnere efetu a su cambiamentu - Limba - Limba de sistema - - - Controlla sa mannària de su testu de sa vista web - Mannària de su testu - Normale - Mannu - Mannu meda - - - Iscàrriga sas immàgines - Allughe/istuda su carrigamentu de sas immàgines pro risparmiare sos datos mòbiles - - - Rotatzione de s\'ischermu - Controlla sa rotatzione automàtica de s\'ischermu - Predefinidu - Sensore\n - (ignora sas impostatziones de sistema) - Verticale - Orizontale - - - Càrriga sos valores predefinidos de Tor - Càrriga sas impostatziones pro su sèrver intermèdiu HTTP de Tor (Orbot) - Server intermèdiu (Proxy) - Abìlita unu server intermèdiu - Faghe colare su tràficu de dandelion* in unu server intermèdiu pro isfrancare sos firewalls.\n - Diat pòdere èssere netzessàriu a torrare a allùghere s\'aplicatzione. Diat pòdere non funtzionare pro carchi telèfono. - Host - Ghenna - Depes torrare a allùghere s\'aplicatzione pro istudare s\'impreu de su server intermediàriu - Impostatziones de su server de Orbot carrigadas - - - - Aberi sos ligàmenes esternos cun sas ischedas personalizadas de Chrome. Chromium o Google Chrome depent èssere installados pro lu pòdere fàghere. \n - NOTA DE IMPORTU: sas ischedas personalizadas de Chrome no impreant sos servers intermediàrios configurados! - - - Impostatziones personales - Aberi sas impostatziones de su contu de diaspora* tuo - Amministra sa lista de sos contatos tuos - - Amministra sas etichetas - Acaba·la de sighire sas etichetas sighidas - - Càmbia contu - Iscantzella sos datos de sa sessione locale e cola a un\'àteru contu o pod de diaspora* - Custu at a iscantzellare totu sos cookies e sos datos de sa sessione. Seguru ses de chèrrere cambiare su contu tuo? - - Iscantzella sa cache - Iscantzella sa cache de sa vista web - - - Istichi automaticamente sas barras superiores e inferiores cando ses iscurrende sa pàgina - Barras chi s\'istichint intelligentemente - - Annanghe un\'avisu de s\'aplicatzione - Annanghe unu riferimentu a custa aplicatzione a sos testos cumpartzidos: [via #dandelion] - - - Vàriu - - Reimpostatzione intrea - Iscantzella localmente totu sos datos relativos a s\'aplicatzione e essi dae totu sos contos - - Custu at a fàghere torrare totu sas impostatziones de s\'aplicatzione a sos valores predefinidos e t\'at a fàghere essire dae totu sos pods. Sas immàgines iscarrigadas dae tie no ant a èssere tocadas. Seguru ses de chèrrere sighire? - - Abìlita su blocadore de publitzidade de base. Sa publitzidade diat pòdere èssere incluida, pro esèmpiu, in sas visualizatziones incorporadas - Bloca sa publitzidade + Abìlita pro abèrrere sos ligàmenes de Youtube in un\'aplicatzione esterna + Ligàmenes de Youtube + Muda su tema de su contu tuo + Tira cara a bassu pro annoare + Tira cara a bassu pro annoare sa pàgina.\nDepes torrare a allùghere s\'aplicatzione pro fàghere in modu chi sas modìficas tèngiant efetu. diff --git a/app/src/main/res/values-si/strings.xml b/app/src/main/res/values-si/strings.xml new file mode 100644 index 00000000..45159998 --- /dev/null +++ b/app/src/main/res/values-si/strings.xml @@ -0,0 +1,81 @@ + + + + නැවත පූරණය + වසන්න + අවලංගු + + සැකසුම් + දැනුම්දීම් + සංවාද + පැතිකඩ + ක්‍රියාකාරකම් + අදහස් දැක්විණි + සැඳහුම් + ප්‍රසිද්ධ + සොයන්න + සබඳතා + සංඛ්‍යාලේඛන + + සියළුම දැනුම්දීම් + එවගේම අදහස් දැක්විණි + + ඔබට පිටවීමට ඇවැසිද? + + තව + පිළිබඳව | උපකාර + ප්‍රසිද්ධ ක්‍රියාකාරකම් + වාර්තා + වියමන පිටුවෙහි තිරසේයාවක් බෙදාගන්න + වියමන පිටුවෙහි තිර සේයාවක් අරගන්න + ලෙස තිරසේයාව සුරකින්න: + සබැඳියේ ලිපිනය පිටපත් විය… + යෙදුමෙන් පිටවන්න + බෙදාගන්න… + නමක් එකතු කරන්න + සබැඳියේ ලිපිනය බෙදාගන්න + බාහිර අතිරික්සුවකින් විවෘත කරන්න… + සබැඳිය පසුරුපුවරුවට පිටපත් කරන්න + + + කෙටුම්පත + සිරැසිය පෙන්වන්න + + + ජාලය + + + පරිශීලක + පරිපාලක + + තේමාව සහ වර්ණ + + භාෂාව + පද්ධතියේ භාෂාව + + මුද්‍රණඅකුරේ ප්‍රමාණය + සාමාන්‍ය + + + පෙරනිමි + + පෙරකලාසිය + පෙරකලාසිය සබල කරන්න + + + ගිණුම වෙනස් කරන්න + + පිලිබඳව + බලපත්‍රය + යෙදුම + උපාංගය + ඇන්ඩ්‍රොයිඩ් අනුවාදය: %1$s + උපාංගයේ නම: %1$s + කේතනාමය: %1$s + මූලාශ්‍රය ගන්න + යෙදුම පරිවර්තනය කරන්න! + යෙදුම බෙදාගන්න + + ජීඑන්යූ ජීපීඑල්v3+ බලපත්‍රය + යූටියුබ් සබැඳිය + diff --git a/app/src/main/res/values-sk/strings.xml b/app/src/main/res/values-sk/strings.xml new file mode 100644 index 00000000..43d88f4b --- /dev/null +++ b/app/src/main/res/values-sk/strings.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/values-sq/strings.xml b/app/src/main/res/values-sq/strings.xml new file mode 100644 index 00000000..43d88f4b --- /dev/null +++ b/app/src/main/res/values-sq/strings.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/values-sr-rRS/strings.xml b/app/src/main/res/values-sr-rRS/strings.xml new file mode 100644 index 00000000..422fd22e --- /dev/null +++ b/app/src/main/res/values-sr-rRS/strings.xml @@ -0,0 +1,38 @@ + + + + Otvori navigacioni panel + Yatvori navigacioni panel + + Podesavanja + Lista promena + + + + + + Sakrij status + Prikaži naziv + + + Izgled + + + + + Promijeni jezik ove aplikacije. Restartuj apikaciju da bi podešavanja dobila efekat. + Jezik + + + + + + + + Ostalo + O ovom + + Pomo\'nici + Prikazi GNU GPLv3 licence + Prikaz licence trecih lica + diff --git a/app/src/main/res/values-sr/strings.xml b/app/src/main/res/values-sr/strings.xml new file mode 100644 index 00000000..543f0196 --- /dev/null +++ b/app/src/main/res/values-sr/strings.xml @@ -0,0 +1,40 @@ + + + + + Поставке + тражи + Дневник измена + + + + још + Подели… + + + Sakrij status + + + Izgled + Мрежа + + + + + Промените језик апликације. Поново покрените након тога + Језик + + + + + + + Обриши кеш + + разно + О програму + Лиценца + + Програмери + Сарадници + diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml index 79576339..7c42e701 100644 --- a/app/src/main/res/values-sv/strings.xml +++ b/app/src/main/res/values-sv/strings.xml @@ -1,7 +1,11 @@ + Öppna navigeringslådan + Stäng navigeringslådan Ladda om + Avsluta + Avbryt Inställningar Notiser @@ -42,20 +46,20 @@ Ta skärmdump av en webbsida Sparar bild som Sparar skärmdump som: - Länkadress kopierad… + Länkadressen kopierades ... Nytt inlägg Till toppen Sök på taggar eller personer Avsluta appen Växla mellan telefon- och skrivbordsvy - Dela… + Dela… efter taggar efter människor Var god ange namn Dela länkadress Spara bild Dela bild - Öppna i en extern webbläsare… + Öppna i en extern webbläsare... Kopiera länkadress Kopiera bildadressen @@ -77,48 +81,11 @@ Podadress Värde saknas Vill du hoppa senast besökta sida i strömmen? - - Om - Licens - Avlusning - Program - Enhet - diaspora*-pod - Avlusningslogg - Avlusningslogg (detaljerad) - Programversion: %1$s - Androidversion: %1$s - Enhetens namn: %1$s - Kodnamn: %1$s - Podprofilens namn: %1$s - Poddomän: %1$s - Texten kopierad till urklippen - dandelion* är en kompletterande app till att använda det sociala nätverket diaspora*. Den lägger till funktioner såsom användbara verktyg and stöd för proxyservrar såsom Tor. - Bidrag med kod! - dandelion* är utvecklat för att vara fritt som i frihet och lyder under samma filosofi som diaspora*-projektet. Om du vill bidra, är det fritt fram! I dagsläget är vi få, så uppskattar öppenhjärtigt all hjälp! - Hämta källkoden - Översätt appen! - Är appen inte tillgänglig på ditt mål? Det kan du ändra på! Hjälp oss översätta! Vi använder oss av plattformen Crowdin så att alla kan hjälpa till att översätta appen. - Jag vill översätta - Lämna respons! - dandelion* utvecklas fortfarande, så har du några förslag eller annan återkoppling, använd vårt buggrapporteringssystem! - Rapportera buggar - Berätta om oss! - Berätta för dina närmaste om diaspora* och #dandelion! Blogga om dina erfarenheter! Vi vill gärna veta vad du tycker! - Dela appen - Hallå! Kolla in #dandelion! %1$s - - Utvecklare - Denna app utvecklas för närvarande och underhålls av < br >< br >%1$s - Bidragsgivare - %1$s < br >< br > Tack så hjärtligt! - GNU GPLv3+ - Tredjepartsbibliotek - Följande bibliotek används: - Vi hämtade inspiration och kod från LeafPic. Kika på det, det är också fri programvara! - Berätta mer - - + Dölj statusfältet vid huvudvyn + Dölj statusfältet + Göm titeln i huvudvyn + Visa titel + Programstartargenväg Den övre verktygspanelen laddar stöm Tryck på en tom yta i övre verktygsfältet för att öppna strömmen @@ -199,4 +166,48 @@ Detta kommer att återställa alla ändrade inställningarna på appen till deras standardvärden och loggar ut dig från alla poddar. Dina nedladdade bilder förblir orörda. Är du säker på att du vill fortsätta? Använd lätt reklamblockering. Reklam finns bland annat i inbyggda vyer Blockera reklam + Om + Licens + Avlusning + Program + Enhet + diaspora*-pod + Avlusningslogg + Avlusningslogg (detaljerad) + Programversion: %1$s + Androidversion: %1$s + Enhetens namn: %1$s + Kodnamn: %1$s + Podprofilens namn: %1$s + Poddomän: %1$s + Texten kopierad till urklippen + dandelion* är en kompletterande app till att använda det sociala nätverket diaspora*. Den lägger till funktioner såsom användbara verktyg and stöd för proxyservrar såsom Tor. + Bidrag med kod! + dandelion* är utvecklat för att vara fritt som i frihet och lyder under samma filosofi som diaspora*-projektet. Om du vill bidra, är det fritt fram! I dagsläget är vi få, så uppskattar öppenhjärtigt all hjälp! + Hämta källkoden + Översätt appen! + Är appen inte tillgänglig på ditt mål? Det kan du ändra på! Hjälp oss översätta! Vi använder oss av plattformen Crowdin så att alla kan hjälpa till att översätta appen. + Jag vill översätta + Lämna respons! + dandelion* utvecklas fortfarande, så har du några förslag eller annan återkoppling, använd vårt buggrapporteringssystem! + Rapportera buggar + Berätta om oss! + Berätta för dina närmaste om diaspora* och #dandelion! Blogga om dina erfarenheter! Vi vill gärna veta vad du tycker! + Dela appen + Hallå! Kolla in #dandelion! %1$s + + Utvecklare + Denna app utvecklas för närvarande och underhålls av < br >< br >%1$s + Bidragsgivare + %1$s < br >< br > Tack så hjärtligt! + GNU GPLv3+ + Tredjepartsbibliotek + Följande bibliotek används: + Vi hämtade inspiration och kod från LeafPic. Kika på det, det är också fri programvara! + Berätta mer + Aktivera för att öppna Youtube-länkar i en extern app + Youtube-länkar + Ändra temat för ditt konto + Dra för att uppdatera + Dra ner på toppen av sidan för att uppdatera.\nDu måste starta om appen för att ändringarna ska träda i kraft. diff --git a/app/src/main/res/values-ta/strings.xml b/app/src/main/res/values-ta/strings.xml new file mode 100644 index 00000000..43d88f4b --- /dev/null +++ b/app/src/main/res/values-ta/strings.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/values-te/strings.xml b/app/src/main/res/values-te/strings.xml new file mode 100644 index 00000000..43d88f4b --- /dev/null +++ b/app/src/main/res/values-te/strings.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/values-th/strings.xml b/app/src/main/res/values-th/strings.xml new file mode 100644 index 00000000..43d88f4b --- /dev/null +++ b/app/src/main/res/values-th/strings.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index e38e96a9..6c40a2c3 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -1,7 +1,11 @@ + Gezinme çekmecesini aç + Gezinme çekmecesini kapat Yeniden Yükle + Kapat + İptal Ayarlar Bildirimler @@ -14,7 +18,7 @@ Yorumlananlar Bahsetmeler Herkes - Arama + Ara Kişiler Değişme Günlüğü İstatistikler @@ -23,46 +27,59 @@ Ayrıca Yorumlar Gönderine Yorumlar Beğeniler + Bahsedilen + Yeniden paylaşılan + Paylaşım Başlatıldı + Hata: Pod listesi alınamadı! + Üzgünüm, devam etmek için internete bağlı olmalısın. + Onayla + Çıkmak mı istiyorsun ? + Daha + Hakkında | Yardım + Takip Edilen Etiketler + Genel etkinlikler + Raporlar + Bağlantıyı metin olarak paylaş + Web sayfasının ekran görüntüsünü paylaş + Web sayfasının ekran görüntüsünü al + Görüntüyü şuna kaydet + Ekran görüntüsü olarak kaydediliyor: + Bağlantı adresi kopyalandı… + Yeni gönderi + Başa dön + Etiketlere veya kişilere göre arayın + Uygulamadan çık + Mobil/masaüstü görünümünü değiştir + Paylaş... + Etiket + Insan + Lütfen bir isim ekleyin + Bağlantı adresini paylaş + Görseli kaydet + Görsel paylaş + Harici tarayıcıda aç… + Bağlantı adresini panoya kopyala + Görsel adresini panoya kopyala + Görsel yüklenemedi - - Hakkında - Lisans - Hata Ayıklama - Uygulama - Cihaz - diaspora* Pod - Hata Ayıklama Günlükleri - Hata Ayıklama Günlüğü (Detaylı) - Uygulama Sürümü: %1$s - Android Sürümü: %1$s - Cihaz Adı: %1$s - Kod adı: %1$s - Pod Profil Adı: %1$s - Pod Adresi: %1$s - Hata ayıklama günlüğü panoya kopyalandı - Koda katkıda bulun! - Kaynağı al - Uygulamayı tercüme et! - Uygulamayı kendi dilinde kullanamıyor musun? Bunu değiştirebilirsin. Neden çeviride bize yardımcı olmuyorsun? Herkesin çeviriye katkıda bulunabilmesi için Crowdin platformunu kullanıyoruz. - Hadi çevirelim - Geri bildirimde bulun! - Hataları Bildir - Herkese duyur! - Uygulamayı paylaş - Hey %1$s ! #dandelion’a bir göz at! - - Yardımcılar - Katkıda Bulunanlar - %1$s<br><br>Teşekkürler! - GNU GPLv3+ License - Üçüncü Parti Kütüphaneler - Şu kütüphaneler kullanıldı: - Daha fazla göster - - + Görüntüleri kaydetmek/yüklemek için \"Depolama Erişim İzni\" vermeniz gerekir. Sonrasında uygulamayı tamamen kapatmalı veya cihazı yeniden başlatmalısınız. Depoylama erişimine izin vermeyebilirsiniz, ancak daha sonra görüntüleri kaydetmek isterseniz, daha sonra izin verebilirsiniz. İzin vermek için: Sistem Ayarları - Uygulamalar - dandelion* İzinler bölümü \"Depolama Erişim İzni\" verebilirsiniz. + Görüntüleri kaydetmek/yüklemek için \"Depolama Erişim İzni\" vermeniz gerekir. Sonrasında uygulamayı tamamen kapatmalı veya cihazı yeniden başlatmalısınız. Depoylama erişimine izin vermeyebilirsiniz, ancak daha sonra görüntüleri kaydetmek isterseniz, daha sonra izin verebilirsiniz. İzin vermek için: Sistem Ayarları - Uygulamalar - dandelion* İzinler bölümü \"Depolama Erişim İzni\" verebilirsiniz. + İzin reddedildi. + İzin verildi. Lütfen tekrar deneyin. + Özel Pod + Pod adı + Protokol + Pod adresi + Eksik değer + Akışta son ziyaret edilen sayfaya git? + Durum çubuğunu ana görünümde gizle + Durum çubuğunu gizle + Başlığı ana görünümde göster + Başlığı göster + Başlatıcı kısayolu Üst araç çubuğu akışı yükler Üst araç çubuğunda boş bir yere tıklayarak akışı başlatabilirsiniz @@ -74,6 +91,7 @@ Menü Kaydırıcı + Gezinme çekmecesindeki girişlerin görünürlüğünü kontrol et Kullanıcı Genel Yönetici @@ -85,8 +103,11 @@ Vurgu rengi İlerleme çubuğunun rengi AMOLED Modu + Uygulamanın birçok yerinde AMOLED ekrana uyumlu siyah renkleri geçersiz kılın. Bu ayarı değiştirmek için yeniden başlatmanız gerekiyor. Diaspora * \'ya göz atmak için, kişisel diyaspora * hesap ayarlarınızda bulunan Dark temasını da aktive etmeniz gerekir. Genişletilmiş Bildirimler + Bildirim zilini, bildirim kategorilerini gösteren bir açılır menü ile genişlet + Bu uygulamanın dilini değiştir. Değişikliklerin etkili olması için uygulamayı yeniden başlatın Dil Sistem dili @@ -102,6 +123,8 @@ Ekran döndürme Otomatik ekran döndürmeyi ayarlayın Varsayılan + Sensör\n + (sistem ayarlarını yok say) Dikey Yatay @@ -109,9 +132,79 @@ Tor (Orbot) HTTP Proxy için proxy ayarlarını yükle Proxy Proxy etkinleştir + Proxy dandelion*\'s traffic to circumvent firewalls.\n + May require restart. This might not work on some phones. Sunucu Port + Proxy kullanımını devre dışı bırakmak için uygulamanın yeniden başlatılması gerekiyor + Orbot proxy hazır ayarı yüklendi + Chrome Özel Sekmeler ile harici bağlantılar açın. Bu özelliği kullanabilmek için Chromium, Firefox veya Google Chrome\'un yüklü olması gerekir.\n + ÖNEMLİ NOT: Chrome Özel Sekmeler, yapılandırılmış proxy sunucuları kullanmaz! + Kişisel ayarlar + diaspora* hesap ayarlarını aç + Kişi listenizi yönetin + Etiketleri yönetin + Mevcut takip edilen etiketleri takipten vazgeç + Hesabı Değiştir + Yerel oturum verilerini silin ve başka bir diaspora * pod(a)/hesab(a) geçin + Bu, tüm çerezleri ve oturum verilerini siler. Gerçekten hesabını değiştirmek istiyor musun? + Önbelleği temizle + WebView önbelleğini temizle + Kaydırma yaparken üst ve alt araç çubuklarını otomatik olarak gizle + Araç Çubuklarını Otomatik Gizle + shared-by-notice ekle + Bu uygulamaya, paylaşılan metinlere bir referans ekleyin: [örn #dandelion] + Çeşitli + Tamamen Sıfırlama + Uygulama ile ilgili tüm ayarları yerel olarak sil ve tüm hesaplardan çıkış yap + Bu, uygulamanın tüm değiştirilen ayarlarını varsayılan değerlerine sıfırlar ve tüm podlardan çıkar. İndirdiğiniz görüntülere donulmaz. Devam etmek istediğine emin misin? + Temel reklam engelleyiciyi etkinleştirin. Reklamlar yerleşik görünümlere dahil edilebilir + Reklamları engelle + Hakkında + Lisans + Hata Ayıklama + Uygulama + Cihaz + diaspora* Pod + Hata Ayıklama Günlükleri + Hata Ayıklama Günlüğü (Detaylı) + Uygulama Sürümü: %1$s + Android Sürümü: %1$s + Cihaz Adı: %1$s + Kod adı: %1$s + Pod Profil Adı: %1$s + Pod Adresi: %1$s + Hata ayıklama günlüğü panoya kopyalandı + dandelion* is your companion app for browsing the social network diaspora*. Yararlı araç çubukları ve Tor Ağı gibi proxy sunucularıyla sosyal deneyimlerinize destek sağlar. + Koda katkıda bulun! + dandelion* özgürlük için özgürce geliştirilir ve diaspora* projesinin fikirlerini takip eder. Katkıda bulunmak istiyorsanız, devam edin! Şu anda çok küçük bir takımız, bu yüzden her türlü yardımı çok takdir ediyoruz! + Kaynağı al + Uygulamayı tercüme et! + Uygulamayı kendi dilinde kullanamıyor musun? Bunu değiştirebilirsin. Neden çeviride bize yardımcı olmuyorsun? Herkesin çeviriye katkıda bulunabilmesi için Crowdin platformunu kullanıyoruz. + Hadi çevirelim + Geri bildirimde bulun! + dandelion* hala geliştirme aşamasındadır, bu yüzden önerileriniz veya herhangi bir geri bildiriminiz varsa, lütfen bize haber vermek için hata izleyicimizi kullanın! + Hataları Bildir + Herkese duyur! + Arkadaşlarınıza ve ailenize diaspora* ve #dandelion hakkında bilgi verin! Neden deneyimleriniz hakkında blog yazmıyorsunuz? Sizden duymak isteriz! + Uygulamayı paylaş + Hey %1$s ! #dandelion’a bir göz at! + + Yardımcılar + Bu uygulamayı şu anda geliştiren ve devam ettiren <br><br>%1$s + Katkıda bulunanlar + %1$s<br><br>Teşekkürler! + GNU GPLv3+ License + Üçüncü Parti Kütüphaneler + Şu kütüphaneler kullanıldı: + LeafPic\'ten biraz ilham ve kod aldık. Göz atabilirsiniz, özgür bir yazılım! + Daha fazla göster + YouTube linklerini harici uygulamada açmak için etkinleştir + YouTube linkleri + Hesabınızın temasını değiştirin + Yenilemek için çek + Yenilemek için sayfanın üst kısmından çekin\nDeğişikliklerin geçerli olması için uygulamayı yeniden başlat. diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index 8494c969..19fb0794 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -1,7 +1,11 @@ + Відкрити панель навігації + Закрити панель навігації Перезавантажити + Закрити + Скасувати Налаштування Сповіщення @@ -48,14 +52,14 @@ Шукати мітки чи людей Вийти Перемкнути мобільний/стаціонарний вигляд - Поширити… + Поширити… за мітками за людьми Будь ласка, додайте ім\'я Поширити посилання Зберегти зображення Поділитися зображенням - Відкрити у зовнішньому браузері… + Відкрити у зовнішньому браузері… Копіювати адресу посилання у буфер обміну Копіювати зображення у буфер обміну @@ -77,48 +81,11 @@ Адреса поду Відсутнє значення Перейти на останню відвідану сторінку в потоці? - - Про нас - Ліцензія - Налагодження - Програма - Пристрій - Под Діаспора* - Журнал налагодження - Журнал налагодження (детальний) - Версія програми: %1$s - Версія Android: %1$s - Назва пристрою: %1$s - Кодова назва: %1$s - Назва профілю поду: %1$s - Домен поду: %1$s - Журнал налагодження скопійовано в буфер обміну - dandelion* — це ваша програмка для перегляду соціальної мережі Діаспора*. Для зручності, застосунок додає такі функції, як використання панелей інструментів і підримку проксі-серверів на зразок мережі Tor. - Зробіть внесок у код! - dandelion* розробляється як вільне програмне забезпечення і слідує ідеям проекту Діаспора*. Якщо ви хочете зробити внесок, приєднуйтеся! Зараз ми дуже маленька команда, тож будемо раді будь-якій допомозі! - Отримати вихідний код - Перекласти цю програмку - Цей застосунок недоступний вашою мовою? Ви можете це змінити! Чому б не допомогти нам, переклавши його? Ми використовуємо платформу crowdin, де будь-хто може перекласти програмку. - Хочу перекладати - Залишіть відгук! - dandelion* все ще перебуває у розробці, тому якщо у вас є побажання або відгуки, будь ласка, скористайтеся нашим баг-трекером, щоб ми знали! - Повідомити про помилку - Поділіться з друзями! - Розкажіть про Діаспору і #dandelion друзям і родині! Чому б не написати у блог про свій досвід? Будемо раді почути вашу думку! - Поділитися застосунком - Привіт! Спробуй #dandelion! %1$s - - Розробники - Зараз цю програмку розробляють і підтримують <br><br>%1$s - Учасники - %1$s<br><br>Дякуємо! - Ліцензія GNU GPLv3+ - Сторонні бібліотеки - Використовуються такі бібліотеки: - Ми взяли трохи натхнення і коду з LeafPic. Це також вільне програмне забезпечення, тож користуйтеся! - Хочу знати більше - - + Приховати рядок статусу + Сховати рядок статусу + Показати заголовки у головному вікні + Показувати заголовок + Створити ярлик Верхня панель завантажує потік Натисніть на вільному місці верхньої панелі, щоб відкрити потік @@ -146,7 +113,7 @@ Розширені сповіщення Додати до дзвоника сповіщень випадне меню з категоріями сповіщень - Змінити мову цієї програмки. Перезавантажте застосунок, щоб зміни запрацювали + Змінити мову додатку. Перезапустіть додаток для застосування змін. Мова Мова системи @@ -199,4 +166,48 @@ Це зітре усі змінені налаштування застосунку до їхніх значень за замовчуванням і вилогінить вас з усіх подів. Ваші завантажені зображення залишаться на міці. Бажаєте продовжити? Увімкнути базовий AdBlocker. Реклама може зберегтися, напр., у вбудованих переглядах Блокувати рекламу + Про Markor + Ліцензія + Налагодження + Програма + Пристрій + Под Діаспора* + Журнал налагодження + Журнал налагодження (детальний) + Версія програми: %1$s + Версія Android: %1$s + Назва пристрою: %1$s + Кодова назва: %1$s + Назва профілю поду: %1$s + Домен поду: %1$s + Журнал налагодження скопійовано в буфер обміну + dandelion* — це ваша програмка для перегляду соціальної мережі Діаспора*. Для зручності, застосунок додає такі функції, як використання панелей інструментів і підримку проксі-серверів на зразок мережі Tor. + Зробіть внесок у код! + dandelion* розробляється як вільне програмне забезпечення і слідує ідеям проекту Діаспора*. Якщо ви хочете зробити внесок, приєднуйтеся! Зараз ми дуже маленька команда, тож будемо раді будь-якій допомозі! + Отримати вихідний код + Перекласти цю програмку + Цей застосунок недоступний вашою мовою? Ви можете це змінити! Чому б не допомогти нам, переклавши його? Ми використовуємо платформу crowdin, де будь-хто може перекласти програмку. + Хочу перекладати + Залишіть відгук! + dandelion* все ще перебуває у розробці, тому якщо у вас є побажання або відгуки, будь ласка, скористайтеся нашим баг-трекером, щоб ми знали! + Повідомити про помилку + Поділіться з друзями! + Розкажіть про Діаспору і #dandelion друзям і родині! Чому б не написати у блог про свій досвід? Будемо раді почути вашу думку! + Поділитися застосунком + Привіт! Спробуй #dandelion! %1$s + + Розробники + Зараз цю програмку розробляють і підтримують <br><br>%1$s + Автори + %1$s<br><br>Дякуємо! + Ліцензія GNU GPLv3+ + Сторонні бібліотеки + Використовуються такі бібліотеки: + Ми взяли трохи натхнення і коду з LeafPic. Це також вільне програмне забезпечення, тож користуйтеся! + Хочу знати більше + Увімкнути для відкриття посилань Youtube у зовнішньому застосунку + Youtube посилання + Змінити тему вашого облікового запису + Потягніть, щоб оновити + Потягніть сторінку згори вниз, щоб оновити.\nВам потрібно перезавантажити застосунок, щоб зміни набрали сили. diff --git a/app/src/main/res/values-ur/strings.xml b/app/src/main/res/values-ur/strings.xml new file mode 100644 index 00000000..43d88f4b --- /dev/null +++ b/app/src/main/res/values-ur/strings.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml new file mode 100644 index 00000000..c38cc780 --- /dev/null +++ b/app/src/main/res/values-vi/strings.xml @@ -0,0 +1,30 @@ + + + + + Cài đặt + Tìm kiếm + + + + + + Ẩn thanh trạng thái + + + Xuất hiện + + + + + + + + + + + + Trong khoảng + + Những người đóng góp + diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml new file mode 100644 index 00000000..d1b2cd37 --- /dev/null +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -0,0 +1,213 @@ + + + + 打开导航栏 + 隐藏导航栏 + 刷新 + 关闭 + 取消 + + 设置 + 通知 + 对话 + 串流 + 资料 + 方面 + 活动 + 已赞 + 已评论 + 提及 + 公共 + 搜索 + 联系人 + 更新日志 + 统计 + + 全部通知 + 也评论了 + 评论此文 + 已赞 + 已提及 + 已转发 + 已开始分享 + + 错误:无法检索播客列表! + 抱歉,您必须连接到互联网 + 确认 + 您是否想要退出? + + 更多 + 关于 | 帮助 + 关注的标签 + 公共活动 + 反馈 + 以文本形式共享链接 + 分享网页截图 + 网页截图 + 保存图像至 + 保存截图为: + 已复制链接地址 + 新文章 + 返回顶部 + 按标签或人物搜索 + 退出应用 + 切换移动版/桌面版视图 + 共享。。。 + 按标签搜索 + 按人物搜索 + 请添加姓名 + 分享链接地址 + 保存图像 + 分享图像 + 在外部浏览器中打开 + 复制链接地址至剪切板 + 复制图像地址至剪切板 + + 无法载入图像 + + 您必须授予“访问存储权限”以保存屏幕截图。 + 完全关闭应用程序或重启设备。如果您不允许存储访问,但希望稍后使用 + 屏幕截图功能,您可以稍后批准权限。请打开:系统设置 - 应用 - + dandelion*。 + 您必须允许“访问存储权限”保存/上传图像。 + 在完全关闭应用程序或重启设备之后。如果您不允许存储访问,但希望稍后保存图像 + 您可以稍后授权。请打开:系统设置 - 应用 - + dandelion。 + 权限被拒绝。 + 已授予权限。请重试。 + 自定义 Pod + Pod 名称 + 协议 + Pod 地址 + 缺失值 + 跳转到上一个访问过的页面? + 在主视图中隐藏状态栏 + 隐藏状态栏 + 在主视图中显示标题 + 显示标题 + 启动器快捷方式 + + 顶级工具栏负载流 + 点击顶部工具栏的空间打开流流 + + 外观 + 网络 + 播客设置 + 可操作性 + + + 导航滑块 + 在导航抽屉中控制条目的可见性 + 用户 + 通用 + 管理员 + + 主题及颜色 + 控制应用所使用的颜色 + 主颜色 + 工具栏颜色 + 强调色 + 进度条颜色 + AMOLED 模式 + 在 AMOLED 中显示友好的黑色。您需要重启切换此设置。在暗中浏览散居地* 也需要激活暗色主题,您可以在您的个人散居地* 帐户设置中找到。 + + 扩展通知 + 通过显示通知类别的辍学菜单扩展通知铃声 + 更改语言(重启应用后生效) + 语言 + 系统语言 + + 控制WebView文本大小 + 字体大小 + 正常 + + 巨大 + + 加载图像 + 切换图像加载到例如保存移动数据 + + 屏幕旋转 + 控制自动旋转屏幕 + 默认 + 传感器\n(忽略系统设置) + 肖像 + 横向 + + 加载 Tor 预设 + 为 Tor (Orbot) HTTP 代理加载代理设置 + 代理 + 启用代理 + 代理 dandelion*,来规避防火墙。\n可能需要重启。这可能无法在某些手机上运行。 + 主机 + 端口 + 应用需要重新启动以禁用代理使用 + 加载轨道代理预设 + + 使用 Chrome 自定义标签打开外部链接。Chromium、Firefox 或 Google Chrome 需要安装才能使用此功能。 \nImportant:Chrome 自定义标签不使用配置的代理服务器! + + 个人设置 + 打开 Diaspora* 帐户设置 + 管理您的联系人列表 + 管理哈希标签 + 取消关注已经跟随哈希 + 更改帐户 + 擦除本地会话数据并切换到另一个 Diaspora* pod/帐户 + 这将删除所有 cookie 和会话数据。您真的想要更改您的帐户吗? + 清除缓存 + 清除WebView缓存 + 滚动时自动隐藏顶部和底部工具栏 + 智能隐藏工具栏 + 附加共享通知 + 将此应用添加到共享文本:[通过 #dandelion] + + 杂项 + 完全重置 + 本地擦除应用程序的所有设置,注销所有账户 + 这将重置应用程序的所有更改设置为默认值,并从所有讲台上注销。您下载的图像将保持不变。您确定要继续吗? + 启用基本的 AdBlocker。广告可能包括在内部的视图中 + 屏蔽广告 + 关于 + 许可 + 调试 + 适用范围 + 设备 + 散居者*播客 + 调试日志 + 调试日志(Verbose) + 应用版本: %1$s + Android 版本: %1$s + 设备名称: %1$s + 代码名: %1$s + 播客资料名称: %1$s + 播客域: %1$s + 调试日志已复制到剪贴板 + dandelion* 是您浏览社交网络 diaspora* 的配套应用。它添加了一些功能,例如有用的工具栏和支持Tor网络等代理服务器。 + 贡献代码! + dandelion* 像自由一样自由发展,遵循 diaspora* 项目的想法。如果你想作出贡献,继续前进!目前我们是一个非常小的团队,因此我们非常感谢任何帮助! + 获取源 + 翻译应用! + 应用程序不可用您的语言?您可以更改它!为什么你没有帮助我们翻译它?我们使用Stringlate来让任何人能够翻译这个应用。 + 让我翻译 + 提供反馈! + dandelion* 仍在开发中,所以如果您有任何建议或任何反馈,请使用我们的错误追踪器让我们知道! + 报告错误 + 传播单词! + 告诉你的朋友和家人关于 Diaspora* 和 #dandelion! 为什么你没有关于你的经历的博客? 我们很喜欢听到你的声音! + 分享应用 + 嘿!看看 #dandelion! %1$s + + 维护者 + 此应用目前正在开发和维持 <br><br>%1$s + 贡献者 + %1$s<br><br>谢谢! + GNU GPLv3+ 授权 + 第三方图书馆 + 使用下列图书馆: + 我们从LeafPic获得了一些灵感和代码。去检查它,也是免费的软件! + 告诉我更多 + 启用在外部应用打开 Youtube 链接 + YouTube链接 + 更改您的帐户的主题 + 下拉刷新 + 在页面顶部下拉刷新。\n您需要重新启动应用程序以使更改生效。 + diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index fdbdc7ad..22866bc8 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -1,7 +1,11 @@ + 開啟側邊導覽選單 + 關閉側邊導覽選單 重新下載 + 關閉 + 取消 設定 通知 @@ -48,7 +52,7 @@ 找人或標籤 退出應用程式 切換行動/桌上型版面 - 分享… + 分享… 找標籤 找人 請輸入名稱 @@ -75,48 +79,7 @@ 隱藏狀態列 在主畫面中顯示標題列 顯示標題列 - - 說明 - 授權條款 - 除錯 - 應用程式 - 裝置 - diaspora* 豆莢 - 除錯紀錄 - 除錯紀錄(詳細版) - 應用程式版本: %1$s - Android 版本: %1$s - 裝置名稱: %1$s - 代號: %1$s - 豆莢設定名稱: %1$s - 豆莢網址: %1$s - 已經複製除錯紀錄到剪貼簿了 - dandelion* (蒲公英*) 是你瀏覽 diaspora* 社交網站的好朋友。它讓你的體驗增加了好用的工具列,並且支援像是 Tor 之類的代理伺服器服務。 - 貢獻程式碼! - dandelion* 是自由軟體開發專案,並且追隨 diaspora* 專案的信念。想要貢獻你的心力嗎?那就來吧!目前我們還只是個很小的團隊,任何形式的幫忙都會讓我們足感心! - 取得源碼 - 翻譯應用程式! - 應用程式沒有你使用語言的版本嗎?你可以改變現狀!何不來幫忙我們翻譯呢?我們使用 crowdin 平台,好讓每個人都能夠參與應用程式的翻譯工作。 - 我來翻譯 - 意見回饋! - dandelion* 還在開發階段,所以如果你有任何建議,或是有其他的意見要回饋,請使用我們的臭蟲追蹤網站來讓我們知道! - 回報臭蟲 - 報給人知! - 告訴你的朋友和家人有 #dandelion 這個好東西!何不部落格一下你的使用經驗呢?我們期待聽聽你的故事! - 分享應用程式 - 哇!看看 #dandelion 這套應用程式! %1$s - - 維護人員 - 目前這套應用程式是由以下人員開發與維護: <br><br>%1$s - 貢獻人 - %1$s<br><br>感謝你們! - GNU GPLv3+ 授權條款 - 第三方程式庫 - 我們使用了下列程式庫: - 我們從 LeafPic 應用程式得到一些啟發以及程式碼。去看看吧,它也是自由軟體喔! - 再多說一些 - - + 桌面捷徑 上方工具列可載入流水帳 可以點上方工具列的空白區域來進入流水帳 @@ -198,4 +161,48 @@ 將要重設此應用程式的所有設定為預設值,並登出你在所有豆莢的帳號。不過不會變動已經下載了的圖片。確定要繼續嗎? 開啟基本的廣告封鎖器。廣告可能會出現在嵌入式視圖之類的地方。 廣告封鎖 + 說明 + 授權條款 + 除錯 + 應用程式 + 裝置 + diaspora* 豆莢 + 除錯紀錄 + 除錯紀錄(詳細版) + 應用程式版本: %1$s + Android 版本: %1$s + 裝置名稱: %1$s + 代號: %1$s + 豆莢設定名稱: %1$s + 豆莢網址: %1$s + 已經複製除錯紀錄到剪貼簿了 + dandelion* (蒲公英*) 是你瀏覽 diaspora* 社交網站的好朋友。它讓你的體驗增加了好用的工具列,並且支援像是 Tor 之類的代理伺服器服務。 + 貢獻程式碼! + dandelion* 是自由軟體開發專案,並且追隨 diaspora* 專案的信念。想要貢獻你的心力嗎?那就來吧!目前我們還只是個很小的團隊,任何形式的幫忙都會讓我們足感心! + 取得源碼 + 翻譯應用程式! + 應用程式沒有你使用語言的版本嗎?你可以改變現狀!何不來幫忙我們翻譯呢?我們使用 crowdin 平台,好讓每個人都能夠參與應用程式的翻譯工作。 + 我來翻譯 + 意見回饋! + dandelion* 還在開發階段,所以如果你有任何建議,或是有其他的意見要回饋,請使用我們的臭蟲追蹤網站來讓我們知道! + 回報臭蟲 + 報給人知! + 告訴你的朋友和家人有 #dandelion 這個好東西!何不部落格一下你的使用經驗呢?我們期待聽聽你的故事! + 分享應用程式 + 哇!看看 #dandelion 這套應用程式! %1$s + + 維護人員 + 目前這套應用程式是由以下人員開發與維護: <br><br>%1$s + 貢獻人 + %1$s<br><br>感謝你們! + GNU GPLv3+ 授權條款 + 第三方程式庫 + 我們使用了下列程式庫: + 我們從 LeafPic 應用程式得到一些啟發以及程式碼。去看看吧,它也是自由軟體喔! + 再多說一些 + 使用其他應用程式來開啟 Youtube 連結 + Youtube 連結 + 修改帳號的佈景主題 + 下拉可更新 + 從頁面的上方下拉一下可以更新內容。\n這個設定修改後需要重啟應用程式才會生效。 diff --git a/app/src/main/res/values/strings-not_translatable.xml b/app/src/main/res/values/strings-not_translatable.xml index aaa5137a..7f67ac27 100644 --- a/app/src/main/res/values/strings-not_translatable.xml +++ b/app/src/main/res/values/strings-not_translatable.xml @@ -37,10 +37,6 @@ not_implemented - @string/not_implemented - @string/not_implemented - @string/not_implemented - @string/not_implemented auto @@ -148,4 +144,7 @@ pref_key__show_title PDF gsantner - + pref_key__open_youtube_external_enabled + pref_key_manage_theme + pref_key__swipe_refresh_enabled + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index d61d659c..78bac4bc 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -4,6 +4,8 @@ Open navigation drawer Close navigation drawer Reload + Close + Cancel Settings @@ -253,5 +255,10 @@ The following libraries are used: We took some inspiration and code from LeafPic. Go check it out, it\'s free software as well! Tell me more + Enable to open Youtube links on external app + Youtube links + Change the theme of your account + Pull to refresh + Pulling down on top of page to refresh.\nYou need to restart the app for changes to take effect. diff --git a/app/src/main/res/xml/preferences__master.xml b/app/src/main/res/xml/preferences__master.xml index 7bb330f9..c6afd55f 100644 --- a/app/src/main/res/xml/preferences__master.xml +++ b/app/src/main/res/xml/preferences__master.xml @@ -73,6 +73,13 @@ android:summary="@string/open_external_links_with_chrome_custom_tabs_description" android:title="@string/pref_title__chrome_custom_tabs_enabled"/> + + + + diff --git a/app/src/main/res/xml/preferences__sub_themes.xml b/app/src/main/res/xml/preferences__sub_themes.xml index ec5a5e00..aa4bde15 100644 --- a/app/src/main/res/xml/preferences__sub_themes.xml +++ b/app/src/main/res/xml/preferences__sub_themes.xml @@ -21,5 +21,12 @@ android:summary="@string/amoled_mode_description__app_specific" android:title="@string/amoled_mode" android:icon="@drawable/ic_color_lens_black_24px" /> + + + \ No newline at end of file diff --git a/build.gradle b/build.gradle index 4afa4183..75a6ed58 100644 --- a/build.gradle +++ b/build.gradle @@ -1,44 +1,43 @@ /*####################################################### -* -* Maintained by Gregor Santner, 2017- -* https://gsantner.net/ -* -* License: Apache 2.0 / Commercial -* https://github.com/gsantner/opoc/#licensing -* https://www.apache.org/licenses/LICENSE-2.0 -* + * + * SPDX-FileCopyrightText: 2017-2023 Gregor Santner + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + * #########################################################*/ -import java.text.SimpleDateFormat - // Top-level build file where you can add configuration options common to all sub-projects/modules. -buildscript { - ext.version_setup_compileSdk = 27 - ext.version_setup_minSdk = 17 - ext.version_setup_targetSdk = ext.version_setup_compileSdk - ext.version_setup_buildTools = "27.0.3" // Specifying optional +import java.text.SimpleDateFormat - // https://developer.android.com/studio/releases/gradle-plugin.html - ext.version_gradle_tools = "3.1.0" - // https://developer.android.com/topic/libraries/support-library/revisions.html - ext.version_library_appcompat = "27.1.1" - // https://github.com/JakeWharton/butterknife/releases - ext.version_library_butterknife = "8.8.1" - // https://github.com/guardianproject/NetCipher/releases - ext.version_library_netcipher = "2.0.0-alpha1" - // https://mvnrepository.com/artifact/org.jetbrains.kotlin/kotlin-gradle-plugin#LookAtCentral - ext.version_plugin_kotlin = "1.2.21" - ext.enable_plugin_kotlin = false +buildscript { + ext { + version_gradle_tools = "3.6.3" + version_plugin_kotlin = "1.3.72" + enable_plugin_kotlin = false + + version_compileSdk = 29 + version_buildTools = "29.0.3" + version_minSdk = 17 + + // https://developer.android.com/topic/libraries/support-library/ + version_library_appcompat = "28.0.0" //androidx + // https://github.com/JakeWharton/butterknife/releases + version_library_butterknife = "8.8.1" //9.0.0-rc2 + // https://github.com/guardianproject/NetCipher/releases + version_library_netcipher = "2.0.0-alpha1" + } repositories { - google() + maven { url 'https://maven.google.com' } jcenter() + maven { url "https://jitpack.io" } + mavenCentral() } + dependencies { - classpath "com.android.tools.build:gradle:$version_gradle_tools" - classpath 'com.github.dcendents:android-maven-gradle-plugin:1.5' + classpath "com.android.tools.build:gradle:${version_gradle_tools}" + classpath 'com.github.dcendents:android-maven-gradle-plugin:2.1' if (project.enable_plugin_kotlin) { - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$version_plugin_kotlin" + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${version_plugin_kotlin}" } // NOTE: Do not place your application dependencies here; they belong @@ -48,15 +47,22 @@ buildscript { allprojects { repositories { - google() + maven { url 'https://maven.google.com' } jcenter() - mavenCentral() maven { url "https://jitpack.io" } + mavenCentral() } tasks.matching { task -> task.name.matches('.*generate.*Resources') }.all { task -> task.dependsOn copyRepoFiles } + + tasks.matching {it instanceof Test}.all { // Enable unit test output, html+xml output + testLogging.events "passed", "skipped", "failed", "standardOut", "standardError" + testLogging.showStandardStreams = true + reports.junitXml.enabled = true + reports.html.enabled = true + } } task clean(type: Delete) { @@ -77,7 +83,7 @@ static String findUsedAndroidLocales() { Set langs = new HashSet<>() new File('.').eachFileRecurse(groovy.io.FileType.DIRECTORIES) { final foldername = it.name - if (foldername.startsWith('values-') && !it.canonicalPath.contains("build" + File.separator + "intermediates")) { + if (foldername.startsWith('values-') && !it.canonicalPath.contains("build" + File.separator + "intermediates") && !it.canonicalPath.contains("gradle" + File.separator + "daemon")) { new File(it.toString()).eachFileRecurse(groovy.io.FileType.FILES) { if (it.name.toLowerCase().endsWith(".xml") && it.getCanonicalFile().getText('UTF-8').contains(" return 'unknown' } } + @SuppressWarnings(["UnnecessaryQualifiedReference", "SpellCheckingInspection", "GroovyUnusedDeclaration"]) // Returns the build date in a RFC3339 compatible format. TZ is always converted to UTC static String getBuildDate() { diff --git a/circle.yml b/circle.yml deleted file mode 100644 index 35978980..00000000 --- a/circle.yml +++ /dev/null @@ -1,38 +0,0 @@ -################### -general: - artifacts: - - /home/ubuntu/dandelion/app/build/outputs/apk/ - branches: - ignore: - - gh-pages - - l10n_master - - crowdin - -################### -machine: - java: - version: oraclejdk8 - environment: - ANDROID_HOME: /usr/local/android-sdk-linux - -################### -dependencies: - pre: - # Android SDK Platform - - if [ ! -d "/usr/local/android-sdk-linux/platforms/android-26" ]; then echo y | android update sdk --no-ui --all --filter "android-26"; fi - # Android SDK Build-tools - - if [ ! -d "/usr/local/android-sdk-linux/build-tools/26.0.1" ]; then echo y | android update sdk --no-ui --all --filter "build-tools-26.0.1"; fi - # Android Support Repository - deprecated - #- if [ ! -d "/usr/local/android-sdk-linux/extras/android/m2repository/com/android/support/design/26.2.0" ]; then echo y | android update sdk --no-ui --all --filter "extra-android-m2repository"; fi - - - cache_directories: - - /usr/local/android-sdk-linux/platforms/android-26 - - /usr/local/android-sdk-linux/build-tools/26.0.1 - #- /usr/local/android-sdk-linux/extras/android/m2repository - -################### -test: - override: - - (./gradlew assembleFlavorDefault): - timeout: 360 diff --git a/crowdin.yml b/crowdin.yml new file mode 100644 index 00000000..c4ffe7a5 --- /dev/null +++ b/crowdin.yml @@ -0,0 +1,70 @@ +# vim: sw=2 ts=2 noexpandtab: +commit_message: "[ci skip] Update translations (%language%)" +append_commit_message: false +files: + - source: /app/src/main/res/values/strings.xml + translation: /app/src/main/res/values-%android_code%/%original_file_name% + languages_mapping: + android_code: + az: az # Azerbaijani + ckb: ckb # Sorani (Kurdish) + kmr: kmr # Kurmanji (Kurdish) + gl: gl # Galician + sc: sc # Sardinian + kab: kab # Kabyle + hi: hi # Indian, Hindi + ml-IN: ml # Indian, Malayalam + fa: fa # Persian + nb: nb-rNO # Norwegian, Bokmal + sv-SE: sv # Swedish + es-ES: es # Espanol + de: de # German + ru: ru # Russian + pl: pl # Polish + ja: ja # Japanese + it: it # Italian + fr: fr # French + da: da # Danish + nl: nl # Dutch + hu: hu # Hungarian + cs: cs # Czech + ko: ko # Korean + zh-CN: zh-rCN # Chinese (Simplified on Android) + zh-TW: zh-rTW # Chinese (Traditional on Android) + el: el # Greek + "no": 'no' # Norwegian + tr: tr # Turkish + ca: ca # Catalan + pt-PT: pt # Portugese + pt-BR: pt-rBR # Portugese, Brazilian + fil: fil # Filipino + uk: uk # Ukrainian + ar: ar # Arabic + fi: fi # Finnish + af: af # Afrikaans + he: iw # Hebrew + ro: ro # Romanian + vi: vi # Vietnamese + bs: bs # Bosnian + sr: sr # Serbian, Cyrillic + sr-CS: sr-rRS # Serbian, Latin + bn: bn # Bengali + pa-IN: pa # Punjabi, India + jv: jw # Javanese + mr: mr # Marathi + te: te # Telugu + ur-PK: ur # Urdu, Pakistan + id: in # Indonesian + sk: sk # Slovak + kn: kn # Kannada (Asian) + ta: ta # Tamil (Asian) + sq: sq # Albanian + mk: mk # Macedonian + si-LK: si # Sinhala (Sri Lanka) + eo: eo # Esperanto + or: or # Odia/Oriya (India) + hr: hr # Croatia + bg: bg # Bulgarian + et: et # Estonian + th: th # Thai + translate_attributes: 0 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 18a0cf95..505b369a 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Sun Apr 08 08:39:15 CEST 2018 +#Fri Jul 26 02:59:10 CEST 2019 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-bin.zip diff --git a/metadata/de/full_description.txt b/metadata/de/full_description.txt new file mode 100644 index 00000000..5813a487 --- /dev/null +++ b/metadata/de/full_description.txt @@ -0,0 +1,23 @@ +Client für das gemeinschaftlich betriebene, verteile soziale Netzwerk diaspora*. +Verbessere dein Netzwerk-Erlebnis durch nützliche Features: + +⚡ Schnellzugriff auf die meisten diaspora* Funktionen +🎨 Anpassbar an deinen Geschmack +👉 Teile Inhalte mit und von der App +🌎 Unterstützung von Proxies (unterstützt Tor/Orbot) +📰 In-App-Browser zum schnellen lesen von Artikeln +🌆 Dark/AMOLED Modus zum Energiesparen und Augen schonen +🈴 Verfügbar in vielen Sprachen +#️⃣ Durchsuche Tags und Aspekte + +🈯 Getrennte App- und Systemsprache. Nutze die App in beliebiger Sprache (zB. Englisch) und behalte deine Systemsprache (zB. Deutsch) bei. + +🔐 Du bist noch auf der Suche nach einem Pod um dich zu registrieren? Die App bringt eine große Liste mit und weitere Pods findest du auf diaspora.fediverse.observer. + +🍻 Mehrere Accounts: Nutze dandelion* und dandelior* um zwei Accounts auf dem gleichen Gerät zu nutzen. Die Apps besitzen eigene Icons und unterschiedliche Vorgabefarben. + +🌍 Hinweis: Die App nutzt Androids WebView Komponente um Inhalte von diaspora* Pods in der mobilen Ansicht anzuzeigen. Für fehlende Features frage bitte auf dem diaspora* Bug Tracker. + +Weitere Informationen: +Project site | diaspora* FAQ + diff --git a/metadata/de/short_description.txt b/metadata/de/short_description.txt new file mode 100644 index 00000000..71a3004f --- /dev/null +++ b/metadata/de/short_description.txt @@ -0,0 +1 @@ +Client für das Soziale Netzwerk diaspora* diff --git a/metadata/en b/metadata/en deleted file mode 120000 index f2b0341f..00000000 --- a/metadata/en +++ /dev/null @@ -1 +0,0 @@ -en-US \ No newline at end of file diff --git a/metadata/en-US/changelogs/1.txt b/metadata/en-US/changelogs/1.txt new file mode 100644 index 00000000..d60031cb --- /dev/null +++ b/metadata/en-US/changelogs/1.txt @@ -0,0 +1 @@ +Added to F-Droid diff --git a/metadata/en-US/featureGraphic.png b/metadata/en-US/featureGraphic.png index 39038c2f..92105097 100644 Binary files a/metadata/en-US/featureGraphic.png and b/metadata/en-US/featureGraphic.png differ diff --git a/metadata/en-US/full_description.txt b/metadata/en-US/full_description.txt index cb8fdf60..e1c445bd 100644 --- a/metadata/en-US/full_description.txt +++ b/metadata/en-US/full_description.txt @@ -12,14 +12,11 @@ It adds useful features to your networking experience: 🈯 Use in any language that the app is translated in - for example in German but have English as system language. -🔐 Looking for a pod to register? The app lists many pods with more being listed at podupti.me. +🔐 Looking for a pod to register? The app lists many pods with more being listed at diaspora.fediverse.observer. 🍻 Multiple accounts: You can use dandelion* and dandelior* to use two accounts at the same time on one device. They use a different icon and other default colors. 🌍 Notice: The app uses the Android WebView component to display contents of diaspora* pods in the mobile view. For missing features and bugs in mobile view, ask at diaspora* bugtracker. More information: -Project Blog | diaspora* FAQ - -Support the project: -Translate using Stringlate | Join discussion on Matrix | Contribution information | Android Contribution Guide | Support main developer +Project site | diaspora* FAQ diff --git a/metadata/en-US/promoGraphic.png b/metadata/en-US/promoGraphic.png index a750adc9..186cec0f 100644 Binary files a/metadata/en-US/promoGraphic.png and b/metadata/en-US/promoGraphic.png differ diff --git a/metadata/zz/full_description.txt b/metadata/zz/full_description.txt new file mode 100644 index 00000000..8e27be7d --- /dev/null +++ b/metadata/zz/full_description.txt @@ -0,0 +1 @@ +text diff --git a/metadata/zz/short_description.txt b/metadata/zz/short_description.txt new file mode 100644 index 00000000..8e27be7d --- /dev/null +++ b/metadata/zz/short_description.txt @@ -0,0 +1 @@ +text diff --git a/metadata/zz/title.txt b/metadata/zz/title.txt new file mode 100644 index 00000000..8e27be7d --- /dev/null +++ b/metadata/zz/title.txt @@ -0,0 +1 @@ +text diff --git a/patches/vanitasvitae-Add-first-image-viewer-design.patch b/patches/vanitasvitae-Add-first-image-viewer-design.patch new file mode 100644 index 00000000..ffb9ecab --- /dev/null +++ b/patches/vanitasvitae-Add-first-image-viewer-design.patch @@ -0,0 +1,240 @@ +From 90ae580fa732962127156a333b5d879cd4a80d36 Mon Sep 17 00:00:00 2001 +From: vanitasvitae +Date: Mon, 10 Apr 2017 21:43:24 +0200 +Subject: [PATCH] Add first image viewer design + +--- + app/build.gradle | 1 + + .../activity/ImageViewFragment.java | 77 +++++++++++++++++++ + .../activity/MainActivity.java | 4 + + .../service/ImageDownloadTask.java | 17 ++++ + .../web/ContextMenuWebView.java | 16 ++++ + .../main/res/layout/image_view__fragment.xml | 19 +++++ + 6 files changed, 134 insertions(+) + create mode 100644 app/src/main/java/com/github/dfa/diaspora_android/activity/ImageViewFragment.java + create mode 100644 app/src/main/res/layout/image_view__fragment.xml + +diff --git a/app/build.gradle b/app/build.gradle +index 5449e9fc..c46301ed 100644 +--- a/app/build.gradle ++++ b/app/build.gradle +@@ -85,6 +85,7 @@ dependencies { + compile 'info.guardianproject.netcipher:netcipher:2.0.0-alpha1' + compile 'info.guardianproject.netcipher:netcipher-webkit:2.0.0-alpha1' + compile 'com.github.DASAR:ShiftColorPicker:v0.5' ++ compile 'com.davemorrissey.labs:subsampling-scale-image-view:3.6.0' + apt 'com.jakewharton:butterknife-compiler:8.0.1' + } + +diff --git a/app/src/main/java/com/github/dfa/diaspora_android/activity/ImageViewFragment.java b/app/src/main/java/com/github/dfa/diaspora_android/activity/ImageViewFragment.java +new file mode 100644 +index 00000000..164e37ae +--- /dev/null ++++ b/app/src/main/java/com/github/dfa/diaspora_android/activity/ImageViewFragment.java +@@ -0,0 +1,77 @@ ++package com.github.dfa.diaspora_android.activity; ++ ++import android.graphics.Bitmap; ++import android.os.Bundle; ++import android.view.LayoutInflater; ++import android.view.Menu; ++import android.view.MenuInflater; ++import android.view.View; ++import android.view.ViewGroup; ++import android.widget.ProgressBar; ++ ++import com.davemorrissey.labs.subscaleview.ImageSource; ++import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView; ++import com.github.dfa.diaspora_android.R; ++import com.github.dfa.diaspora_android.service.ImageDownloadTask; ++import com.github.dfa.diaspora_android.ui.theme.ThemedFragment; ++import com.github.dfa.diaspora_android.util.AppLog; ++ ++import butterknife.BindView; ++import butterknife.ButterKnife; ++ ++/** ++ * Created by vanitas on 10.04.17. ++ */ ++ ++public class ImageViewFragment extends ThemedFragment { ++ public static final String TAG = "com.github.dfa.diaspora_android.ImageViewFragment"; ++ public static final String IMAGE_SOURCE = "IMAGE_SOURCE"; ++ ++ @BindView(R.id.imageView) ++ SubsamplingScaleImageView imageView; ++ @BindView(R.id.marker_progress) ++ ProgressBar progressBar; ++ ++ @Override ++ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { ++ AppLog.d(this, "onCreateView()"); ++ View view = inflater.inflate(R.layout.image_view__fragment, container, false); ++ ButterKnife.bind(this, view); ++ return view; ++ } ++ ++ @Override ++ public void onViewCreated(View view, Bundle savedInstanceState) { ++ super.onViewCreated(view, savedInstanceState); ++ new ImageDownloadTask.ImageViewFragmentDownloadTask(this) ++ .execute(getArguments().getString(IMAGE_SOURCE)); ++ imageView.setMaxScale(5); ++ imageView.setDoubleTapZoomStyle(SubsamplingScaleImageView.ZOOM_FOCUS_CENTER); ++ imageView.setZoomEnabled(true); ++ } ++ ++ @Override ++ protected void applyColorToViews() { ++ ++ } ++ ++ @Override ++ public String getFragmentTag() { ++ return TAG; ++ } ++ ++ @Override ++ public void onCreateBottomOptionsMenu(Menu menu, MenuInflater inflater) { ++ ++ } ++ ++ @Override ++ public boolean onBackPressed() { ++ return false; ++ } ++ ++ public void showBitmap(Bitmap bitmap) { ++ progressBar.setVisibility(View.GONE); ++ imageView.setImage(ImageSource.bitmap(bitmap)); ++ } ++} +diff --git a/app/src/main/java/com/github/dfa/diaspora_android/activity/MainActivity.java b/app/src/main/java/com/github/dfa/diaspora_android/activity/MainActivity.java +index 8019fb6d..031b624f 100644 +--- a/app/src/main/java/com/github/dfa/diaspora_android/activity/MainActivity.java ++++ b/app/src/main/java/com/github/dfa/diaspora_android/activity/MainActivity.java +@@ -321,6 +321,10 @@ protected CustomFragment getFragment(String fragmentTag) { + PodSelectionFragment psf = new PodSelectionFragment(); + fm.beginTransaction().add(psf, fragmentTag).commit(); + return psf; ++ case ImageViewFragment.TAG: ++ ImageViewFragment ivf = new ImageViewFragment(); ++ fm.beginTransaction().add(ivf, fragmentTag).commit(); ++ return ivf; + default: + AppLog.e(this, "Invalid Fragment Tag: " + fragmentTag + + "\nAdd Fragments Tag to getFragment()'s switch case."); +diff --git a/app/src/main/java/com/github/dfa/diaspora_android/service/ImageDownloadTask.java b/app/src/main/java/com/github/dfa/diaspora_android/service/ImageDownloadTask.java +index 0834ee95..f11cfb82 100644 +--- a/app/src/main/java/com/github/dfa/diaspora_android/service/ImageDownloadTask.java ++++ b/app/src/main/java/com/github/dfa/diaspora_android/service/ImageDownloadTask.java +@@ -24,6 +24,9 @@ + import android.support.annotation.Nullable; + import android.widget.ImageView; + ++import com.davemorrissey.labs.subscaleview.ImageSource; ++import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView; ++import com.github.dfa.diaspora_android.activity.ImageViewFragment; + import com.github.dfa.diaspora_android.util.AppLog; + + import java.io.FileOutputStream; +@@ -95,4 +98,18 @@ protected void onPostExecute(Bitmap result) { + imageView.setImageBitmap(result); + } + } ++ ++ public static class ImageViewFragmentDownloadTask extends ImageDownloadTask { ++ ImageViewFragment ivf; ++ ++ public ImageViewFragmentDownloadTask(ImageViewFragment imageViewFragment) { ++ super(null, null); ++ this.ivf = imageViewFragment; ++ } ++ ++ @Override ++ protected void onPostExecute(Bitmap result) { ++ ivf.showBitmap(result); ++ } ++ } + } +diff --git a/app/src/main/java/com/github/dfa/diaspora_android/web/ContextMenuWebView.java b/app/src/main/java/com/github/dfa/diaspora_android/web/ContextMenuWebView.java +index c4c0e278..6c8947bd 100644 +--- a/app/src/main/java/com/github/dfa/diaspora_android/web/ContextMenuWebView.java ++++ b/app/src/main/java/com/github/dfa/diaspora_android/web/ContextMenuWebView.java +@@ -30,6 +30,7 @@ + import android.content.pm.PackageManager; + import android.graphics.Bitmap; + import android.net.Uri; ++import android.os.Bundle; + import android.os.Environment; + import android.support.v4.content.LocalBroadcastManager; + import android.util.AttributeSet; +@@ -38,6 +39,7 @@ + import android.widget.Toast; + + import com.github.dfa.diaspora_android.R; ++import com.github.dfa.diaspora_android.activity.ImageViewFragment; + import com.github.dfa.diaspora_android.activity.MainActivity; + import com.github.dfa.diaspora_android.service.ImageDownloadTask; + +@@ -53,6 +55,7 @@ + public static final int ID_SAVE_IMAGE = 10; + public static final int ID_IMAGE_EXTERNAL_BROWSER = 11; + public static final int ID_COPY_IMAGE_LINK = 15; ++ public static final int ID_VIEW_IMAGE = 16; + public static final int ID_COPY_LINK = 12; + public static final int ID_SHARE_LINK = 13; + public static final int ID_SHARE_IMAGE = 14; +@@ -82,6 +85,18 @@ public boolean onMenuItemClick(MenuItem item) { + HitTestResult result = getHitTestResult(); + String url = result.getExtra(); + switch (item.getItemId()) { ++ ++ case ID_VIEW_IMAGE: { ++ ImageViewFragment ivf = new ImageViewFragment(); ++ Bundle b = new Bundle(); ++ b.putString(ImageViewFragment.IMAGE_SOURCE, url); ++ ivf.setArguments(b); ++ ((MainActivity) parentActivity).getSupportFragmentManager().beginTransaction() ++ .addToBackStack(null) ++ .replace(R.id.fragment_container, ivf).commit(); ++ break; ++ } ++ + //Save image to external memory + case ID_SAVE_IMAGE: { + boolean writeToStoragePermitted = true; +@@ -224,6 +239,7 @@ protected void onPostExecute(Bitmap result) { + 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_VIEW_IMAGE, 0, "View").setOnMenuItemClickListener(handler); + } else if (result.getType() == HitTestResult.ANCHOR_TYPE || + result.getType() == HitTestResult.SRC_ANCHOR_TYPE) { + // Menu options for a hyperlink. +diff --git a/app/src/main/res/layout/image_view__fragment.xml b/app/src/main/res/layout/image_view__fragment.xml +new file mode 100644 +index 00000000..2b91c501 +--- /dev/null ++++ b/app/src/main/res/layout/image_view__fragment.xml +@@ -0,0 +1,19 @@ ++ ++ ++ ++ ++ +\ No newline at end of file