Compare commits
No commits in common. "master" and "cleaning" have entirely different histories.
140
.drone.yml
Normal file
140
.drone.yml
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
kind: pipeline
|
||||||
|
type: docker
|
||||||
|
name: test
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: BuildAndTest
|
||||||
|
image: mingc/android-build-box:latest
|
||||||
|
commands:
|
||||||
|
- echo "---------------------------------------------------------"
|
||||||
|
- echo "Configure gradle..."
|
||||||
|
- mkdir -p ~/.gradle && echo "org.gradle.daemon=false\nignoreGitVersion=true\npushCache=false\nsystemProp.org.gradle.internal.http.connectionTimeout=180000\nsystemProp.org.gradle.internal.http.socketTimeout=180000" >> ~/.gradle/gradle.properties
|
||||||
|
- echo "---------------------------------------------------------"
|
||||||
|
- echo "Building..."
|
||||||
|
- ./gradlew build -x test
|
||||||
|
- echo "---------------------------------------------------------"
|
||||||
|
- echo "Testing..."
|
||||||
|
- echo "---------------------------------------------------------"
|
||||||
|
- ./gradlew koverMergedXmlReport
|
||||||
|
environment:
|
||||||
|
TZ: Europe/Paris
|
||||||
|
SONAR_HOST_URL:
|
||||||
|
from_secret: sonarScannerHostUrl
|
||||||
|
SONAR_LOGIN:
|
||||||
|
from_secret: sonarScannerLogin
|
||||||
|
- name: Analyse
|
||||||
|
image: kytay/sonar-node-plugin
|
||||||
|
settings:
|
||||||
|
sonar_host:
|
||||||
|
from_secret: sonarScannerHostUrl
|
||||||
|
sonar_token:
|
||||||
|
from_secret: sonarScannerLogin
|
||||||
|
use_node_version: 16.18.1
|
||||||
|
sonar_debug: true
|
||||||
|
sonar_project_settings: ./sonar-project.properties
|
||||||
|
trigger:
|
||||||
|
event:
|
||||||
|
- push
|
||||||
|
|
||||||
|
---
|
||||||
|
kind: pipeline
|
||||||
|
type: docker
|
||||||
|
name: Publish
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: createTagAndChangelog
|
||||||
|
image: ubuntu:latest
|
||||||
|
commands:
|
||||||
|
- apt-get update && apt-get install -y git
|
||||||
|
- git fetch --tags -p
|
||||||
|
- VER=$(git describe --tags --abbrev=0)
|
||||||
|
- CHANGELOG=$(git log $VER..HEAD --pretty="- %s")
|
||||||
|
- echo "**$VER**\n\n$CHANGELOG\n\n--------------------------------------------------------------------\n\n$(cat CHANGELOG.md)" > CHANGELOG.md
|
||||||
|
- git add CHANGELOG.md
|
||||||
|
- git commit -m "Changelog for $VER [CI SKIP]"
|
||||||
|
- ./build.sh --publish --from-ci
|
||||||
|
- git remote add pushing https://$GITEA_USR:$GITEA_PASS@gitea.amine-louveau.fr/Louvorg/ReaderForSelfoss-multiplatform.git
|
||||||
|
- git push pushing master
|
||||||
|
- git push pushing --tags
|
||||||
|
environment:
|
||||||
|
TZ: Europe/Paris
|
||||||
|
GITEA_USR:
|
||||||
|
from_secret: giteaUsr
|
||||||
|
GITEA_PASS:
|
||||||
|
from_secret: giteaPass
|
||||||
|
|
||||||
|
- name: scpFiles
|
||||||
|
image: appleboy/drone-scp
|
||||||
|
settings:
|
||||||
|
host: amine-louveau.fr
|
||||||
|
username: ubuntu
|
||||||
|
key:
|
||||||
|
from_secret: privateKey
|
||||||
|
port: 22
|
||||||
|
target: /home/ubuntu/
|
||||||
|
source: version.txt
|
||||||
|
|
||||||
|
- name: deploy
|
||||||
|
image: appleboy/drone-ssh
|
||||||
|
settings:
|
||||||
|
host: amine-louveau.fr
|
||||||
|
user: ubuntu
|
||||||
|
key:
|
||||||
|
from_secret: privateKey
|
||||||
|
command_timeout: 2m
|
||||||
|
script:
|
||||||
|
- cd /home/ubuntu && sudo rm -rf /var/www/amine/version.txt && sudo chown www-data:www-data ./version.txt && sudo mv version.txt /var/www/amine/
|
||||||
|
|
||||||
|
trigger:
|
||||||
|
event:
|
||||||
|
- promote
|
||||||
|
target:
|
||||||
|
- production
|
||||||
|
|
||||||
|
---
|
||||||
|
kind: pipeline
|
||||||
|
type: docker
|
||||||
|
name: Release
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: build
|
||||||
|
image: mingc/android-build-box:latest
|
||||||
|
commands:
|
||||||
|
- echo "---------------------------------------------------------"
|
||||||
|
- echo "Fetch tags..."
|
||||||
|
- git fetch --tags
|
||||||
|
- echo "---------------------------------------------------------"
|
||||||
|
- echo "Configure gradle..."
|
||||||
|
- mkdir -p ~/.gradle && echo "org.gradle.daemon=false\nignoreGitVersion=false\npushCache=false\nsystemProp.org.gradle.internal.http.connectionTimeout=180000\nsystemProp.org.gradle.internal.http.socketTimeout=180000" >> ~/.gradle/gradle.properties
|
||||||
|
- echo "---------------------------------------------------------"
|
||||||
|
- echo "Generate APK"
|
||||||
|
- ./gradlew :androidApp:assembleGithubConfigRelease -P pushCache=false
|
||||||
|
- echo "---------------------------------------------------------"
|
||||||
|
- echo "Get Key"
|
||||||
|
- wget https://amine-louveau.fr/key
|
||||||
|
- echo "---------------------------------------------------------"
|
||||||
|
- echo "Zipalign"
|
||||||
|
- $ANDROID_HOME/build-tools/31.0.0/zipalign -f -v 4 androidApp/build/outputs/apk/githubConfig/release/androidApp-githubConfig-release-unsigned.apk androidApp/build/outputs/apk/githubConfig/release/android-prod-released-ziped.apk
|
||||||
|
- echo "---------------------------------------------------------"
|
||||||
|
- echo "Sign"
|
||||||
|
- $ANDROID_HOME/build-tools/31.0.0/apksigner sign -v --out signed.apk --ks ./key --ks-key-alias $YOUR_KEY_ALIAS --ks-pass pass:$YOUR_KEYSTORE_PASSWORD --v1-signing-enabled true --v2-signing-enabled true androidApp/build/outputs/apk/githubConfig/release/android-prod-released-ziped.apk
|
||||||
|
- echo "---------------------------------------------------------"
|
||||||
|
- echo "Verify"
|
||||||
|
- $ANDROID_HOME/build-tools/31.0.0/apksigner verify signed.apk
|
||||||
|
environment:
|
||||||
|
TZ: Europe/Paris
|
||||||
|
YOUR_KEYSTORE_PASSWORD:
|
||||||
|
from_secret: keyPass
|
||||||
|
YOUR_KEY_ALIAS:
|
||||||
|
from_secret: keyAlias
|
||||||
|
|
||||||
|
- name: gitea_release
|
||||||
|
image: plugins/gitea-release
|
||||||
|
settings:
|
||||||
|
api_key:
|
||||||
|
from_secret: giteaAPI
|
||||||
|
base_url: https://gitea.amine-louveau.fr
|
||||||
|
files: signed.apk
|
||||||
|
trigger:
|
||||||
|
event:
|
||||||
|
- tag
|
@ -1,36 +0,0 @@
|
|||||||
root = true
|
|
||||||
|
|
||||||
[*]
|
|
||||||
insert_final_newline = true
|
|
||||||
|
|
||||||
[.editorconfig]
|
|
||||||
insert_final_newline = false
|
|
||||||
ij_kotlin_line_break_after_multiline_when_entry = false
|
|
||||||
|
|
||||||
[*.{kt,kts}]
|
|
||||||
# Disable wildcard imports entirely
|
|
||||||
ij_kotlin_name_count_to_use_star_import = 2147483647
|
|
||||||
ij_kotlin_name_count_to_use_star_import_for_members = 2147483647
|
|
||||||
end_of_line = lf
|
|
||||||
ij_kotlin_allow_trailing_comma = true
|
|
||||||
ij_kotlin_allow_trailing_comma_on_call_site = true
|
|
||||||
ij_kotlin_imports_layout = *, java.**, javax.**, kotlin.**, ^
|
|
||||||
ij_kotlin_indent_before_arrow_on_new_line = false
|
|
||||||
ij_kotlin_line_break_after_multiline_when_entry = true
|
|
||||||
ij_kotlin_packages_to_use_import_on_demand = unset
|
|
||||||
indent_size = 4
|
|
||||||
indent_style = space
|
|
||||||
ktlint_argument_list_wrapping_ignore_when_parameter_count_greater_or_equal_than = unset
|
|
||||||
ktlint_chain_method_rule_force_multiline_when_chain_operator_count_greater_or_equal_than = 4
|
|
||||||
ktlint_class_signature_rule_force_multiline_when_parameter_count_greater_or_equal_than = 1
|
|
||||||
ktlint_code_style = ktlint_official
|
|
||||||
ktlint_enum_entry_name_casing = upper_or_camel_cases
|
|
||||||
ktlint_function_naming_ignore_when_annotated_with = unset
|
|
||||||
ktlint_function_signature_body_expression_wrapping = multiline
|
|
||||||
ktlint_function_signature_rule_force_multiline_when_parameter_count_greater_or_equal_than = 2
|
|
||||||
ktlint_ignore_back_ticked_identifier = false
|
|
||||||
ktlint_property_naming_constant_naming = screaming_snake_case
|
|
||||||
max_line_length = 140
|
|
||||||
|
|
||||||
[**/build]
|
|
||||||
ktlint = disabled
|
|
@ -1,10 +0,0 @@
|
|||||||
version: '3'
|
|
||||||
services:
|
|
||||||
selfoss:
|
|
||||||
container_name: selfoss
|
|
||||||
image: rsprta/selfoss
|
|
||||||
network_mode: "host"
|
|
||||||
ports:
|
|
||||||
- "8888:8888"
|
|
||||||
|
|
||||||
|
|
@ -1,47 +0,0 @@
|
|||||||
name: Build
|
|
||||||
on:
|
|
||||||
workflow_call:
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
BuildAndTestAndCoverage:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Check out repository code
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
- name: Fetch tags
|
|
||||||
run: git fetch --tags -p
|
|
||||||
- uses: actions/setup-java@v4
|
|
||||||
with:
|
|
||||||
distribution: 'temurin'
|
|
||||||
java-version: '17'
|
|
||||||
cache: gradle
|
|
||||||
- uses: gradle/actions/setup-gradle@v3
|
|
||||||
- uses: android-actions/setup-android@v3
|
|
||||||
- name: Configure gradle...
|
|
||||||
run: mkdir -p ~/.gradle && echo "org.gradle.daemon=false\nignoreGitVersion=true" >> ~/.gradle/gradle.properties
|
|
||||||
- name: Build and test
|
|
||||||
run: ./gradlew build -x testReleaseUnitTest -x testDebugUnitTest -x testGithubConfigReleaseUnitTest -x testGithubConfigDebugUnitTest # These tests will be done
|
|
||||||
- uses: KengoTODA/actions-setup-docker-compose@v1
|
|
||||||
with:
|
|
||||||
version: "2.23.3"
|
|
||||||
# TESTS ARE RUN LOCALLY
|
|
||||||
# - name: run selfoss
|
|
||||||
# run: |
|
|
||||||
# docker compose -f .gitea/workflows/assets/docker-compose.yml up -d
|
|
||||||
- name: coverage
|
|
||||||
run: |
|
|
||||||
./gradlew :koverHtmlReport
|
|
||||||
- uses: actions/upload-artifact@v3
|
|
||||||
with:
|
|
||||||
name: coverage
|
|
||||||
path: build/reports/kover/html
|
|
||||||
retention-days: 1
|
|
||||||
overwrite: true
|
|
||||||
include-hidden-files: true
|
|
||||||
# TESTS ARE RUN LOCALLY
|
|
||||||
# - name: Clean
|
|
||||||
# if: always()
|
|
||||||
# run: |
|
|
||||||
# docker compose -f .gitea/workflows/assets/docker-compose.yml stop
|
|
@ -1,128 +0,0 @@
|
|||||||
name: Create tag
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- release
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
uses: ./.gitea/workflows/common_build.yml
|
|
||||||
createTagAndChangelog:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs: build
|
|
||||||
steps:
|
|
||||||
- name: Check out repository code
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
ref: master
|
|
||||||
- name: Config git
|
|
||||||
run: |
|
|
||||||
git config --global user.email aminecmi+giteadrone@pm.me
|
|
||||||
git config --global user.name giteadrone
|
|
||||||
- name: Creating the tag and generate changelog
|
|
||||||
run: |
|
|
||||||
git fetch --tags -p
|
|
||||||
PREV=$(git describe --tags --abbrev=0)
|
|
||||||
./build.sh --publish --from-ci
|
|
||||||
VER=$(git describe --tags --abbrev=0)
|
|
||||||
CHANGELOG=$(git log $PREV..HEAD --pretty="- %s")
|
|
||||||
echo "**$VER
|
|
||||||
|
|
||||||
$CHANGELOG
|
|
||||||
|
|
||||||
--------------------------------------------------------------------
|
|
||||||
|
|
||||||
$(cat CHANGELOG.md)" > CHANGELOG.md
|
|
||||||
git add CHANGELOG.md
|
|
||||||
touch ./fastlane/metadata/android/en\-US/changelogs/$VER.txt
|
|
||||||
echo "**$VER**
|
|
||||||
|
|
||||||
$CHANGELOG" > ./fastlane/metadata/android/en\-US/changelogs/$VER.txt
|
|
||||||
git add ./fastlane/metadata/android/en\-US/changelogs/$VER.txt
|
|
||||||
git commit -m "Changelog for $VER"
|
|
||||||
- name: Push changes
|
|
||||||
uses: appleboy/git-push-action@v1.0.0
|
|
||||||
with:
|
|
||||||
author_name: giteadrone
|
|
||||||
author_email: aminecmi+giteadrone@pm.me
|
|
||||||
remote: ${{ secrets.REMOTE_URL }}
|
|
||||||
followtags: true
|
|
||||||
ssh_key: ${{ secrets.PRIVATE_KEY }}
|
|
||||||
tags: true
|
|
||||||
branch: master
|
|
||||||
- name: copy file via ssh password
|
|
||||||
uses: appleboy/scp-action@v0.1.7
|
|
||||||
with:
|
|
||||||
host: amine-bouabdallaoui.fr
|
|
||||||
username: ubuntu
|
|
||||||
key: ${{ secrets.PRIVATE_KEY }}
|
|
||||||
source: "version.txt"
|
|
||||||
target: "/home/ubuntu/"
|
|
||||||
- name: deploy version file
|
|
||||||
uses: appleboy/ssh-action@v1.2.0
|
|
||||||
with:
|
|
||||||
host: amine-bouabdallaoui.fr
|
|
||||||
username: ubuntu
|
|
||||||
key: ${{ secrets.PRIVATE_KEY }}
|
|
||||||
script: cd /home/ubuntu && sudo rm -rf /var/www/amine/version.txt && sudo chown www-data:www-data ./version.txt && sudo mv version.txt /var/www/amine/
|
|
||||||
release:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs: createTagAndChangelog
|
|
||||||
steps:
|
|
||||||
- name: Check out repository code
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
- name: Fetch tags
|
|
||||||
id: version
|
|
||||||
run: |
|
|
||||||
git fetch --tags -p
|
|
||||||
PREV=$(git describe --tags --abbrev=0)
|
|
||||||
echo $PREV
|
|
||||||
echo "VERSION=$PREV" >> $GITHUB_OUTPUT
|
|
||||||
- uses: actions/setup-java@v4
|
|
||||||
with:
|
|
||||||
distribution: 'temurin'
|
|
||||||
java-version: '17'
|
|
||||||
cache: gradle
|
|
||||||
- name: Setup Android SDK
|
|
||||||
uses: android-actions/setup-android@v3
|
|
||||||
- name: Configure gradle...
|
|
||||||
run: mkdir -p ~/.gradle && echo "org.gradle.daemon=false\nignoreGitVersion=false\nsystemProp.org.gradle.internal.http.connectionTimeout=180000\nsystemProp.org.gradle.internal.http.socketTimeout=180000" >> ~/.gradle/gradle.properties
|
|
||||||
- name: setup go
|
|
||||||
uses: https://github.com/actions/setup-go@v4
|
|
||||||
with:
|
|
||||||
go-version: '>=1.20.1'
|
|
||||||
- name: Generate APK
|
|
||||||
run: ./gradlew :androidApp:assembleGithubConfigRelease
|
|
||||||
- name: Get Key
|
|
||||||
run: wget ${{ secrets.KEY_URL }}
|
|
||||||
- name: Zippalign
|
|
||||||
run: |
|
|
||||||
sdkmanager "build-tools;31.0.0"
|
|
||||||
ls $ANDROID_HOME/build-tools
|
|
||||||
$ANDROID_HOME/build-tools/31.0.0/zipalign -f -v 4 androidApp/build/outputs/apk/githubConfig/release/androidApp-githubConfig-release-unsigned.apk androidApp/build/outputs/apk/githubConfig/release/android-prod-released-ziped.apk
|
|
||||||
- name: Sigh
|
|
||||||
run: $ANDROID_HOME/build-tools/31.0.0/apksigner sign -v --out signed.apk --ks ./key --ks-key-alias ${{ secrets.KEY_ALIAS }} --ks-pass pass:${{ secrets.KEYSTORE_PASSWORD }} --v1-signing-enabled true --v2-signing-enabled true androidApp/build/outputs/apk/githubConfig/release/android-prod-released-ziped.apk
|
|
||||||
- name: Verify
|
|
||||||
run: $ANDROID_HOME/build-tools/31.0.0/apksigner verify signed.apk
|
|
||||||
- name: Release
|
|
||||||
uses: https://gitea.com/actions/gitea-release-action@main
|
|
||||||
with:
|
|
||||||
files: signed.apk
|
|
||||||
token: ${{ secrets.API_KEY }}
|
|
||||||
tag_name: ${{ steps.version.outputs.VERSION }}
|
|
||||||
name: ${{ steps.version.outputs.VERSION }}
|
|
||||||
- name: Send mail
|
|
||||||
uses: https://github.com/dawidd6/action-send-mail@v4
|
|
||||||
with:
|
|
||||||
connection_url: ${{ secrets.MAIL_CONNECTION }}
|
|
||||||
to: ${{ secrets.MAIL_TO }}
|
|
||||||
from: ${{ secrets.MAIL_FROM }}
|
|
||||||
subject: Mapping file
|
|
||||||
priority: high
|
|
||||||
convert_markdown: true
|
|
||||||
body: Nouveau fichier de mapping pour la version ${{ steps.version.outputs.VERSION }}
|
|
||||||
attachments: androidApp/build/outputs/mapping/githubConfigRelease/mapping.txt
|
|
@ -1,28 +0,0 @@
|
|||||||
name: Check PR code
|
|
||||||
on:
|
|
||||||
pull_request:
|
|
||||||
branches:
|
|
||||||
- master
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
Lint:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Check out repository code
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
- uses: actions/setup-java@v4
|
|
||||||
with:
|
|
||||||
distribution: 'temurin'
|
|
||||||
java-version: '17'
|
|
||||||
cache: gradle
|
|
||||||
- name: Install klint
|
|
||||||
run: curl -sSLO https://github.com/pinterest/ktlint/releases/download/1.5.0/ktlint && chmod a+x ktlint && mv ktlint /usr/local/bin/
|
|
||||||
- name: Install detekt
|
|
||||||
run: curl -sSLO https://github.com/detekt/detekt/releases/download/v1.23.7/detekt-cli-1.23.7.zip && unzip detekt-cli-1.23.7.zip
|
|
||||||
- name: Linting...
|
|
||||||
run: ktlint 'shared/**/*.kt' 'androidApp/**/*.kt' '!shared/build'
|
|
||||||
- name: Detecting...
|
|
||||||
run: ./detekt-cli-1.23.7/bin/detekt-cli -c detekt.yml --excludes '**/shared/build/**/*.kt'
|
|
||||||
build:
|
|
||||||
needs: Lint
|
|
||||||
uses: ./.gitea/workflows/common_build.yml
|
|
@ -1,9 +0,0 @@
|
|||||||
name: Check master code
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- master
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
uses: ./.gitea/workflows/common_build.yml
|
|
2
.github/CONTRIBUTING.md
vendored
2
.github/CONTRIBUTING.md
vendored
@ -10,7 +10,7 @@ Please read the guidelines before contributing, and follow them (or try to) when
|
|||||||
|
|
||||||
There are many ways to contribute to this project, you could [translate the app](https://crowdin.com/project/readerforselfoss), report bugs, request missing features, suggest enhancements and changes to existing ones. You also can improve the README with useful tips that could help the other users.
|
There are many ways to contribute to this project, you could [translate the app](https://crowdin.com/project/readerforselfoss), report bugs, request missing features, suggest enhancements and changes to existing ones. You also can improve the README with useful tips that could help the other users.
|
||||||
|
|
||||||
You can fork the repository, and [help me solve some issues](https://gitea.amine-bouabdallaoui.fr/Louvorg/ReaderForSelfoss-multiplatform/issues?q=is%3Aissue+is%3Aopen+label%3A%22Up+For+Grabs%22) or [develop new things](https://gitea.amine-bouabdallaoui.fr/Louvorg/ReaderForSelfoss-multiplatform/issues)
|
You can fork the repository, and [help me solve some issues](https://gitea.amine-louveau.fr/Louvorg/ReaderForSelfoss-multiplatform/issues?q=is%3Aissue+is%3Aopen+label%3A%22Up+For+Grabs%22) or [develop new things](https://gitea.amine-louveau.fr/Louvorg/ReaderForSelfoss-multiplatform/issues)
|
||||||
|
|
||||||
### What I can't help you with.
|
### What I can't help you with.
|
||||||
|
|
||||||
|
5
.gitignore
vendored
5
.gitignore
vendored
@ -321,8 +321,3 @@ fabric.properties
|
|||||||
|
|
||||||
|
|
||||||
crowdin.properties
|
crowdin.properties
|
||||||
|
|
||||||
.kotlin/
|
|
||||||
build-cache/
|
|
||||||
|
|
||||||
act
|
|
||||||
|
381
CHANGELOG.md
381
CHANGELOG.md
@ -1,384 +1,3 @@
|
|||||||
**v125020471
|
|
||||||
|
|
||||||
- chore: no more docker-compose.
|
|
||||||
- bump: gradle plugin.
|
|
||||||
- Merge pull request 'fix: check index exists.' (#183) from fix-index into master
|
|
||||||
- fix: check index exists.
|
|
||||||
- Changelog for v125020411
|
|
||||||
|
|
||||||
--------------------------------------------------------------------
|
|
||||||
|
|
||||||
**v125020411
|
|
||||||
|
|
||||||
- Merge pull request 'bump' (#182) from bump into master
|
|
||||||
- chore: non transiant R classes.
|
|
||||||
- Merge pull request 'fix: One more missing context.' (#181) from fix-one-more-context into master
|
|
||||||
- bump
|
|
||||||
- fix: One more missing context.
|
|
||||||
|
|
||||||
--------------------------------------------------------------------
|
|
||||||
|
|
||||||
**v125010241
|
|
||||||
|
|
||||||
- Merge pull request 'fix: Link not opening.' (#178) from fix-open-link into master
|
|
||||||
- refactor: context fragments issues.
|
|
||||||
- logs: Context issues.
|
|
||||||
- fix: Handle empty url issue, again.
|
|
||||||
- fix: Link not opening.
|
|
||||||
- Changelog for v125010201
|
|
||||||
|
|
||||||
--------------------------------------------------------------------
|
|
||||||
|
|
||||||
**v125010201
|
|
||||||
|
|
||||||
- fix: Handle empty url issue.
|
|
||||||
- Merge pull request 'Removed the floating bar.' (#177) from floating-bar into master
|
|
||||||
- chore: changing actions in reader fragment.
|
|
||||||
- Changelog for v125010131
|
|
||||||
|
|
||||||
--------------------------------------------------------------------
|
|
||||||
|
|
||||||
**v125010131
|
|
||||||
|
|
||||||
- fix: reload the adapter when it's needed. Fixes #128. (#176)
|
|
||||||
- feat: basic auth and images loading. Fixes #172. (#175)
|
|
||||||
- Changelog for v125010111
|
|
||||||
|
|
||||||
--------------------------------------------------------------------
|
|
||||||
|
|
||||||
**v125010111
|
|
||||||
|
|
||||||
- Debug trying to fix context issues. (#174)
|
|
||||||
- Changelog for v125010031
|
|
||||||
|
|
||||||
--------------------------------------------------------------------
|
|
||||||
|
|
||||||
**v125010031
|
|
||||||
|
|
||||||
- Merge pull request 'Bump dependencies' (#173) from upgarde into master
|
|
||||||
- chore: "faster" action.
|
|
||||||
- fastlane: icon change.
|
|
||||||
- chore: ignoring a pixel issue.
|
|
||||||
- test: fixed an ui test issue.
|
|
||||||
- fix: center the loading thing.
|
|
||||||
- test: items displaying.
|
|
||||||
- bump: sqldelight.
|
|
||||||
- bump: material, desugar jdk, jsoup, kodein, settings, napier, mock.
|
|
||||||
- bump: androix and coroutines.
|
|
||||||
- bump: ktor. Closes #67.
|
|
||||||
- Changelog for v124123651
|
|
||||||
|
|
||||||
--------------------------------------------------------------------
|
|
||||||
|
|
||||||
**v124123651
|
|
||||||
|
|
||||||
- Merge pull request 'Bugfixes' (#171) from bugfixes into master
|
|
||||||
- config: crowdin
|
|
||||||
- chore: can links be empty ?
|
|
||||||
- fix: Context issues in article fragment.
|
|
||||||
- fix: Context issues in fragment sheet.
|
|
||||||
- fix: build.
|
|
||||||
- chore: compile issue fix.
|
|
||||||
- chore: filter some bugs.
|
|
||||||
- bugfix: catch users using something other than selfoss.
|
|
||||||
- bugfix: No browser, no link.
|
|
||||||
- translations
|
|
||||||
- chore: remove log.
|
|
||||||
- translation
|
|
||||||
- Changelog for v124123641
|
|
||||||
|
|
||||||
--------------------------------------------------------------------
|
|
||||||
|
|
||||||
**v124123641
|
|
||||||
|
|
||||||
- Chore: no tests on build.
|
|
||||||
- Merge pull request 'testing' (#170) from testing into master
|
|
||||||
- fix: Displaying fixes. Fixes #155
|
|
||||||
- test: coverage
|
|
||||||
- chore: update and use multiplatform datetime
|
|
||||||
- Changelog for v124123421
|
|
||||||
|
|
||||||
--------------------------------------------------------------------
|
|
||||||
|
|
||||||
**v124123421
|
|
||||||
|
|
||||||
- fix: Trying to fix the serialization issue.
|
|
||||||
- Changelog for v124113311
|
|
||||||
|
|
||||||
--------------------------------------------------------------------
|
|
||||||
|
|
||||||
**v124113311
|
|
||||||
|
|
||||||
- chore: update versions. (#165)
|
|
||||||
- chore: fastlane changelog.
|
|
||||||
- chore: fastlane fixes.
|
|
||||||
- Changelog for v124113301
|
|
||||||
|
|
||||||
--------------------------------------------------------------------
|
|
||||||
|
|
||||||
**v124113301**
|
|
||||||
|
|
||||||
- chore: Gitea Action
|
|
||||||
- Merge pull request 'chore: Gitea Action' (#164) from runner into master
|
|
||||||
- chore: Gitea Action
|
|
||||||
- chore: Readme update.
|
|
||||||
|
|
||||||
--------------------------------------------------------------------
|
|
||||||
|
|
||||||
**v124041081**
|
|
||||||
|
|
||||||
- chore: comment.
|
|
||||||
- fix: Last time fixing the parsing date hack before moving it to os version.
|
|
||||||
- Changelog for v124030731 [CI SKIP]
|
|
||||||
|
|
||||||
--------------------------------------------------------------------
|
|
||||||
|
|
||||||
**v124030731**
|
|
||||||
|
|
||||||
- fix: Basic auth and password can have non whitspace characters. Fixes 142.
|
|
||||||
- Changelog for v124020451 [CI SKIP]
|
|
||||||
|
|
||||||
--------------------------------------------------------------------
|
|
||||||
|
|
||||||
**v124020451**
|
|
||||||
|
|
||||||
- fix: Fixed handling of position in card adapter.
|
|
||||||
- Changelog for v124010301 [CI SKIP]
|
|
||||||
|
|
||||||
--------------------------------------------------------------------
|
|
||||||
|
|
||||||
**v124010301**
|
|
||||||
|
|
||||||
- fix: This may fix the oom errors.
|
|
||||||
- Changelog for v124010191 [CI SKIP]
|
|
||||||
|
|
||||||
--------------------------------------------------------------------
|
|
||||||
|
|
||||||
**v124010191**
|
|
||||||
|
|
||||||
- fix: moving listeners.
|
|
||||||
- chore: removed a useless log.
|
|
||||||
- Changelog for v124010032 [CI SKIP]
|
|
||||||
|
|
||||||
--------------------------------------------------------------------
|
|
||||||
|
|
||||||
**v124010032**
|
|
||||||
|
|
||||||
- fix: Another date format thing.
|
|
||||||
- Changelog for v124010031 [CI SKIP]
|
|
||||||
|
|
||||||
--------------------------------------------------------------------
|
|
||||||
|
|
||||||
**v124010031**
|
|
||||||
|
|
||||||
- fix: Checking if selfoss instance.
|
|
||||||
- fix: handle three characters lenght hexcode colors.
|
|
||||||
- Changelog for v123113311 [CI SKIP]
|
|
||||||
|
|
||||||
--------------------------------------------------------------------
|
|
||||||
|
|
||||||
**v123113311**
|
|
||||||
|
|
||||||
- chore: Source tracker url in the menu.
|
|
||||||
- fix: Handle kodein proguard rules.
|
|
||||||
- Changelog for v123102961 [CI SKIP]
|
|
||||||
|
|
||||||
--------------------------------------------------------------------
|
|
||||||
|
|
||||||
**v123102961**
|
|
||||||
|
|
||||||
- chore: domain changes.
|
|
||||||
- Changelog for v123102852 [CI SKIP]
|
|
||||||
|
|
||||||
--------------------------------------------------------------------
|
|
||||||
|
|
||||||
**v123102852**
|
|
||||||
|
|
||||||
- chore: lint cleaning.
|
|
||||||
- Changelog for v123102841 [CI SKIP]
|
|
||||||
|
|
||||||
--------------------------------------------------------------------
|
|
||||||
|
|
||||||
**v123102841**
|
|
||||||
|
|
||||||
- chore: cleaning ci steps and upgrading dependencies.
|
|
||||||
- feat: Self signed ssl support.
|
|
||||||
- Changelog for v123061811 [CI SKIP]
|
|
||||||
|
|
||||||
--------------------------------------------------------------------
|
|
||||||
|
|
||||||
**v123061811**
|
|
||||||
|
|
||||||
- feat: Added confirmation dialog for disconnect item menu.
|
|
||||||
- Changelog for v123061651 [CI SKIP]
|
|
||||||
|
|
||||||
--------------------------------------------------------------------
|
|
||||||
|
|
||||||
**v123061651**
|
|
||||||
|
|
||||||
- i18n: Translation update.
|
|
||||||
- i18n: Translation update.
|
|
||||||
- i18n: Translation update.
|
|
||||||
- fix: avoid trying to open invalid image urls.
|
|
||||||
- Changelog for v123051471 [CI SKIP]
|
|
||||||
|
|
||||||
--------------------------------------------------------------------
|
|
||||||
|
|
||||||
**v123051471**
|
|
||||||
|
|
||||||
- fix: images could be null.
|
|
||||||
- fix: Check if color is not empty before parsing it.
|
|
||||||
- chore: Removed unused log.
|
|
||||||
- Changelog for v123051331 [CI SKIP]
|
|
||||||
|
|
||||||
--------------------------------------------------------------------
|
|
||||||
|
|
||||||
**v123051331**
|
|
||||||
|
|
||||||
- fix: illegal input.
|
|
||||||
- Changelog for v123051321 [CI SKIP]
|
|
||||||
|
|
||||||
--------------------------------------------------------------------
|
|
||||||
|
|
||||||
**v123051321**
|
|
||||||
|
|
||||||
- debug: Debug null context.
|
|
||||||
- Changelog for v123051301 [CI SKIP]
|
|
||||||
|
|
||||||
--------------------------------------------------------------------
|
|
||||||
|
|
||||||
**v123051301**
|
|
||||||
|
|
||||||
- feat: Basic auth from url. Fixes #142 (#143)
|
|
||||||
- debug: Debug index out of bound exception.
|
|
||||||
- Changelog for v123051211 [CI SKIP]
|
|
||||||
|
|
||||||
--------------------------------------------------------------------
|
|
||||||
|
|
||||||
**v123051211**
|
|
||||||
|
|
||||||
- fix: Sometimes url isn't even defined.
|
|
||||||
- Changelog for v123041021 [CI SKIP]
|
|
||||||
|
|
||||||
--------------------------------------------------------------------
|
|
||||||
|
|
||||||
**v123041021**
|
|
||||||
|
|
||||||
- fix: 'Enable Core Library Desugaring to support older Android versions' (#138) from davidoskky/ReaderForSelfoss-multiplatform:desugaring into master
|
|
||||||
- Enable Core Library Desugaring to support older Android versions
|
|
||||||
- Changelog for v123030851 [CI SKIP]
|
|
||||||
|
|
||||||
--------------------------------------------------------------------
|
|
||||||
|
|
||||||
**v123030851**
|
|
||||||
|
|
||||||
- chore: replace textDrawable library (#136)
|
|
||||||
- refactor: Remove slow login check. Closes #135.
|
|
||||||
- ci: send the mapping file after a release.
|
|
||||||
- Changelog for v123030751 [CI SKIP]
|
|
||||||
|
|
||||||
--------------------------------------------------------------------
|
|
||||||
|
|
||||||
**v123030751**
|
|
||||||
|
|
||||||
- debug: added a lot to pinpoint the url issue.
|
|
||||||
- feat: Use /sources/stats in the home (#133)
|
|
||||||
- Changelog for v123030681 [CI SKIP]
|
|
||||||
|
|
||||||
--------------------------------------------------------------------
|
|
||||||
|
|
||||||
**v123030681**
|
|
||||||
|
|
||||||
- fix: Unread and starred can be null.
|
|
||||||
- Fixed version number issue.
|
|
||||||
- Changelog for v123030621 [CI SKIP]
|
|
||||||
|
|
||||||
--------------------------------------------------------------------
|
|
||||||
|
|
||||||
**v123030621**
|
|
||||||
|
|
||||||
- fix: url required issue.
|
|
||||||
- fix: Canvas reused issue.
|
|
||||||
- Changelog for v123020572 [CI SKIP]
|
|
||||||
|
|
||||||
--------------------------------------------------------------------
|
|
||||||
|
|
||||||
**v123020572**
|
|
||||||
|
|
||||||
- fix: requirecontext issues ?
|
|
||||||
- debug: activity not found exception.
|
|
||||||
- Changelog for v123020571 [CI SKIP]
|
|
||||||
|
|
||||||
--------------------------------------------------------------------
|
|
||||||
|
|
||||||
**v123020571**
|
|
||||||
|
|
||||||
- chore: remove errors logging.
|
|
||||||
- fix: quickfix for url param not provided for some sources.
|
|
||||||
- Update 'CHANGELOG.md'
|
|
||||||
- Changelog for v123020523 [CI SKIP]
|
|
||||||
|
|
||||||
--------------------------------------------------------------------
|
|
||||||
|
|
||||||
**v123020523**
|
|
||||||
|
|
||||||
- fix: Git changelog.
|
|
||||||
|
|
||||||
--------------------------------------------------------------------
|
|
||||||
|
|
||||||
**v123020491**
|
|
||||||
|
|
||||||
- fix: Fixed acra bug reporting.
|
|
||||||
|
|
||||||
--------------------------------------------------------------------
|
|
||||||
|
|
||||||
**v123010301**
|
|
||||||
|
|
||||||
- Chore: acra config.
|
|
||||||
|
|
||||||
--------------------------------------------------------------------
|
|
||||||
|
|
||||||
**v123010281**
|
|
||||||
|
|
||||||
- improvement: Improve right to left support (#130) Co-authored-by: davidoskky <davidoskky@hidden.hidden> Co-committed-by: davidoskky <davidoskky@hidden.hidden>
|
|
||||||
|
|
||||||
--------------------------------------------------------------------
|
|
||||||
|
|
||||||
**v123010261**
|
|
||||||
|
|
||||||
- feat: Handle public instances (#126) Co-authored-by: davidoskky <davidoskky@hidden.hidden> Co-committed-by: davidoskky <davidoskky@hidden.hidden>
|
|
||||||
- ci: Pull request should trigger ci.
|
|
||||||
- fix: Complete the disconnection before redirecting to the login screen
|
|
||||||
- Complete the disconnection before redirecting to the login screen
|
|
||||||
|
|
||||||
--------------------------------------------------------------------
|
|
||||||
|
|
||||||
**v123010241**
|
|
||||||
|
|
||||||
- Merge pull request 'feat: swipe down to close images' (#122) from davidoskky/ReaderForSelfoss-multiplatform:swipe_down into master
|
|
||||||
- Remove unnecessary definition
|
|
||||||
- Remove unused import
|
|
||||||
- Adjust the image closing animation
|
|
||||||
- Add a dark hue to the underlying article when swiping to close images
|
|
||||||
- Rename activity style to avoid interferences
|
|
||||||
- Adapt the style of the image activity to the rest of the application
|
|
||||||
- Resolve issues when swiping down to close images
|
|
||||||
- Close the image fragment only if the image has been dragged down
|
|
||||||
- Animate swipe down to close images
|
|
||||||
- Swipe down to close images
|
|
||||||
|
|
||||||
--------------------------------------------------------------------
|
|
||||||
|
|
||||||
**v123010041**
|
|
||||||
|
|
||||||
- Merge pull request 'scroll-tag-filters' (#124) from scroll-tag-filters into master
|
|
||||||
- fix: added POST_NOTIFICATIONS to fix notifications issues.
|
|
||||||
- fix: scrollable filter sheet.
|
|
||||||
- enhancement: Ellipsize chips text.
|
|
||||||
- Cleaning.
|
|
||||||
|
|
||||||
--------------------------------------------------------------------
|
|
||||||
|
|
||||||
**v122123641**
|
**v122123641**
|
||||||
|
|
||||||
- feat: Disable the failing source in the filter sheet.
|
- feat: Disable the failing source in the filter sheet.
|
||||||
|
16
README.md
16
README.md
@ -1,4 +1,4 @@
|
|||||||
# ReaderForSelfoss-multiplatform [](https://gitea.amine-bouabdallaoui.fr/Louvorg/ReaderForSelfoss-multiplatform/actions?workflow=on_push.yml&actor=0&status=0)
|
# ReaderForSelfoss-multiplatform [](https://build.amine-louveau.fr/Louvorg/ReaderForSelfoss-multiplatform)
|
||||||
|
|
||||||
[](https://crowdin.com/project/readerforselfoss)
|
[](https://crowdin.com/project/readerforselfoss)
|
||||||
|
|
||||||
@ -10,6 +10,10 @@ If you are a user, you can still create new issues. I'll fix them when I can.
|
|||||||
|
|
||||||
<a href="https://f-droid.org/packages/bou.amine.apps.readerforselfossv2.android"><img src="https://f-droid.org/badge/get-it-on.png" alt="Get it on F-Droid" height="100"></a>
|
<a href="https://f-droid.org/packages/bou.amine.apps.readerforselfossv2.android"><img src="https://f-droid.org/badge/get-it-on.png" alt="Get it on F-Droid" height="100"></a>
|
||||||
|
|
||||||
|
## Screen captures
|
||||||
|
|
||||||
|
<img src="res//fr-card.png?raw=true" alt="card view" width="400"/> <img src="res//fr-list.png?raw=true" alt="list view" width="400"/>
|
||||||
|
|
||||||
## Like my app ?
|
## Like my app ?
|
||||||
|
|
||||||
<a href="https://www.buymeacoffee.com/aminecmi" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/lato-orange.png" alt="Buy Me A Coffee" style="height: 51px !important;width: 217px !important;" ></a>
|
<a href="https://www.buymeacoffee.com/aminecmi" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/lato-orange.png" alt="Buy Me A Coffee" style="height: 51px !important;width: 217px !important;" ></a>
|
||||||
@ -18,15 +22,15 @@ If you are a user, you can still create new issues. I'll fix them when I can.
|
|||||||
|
|
||||||
1. **You'll have to have a Selfoss instance running.** You'll find everything you need to install it [here](https://selfoss.aditu.de/).
|
1. **You'll have to have a Selfoss instance running.** You'll find everything you need to install it [here](https://selfoss.aditu.de/).
|
||||||
|
|
||||||
2. Check the [Contribution guide](https://gitea.amine-bouabdallaoui.fr/Louvorg/ReaderforSelfoss-multiplatform/src/branch/master/.github/CONTRIBUTING.md).
|
2. Check the [Contribution guide](https://gitea.amine-louveau.fr/Louvorg/ReaderforSelfoss-multiplatform/src/branch/master/.github/CONTRIBUTING.md).
|
||||||
|
|
||||||
3. Build the project by following [these steps](https://gitea.amine-bouabdallaoui.fr/Louvorg/ReaderforSelfoss-multiplatform/src/branch/master/.github/CONTRIBUTING.md#build-the-project) (you should have read them after the contribution guide)
|
3. Build the project by following [these steps](https://gitea.amine-louveau.fr/Louvorg/ReaderforSelfoss-multiplatform/src/branch/master/.github/CONTRIBUTING.md#build-the-project) (you should have read them after the contribution guide)
|
||||||
|
|
||||||
## Useful links
|
## Useful links
|
||||||
|
|
||||||
- [Check what changed](https://gitea.amine-bouabdallaoui.fr/Louvorg/ReaderforSelfoss-multiplatform/src/branch/master/CHANGELOG.md)
|
- [Check what changed](https://gitea.amine-louveau.fr/Louvorg/ReaderforSelfoss-multiplatform/src/branch/master/CHANGELOG.md)
|
||||||
- [See what I'm doing](https://gitea.amine-bouabdallaoui.fr/Louvorg/ReaderforSelfoss-multiplatform/projects/1)
|
- [See what I'm doing](https://gitea.amine-louveau.fr/Louvorg/ReaderforSelfoss-multiplatform/projects/1)
|
||||||
- [Create an issue, or request a new feature](https://gitea.amine-bouabdallaoui.fr/Louvorg/ReaderforSelfoss-multiplatform/issues)
|
- [Create an issue, or request a new feature](https://gitea.amine-louveau.fr/Louvorg/ReaderforSelfoss-multiplatform/issues)
|
||||||
- [Help translation the app](https://crowdin.com/project/readerforselfoss)
|
- [Help translation the app](https://crowdin.com/project/readerforselfoss)
|
||||||
|
|
||||||
## Contributors (V1) (Alphabetical order) ❤️
|
## Contributors (V1) (Alphabetical order) ❤️
|
||||||
|
1
androidApp/.gitignore
vendored
1
androidApp/.gitignore
vendored
@ -1,2 +1 @@
|
|||||||
/build
|
/build
|
||||||
.kotlin/
|
|
@ -1,23 +1,22 @@
|
|||||||
import java.io.ByteArrayOutputStream
|
import java.io.ByteArrayOutputStream
|
||||||
|
|
||||||
val ignoreGitVersion: String by project
|
val ignoreGitVersion: String by project
|
||||||
val acraVersion = "5.12.0"
|
val acraVersion = "5.9.7"
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
id("com.android.application")
|
id("com.android.application")
|
||||||
kotlin("android")
|
kotlin("android")
|
||||||
kotlin("kapt")
|
kotlin("kapt")
|
||||||
id("com.mikepenz.aboutlibraries.plugin")
|
id("com.mikepenz.aboutlibraries.plugin")
|
||||||
id("org.jetbrains.kotlinx.kover")
|
id("org.jetbrains.kotlinx.kover") version "0.6.1"
|
||||||
id("app.cash.sqldelight") version "2.0.2"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Project.execWithOutput(cmd: String, ignore: Boolean = false): String {
|
fun Project.execWithOutput(cmd: String, ignore: Boolean = false): String {
|
||||||
val result: String = ByteArrayOutputStream().use { outputStream ->
|
var result: String = ByteArrayOutputStream().use { outputStream ->
|
||||||
project.exec {
|
project.exec {
|
||||||
commandLine = cmd.split(" ")
|
commandLine = cmd.split(" ")
|
||||||
standardOutput = outputStream
|
standardOutput = outputStream
|
||||||
isIgnoreExitValue = ignore
|
isIgnoreExitValue = ignore ?: false
|
||||||
}
|
}
|
||||||
outputStream.toString()
|
outputStream.toString()
|
||||||
}
|
}
|
||||||
@ -25,10 +24,11 @@ fun Project.execWithOutput(cmd: String, ignore: Boolean = false): String {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun gitVersion(): String {
|
fun gitVersion(): String {
|
||||||
|
var process = ""
|
||||||
val maybeTagOfCurrentCommit = execWithOutput("git -C ../ describe --contains HEAD", true)
|
val maybeTagOfCurrentCommit = execWithOutput("git -C ../ describe --contains HEAD", true)
|
||||||
val process = if (maybeTagOfCurrentCommit.isEmpty()) {
|
process = if (maybeTagOfCurrentCommit.isEmpty()) {
|
||||||
println("No tag on current commit. Will take the latest one.")
|
println("No tag on current commit. Will take the latest one.")
|
||||||
execWithOutput("git -C ../ for-each-ref refs/tags --sort=-refname --format='%(refname:short)' --count=1")
|
execWithOutput("git -C ../ for-each-ref refs/tags --sort=-authordate --format='%(refname:short)' --count=1")
|
||||||
} else {
|
} else {
|
||||||
println("Tag found on current commit")
|
println("Tag found on current commit")
|
||||||
execWithOutput("git -C ../ describe --contains HEAD")
|
execWithOutput("git -C ../ describe --contains HEAD")
|
||||||
@ -56,24 +56,24 @@ fun versionNameFromGit(): String {
|
|||||||
|
|
||||||
android {
|
android {
|
||||||
compileOptions {
|
compileOptions {
|
||||||
isCoreLibraryDesugaringEnabled = true
|
|
||||||
// Flag to enable support for the new language APIs
|
// Flag to enable support for the new language APIs
|
||||||
sourceCompatibility = JavaVersion.VERSION_17
|
sourceCompatibility = JavaVersion.VERSION_11
|
||||||
targetCompatibility = JavaVersion.VERSION_17
|
targetCompatibility = JavaVersion.VERSION_11
|
||||||
}
|
}
|
||||||
|
|
||||||
// For Kotlin projects
|
// For Kotlin projects
|
||||||
kotlinOptions {
|
kotlinOptions {
|
||||||
jvmTarget = "17"
|
jvmTarget = "11"
|
||||||
}
|
}
|
||||||
compileSdk = 35
|
compileSdk = 33
|
||||||
|
buildToolsVersion = "31.0.0"
|
||||||
buildFeatures {
|
buildFeatures {
|
||||||
viewBinding = true
|
viewBinding = true
|
||||||
}
|
}
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId = "bou.amine.apps.readerforselfossv2.android"
|
applicationId = "bou.amine.apps.readerforselfossv2.android"
|
||||||
minSdk = 25
|
minSdk = 21
|
||||||
targetSdk = 34 // 35 when edge-to-edge is handled
|
targetSdk = 33
|
||||||
versionCode = versionCodeFromGit()
|
versionCode = versionCodeFromGit()
|
||||||
versionName = versionNameFromGit()
|
versionName = versionNameFromGit()
|
||||||
|
|
||||||
@ -85,9 +85,8 @@ android {
|
|||||||
|
|
||||||
// tests
|
// tests
|
||||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||||
testInstrumentationRunnerArguments["clearPackageData"] = "true"
|
|
||||||
}
|
}
|
||||||
packaging {
|
packagingOptions {
|
||||||
resources {
|
resources {
|
||||||
excludes += "/META-INF/{AL2.0,LGPL2.1}"
|
excludes += "/META-INF/{AL2.0,LGPL2.1}"
|
||||||
}
|
}
|
||||||
@ -109,37 +108,31 @@ android {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
namespace = "bou.amine.apps.readerforselfossv2.android"
|
namespace = "bou.amine.apps.readerforselfossv2.android"
|
||||||
testOptions {
|
|
||||||
animationsDisabled = true
|
|
||||||
execution = "ANDROIDX_TEST_ORCHESTRATOR"
|
|
||||||
unitTests {
|
|
||||||
isIncludeAndroidResources = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.1.4")
|
|
||||||
|
|
||||||
implementation(project(":shared"))
|
implementation(project(":shared"))
|
||||||
implementation("androidx.appcompat:appcompat:1.7.0")
|
implementation("com.google.android.material:material:1.5.0")
|
||||||
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.10.1")
|
implementation("androidx.appcompat:appcompat:1.4.1")
|
||||||
|
implementation("androidx.constraintlayout:constraintlayout:2.1.3")
|
||||||
|
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.0")
|
||||||
|
|
||||||
implementation("androidx.preference:preference-ktx:1.2.1")
|
implementation("androidx.preference:preference-ktx:1.1.1")
|
||||||
|
|
||||||
implementation(fileTree(mapOf("include" to listOf("*.jar"), "dir" to "libs")))
|
implementation(fileTree(mapOf("include" to listOf("*.jar"), "dir" to "libs")))
|
||||||
|
|
||||||
// Android Support
|
// Android Support
|
||||||
implementation("com.google.android.material:material:1.12.0")
|
implementation("androidx.appcompat:appcompat:1.4.1")
|
||||||
implementation("androidx.recyclerview:recyclerview:1.4.0-rc01")
|
implementation("com.google.android.material:material:1.5.0")
|
||||||
|
implementation("androidx.recyclerview:recyclerview:1.3.0-alpha01")
|
||||||
implementation("androidx.legacy:legacy-support-v4:1.0.0")
|
implementation("androidx.legacy:legacy-support-v4:1.0.0")
|
||||||
implementation("androidx.vectordrawable:vectordrawable:1.2.0")
|
implementation("androidx.vectordrawable:vectordrawable:1.2.0-alpha02")
|
||||||
implementation("androidx.cardview:cardview:1.0.0")
|
implementation("androidx.cardview:cardview:1.0.0")
|
||||||
implementation("androidx.annotation:annotation:1.9.1")
|
implementation("androidx.annotation:annotation:1.3.0")
|
||||||
implementation("androidx.work:work-runtime-ktx:2.10.0")
|
implementation("androidx.work:work-runtime-ktx:2.7.1")
|
||||||
implementation("androidx.constraintlayout:constraintlayout:2.2.0")
|
implementation("androidx.constraintlayout:constraintlayout:2.1.3")
|
||||||
implementation("org.jsoup:jsoup:1.18.3")
|
implementation("org.jsoup:jsoup:1.14.3")
|
||||||
|
|
||||||
//multidex
|
//multidex
|
||||||
implementation("androidx.multidex:multidex:2.0.1")
|
implementation("androidx.multidex:multidex:2.0.1")
|
||||||
@ -150,59 +143,51 @@ dependencies {
|
|||||||
|
|
||||||
// Material-ish things
|
// Material-ish things
|
||||||
implementation("com.ashokvarma.android:bottom-navigation-bar:2.2.0")
|
implementation("com.ashokvarma.android:bottom-navigation-bar:2.2.0")
|
||||||
|
implementation("com.amulyakhare:com.amulyakhare.textdrawable:1.0.1")
|
||||||
|
|
||||||
// glide
|
// glide
|
||||||
kapt("com.github.bumptech.glide:compiler:4.16.0")
|
kapt("com.github.bumptech.glide:compiler:4.14.2")
|
||||||
implementation("com.github.bumptech.glide:okhttp3-integration:4.16.0")
|
implementation("com.github.bumptech.glide:okhttp3-integration:4.14.2")
|
||||||
|
|
||||||
// Themes
|
// Themes
|
||||||
implementation("com.leinardi.android:speed-dial:3.3.0")
|
implementation("com.github.rubensousa:floatingtoolbar:1.5.1")
|
||||||
|
|
||||||
// Pager
|
// Pager
|
||||||
implementation("me.relex:circleindicator:2.1.6")
|
implementation("me.relex:circleindicator:2.1.6")
|
||||||
implementation("androidx.viewpager2:viewpager2:1.1.0")
|
implementation("androidx.viewpager2:viewpager2:1.1.0-beta01")
|
||||||
|
|
||||||
//Dependency Injection
|
//Dependency Injection
|
||||||
implementation("org.kodein.di:kodein-di:7.23.1")
|
implementation("org.kodein.di:kodein-di:7.14.0")
|
||||||
implementation("org.kodein.di:kodein-di-framework-android-x:7.23.1")
|
implementation("org.kodein.di:kodein-di-framework-android-x:7.14.0")
|
||||||
implementation("org.kodein.di:kodein-di-framework-android-x-viewmodel:7.23.1")
|
implementation("org.kodein.di:kodein-di-framework-android-x-viewmodel:7.14.0")
|
||||||
|
|
||||||
//Settings
|
//Settings
|
||||||
implementation("com.russhwolf:multiplatform-settings-no-arg:1.3.0")
|
implementation("com.russhwolf:multiplatform-settings-no-arg:0.9")
|
||||||
|
|
||||||
//Logging
|
//Logging
|
||||||
implementation("io.github.aakira:napier:2.7.1")
|
implementation("io.github.aakira:napier:2.6.1")
|
||||||
|
|
||||||
//PhotoView
|
//PhotoView
|
||||||
implementation("com.github.chrisbanes:PhotoView:2.3.0")
|
implementation("com.github.chrisbanes:PhotoView:2.3.0")
|
||||||
|
|
||||||
implementation("androidx.core:core-ktx:1.15.0")
|
implementation("androidx.core:core-ktx:1.8.0")
|
||||||
|
|
||||||
implementation("androidx.lifecycle:lifecycle-extensions:2.2.0")
|
implementation("androidx.lifecycle:lifecycle-extensions:2.2.0")
|
||||||
|
|
||||||
// Network information
|
// Network information
|
||||||
implementation("com.github.ln-12:multiplatform-connectivity-status:1.3.0")
|
implementation("com.github.ln-12:multiplatform-connectivity-status:1.3.0")
|
||||||
|
|
||||||
// SQLDELIGHT
|
// SQLDELIGHT
|
||||||
implementation("app.cash.sqldelight:android-driver:2.0.2")
|
implementation("com.squareup.sqldelight:android-driver:1.5.4")
|
||||||
|
|
||||||
//test
|
//test
|
||||||
testImplementation("junit:junit:4.13.2")
|
testImplementation("junit:junit:4.13.2")
|
||||||
testImplementation("io.mockk:mockk:1.13.14")
|
testImplementation("io.mockk:mockk:1.12.0")
|
||||||
testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.1")
|
testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.0")
|
||||||
implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.6.1")
|
implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.4.0")
|
||||||
androidTestImplementation("androidx.test:runner:1.6.2")
|
|
||||||
androidTestImplementation("androidx.test:rules:1.6.1")
|
|
||||||
androidTestImplementation("androidx.test.espresso:espresso-core:3.6.1")
|
|
||||||
implementation("androidx.test.espresso:espresso-idling-resource:3.6.1")
|
|
||||||
androidTestImplementation("androidx.test.ext:junit-ktx:1.2.1")
|
|
||||||
androidTestUtil("androidx.test:orchestrator:1.5.1")
|
|
||||||
testImplementation("org.robolectric:robolectric:4.14.1")
|
|
||||||
testImplementation("androidx.test:core-ktx:1.6.1")
|
|
||||||
|
|
||||||
implementation("ch.acra:acra-http:$acraVersion")
|
implementation("ch.acra:acra-http:$acraVersion")
|
||||||
implementation("ch.acra:acra-toast:$acraVersion")
|
implementation("ch.acra:acra-toast:$acraVersion")
|
||||||
implementation("com.google.auto.service:auto-service:1.1.1")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.withType<Test> {
|
tasks.withType<Test> {
|
||||||
|
10
androidApp/proguard-rules.pro
vendored
10
androidApp/proguard-rules.pro
vendored
@ -55,7 +55,6 @@
|
|||||||
# maybe remove later ?
|
# maybe remove later ?
|
||||||
-keep class * extends androidx.fragment.app.Fragment
|
-keep class * extends androidx.fragment.app.Fragment
|
||||||
|
|
||||||
-dontwarn org.slf4j.impl.StaticLoggerBinder
|
|
||||||
|
|
||||||
# Keep `Companion` object fields of serializable classes.
|
# Keep `Companion` object fields of serializable classes.
|
||||||
# This avoids serializer lookup through `getDeclaredClasses` as done for named companion objects.
|
# This avoids serializer lookup through `getDeclaredClasses` as done for named companion objects.
|
||||||
@ -86,12 +85,3 @@
|
|||||||
|
|
||||||
-dontwarn io.mockk.**
|
-dontwarn io.mockk.**
|
||||||
-keep class io.mockk.** { *; }
|
-keep class io.mockk.** { *; }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Kodein
|
|
||||||
-keep, allowobfuscation, allowoptimization class org.kodein.type.TypeReference
|
|
||||||
-keep, allowobfuscation, allowoptimization class org.kodein.type.JVMAbstractTypeToken$Companion$WrappingTest
|
|
||||||
|
|
||||||
-keep, allowobfuscation, allowoptimization class * extends org.kodein.type.TypeReference
|
|
||||||
-keep, allowobfuscation, allowoptimization class * extends org.kodein.type.JVMAbstractTypeToken$Companion$WrappingTest
|
|
@ -1,121 +0,0 @@
|
|||||||
package bou.amine.apps.readerforselfossv2.android
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import androidx.annotation.ArrayRes
|
|
||||||
import androidx.test.espresso.Espresso.onData
|
|
||||||
import androidx.test.espresso.Espresso.onView
|
|
||||||
import androidx.test.espresso.action.ViewActions.click
|
|
||||||
import androidx.test.espresso.action.ViewActions.replaceText
|
|
||||||
import androidx.test.espresso.action.ViewActions.typeTextIntoFocusedView
|
|
||||||
import androidx.test.espresso.assertion.ViewAssertions.doesNotExist
|
|
||||||
import androidx.test.espresso.assertion.ViewAssertions.matches
|
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.isChecked
|
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
|
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.isNotChecked
|
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.withId
|
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.withText
|
|
||||||
import org.hamcrest.CoreMatchers.allOf
|
|
||||||
import org.hamcrest.Matchers.hasToString
|
|
||||||
|
|
||||||
fun performLogin(someUrl: String? = null) {
|
|
||||||
onView(withId(R.id.urlView)).perform(click()).perform(
|
|
||||||
typeTextIntoFocusedView(
|
|
||||||
if (!someUrl.isNullOrEmpty()) someUrl else "http://10.0.2.2:8888",
|
|
||||||
),
|
|
||||||
)
|
|
||||||
onView(withId(R.id.signInButton)).perform(click())
|
|
||||||
}
|
|
||||||
|
|
||||||
fun loginAndInitHome() {
|
|
||||||
performLogin()
|
|
||||||
onView(withText(R.string.gdpr_dialog_title)).check(matches(isDisplayed()))
|
|
||||||
onView(withText("OK")).perform(click())
|
|
||||||
}
|
|
||||||
|
|
||||||
fun changeAndCancelSetting(
|
|
||||||
oldValue: String,
|
|
||||||
newValue: String,
|
|
||||||
openSettingItem: () -> Unit,
|
|
||||||
) {
|
|
||||||
openSettingItem()
|
|
||||||
onView(
|
|
||||||
withId(android.R.id.edit),
|
|
||||||
).perform(replaceText(newValue))
|
|
||||||
onView(
|
|
||||||
withId(android.R.id.button2),
|
|
||||||
).perform(click())
|
|
||||||
openSettingItem()
|
|
||||||
onView(
|
|
||||||
withId(android.R.id.edit),
|
|
||||||
).check(matches(withText(oldValue)))
|
|
||||||
onView(
|
|
||||||
withText(newValue),
|
|
||||||
).check(doesNotExist())
|
|
||||||
onView(
|
|
||||||
withId(android.R.id.button2),
|
|
||||||
).perform(click())
|
|
||||||
}
|
|
||||||
|
|
||||||
fun changeAndSaveSetting(
|
|
||||||
oldValue: String,
|
|
||||||
newValue: String,
|
|
||||||
openSettingItem: () -> Unit,
|
|
||||||
) {
|
|
||||||
openSettingItem()
|
|
||||||
onView(
|
|
||||||
withId(android.R.id.edit),
|
|
||||||
).perform(replaceText(newValue))
|
|
||||||
onView(
|
|
||||||
withId(android.R.id.button1),
|
|
||||||
).perform(click())
|
|
||||||
openSettingItem()
|
|
||||||
onView(
|
|
||||||
withId(android.R.id.edit),
|
|
||||||
).check(matches(withText(newValue)))
|
|
||||||
if (oldValue.isNotEmpty()) {
|
|
||||||
onView(
|
|
||||||
withText(oldValue),
|
|
||||||
).check(doesNotExist())
|
|
||||||
}
|
|
||||||
onView(
|
|
||||||
withId(android.R.id.button2),
|
|
||||||
).perform(click())
|
|
||||||
}
|
|
||||||
|
|
||||||
fun testPreferencesFromArray(
|
|
||||||
context: Context,
|
|
||||||
@ArrayRes arrayRes: Int,
|
|
||||||
openSettingItem: () -> Unit,
|
|
||||||
) {
|
|
||||||
openSettingItem()
|
|
||||||
context.resources.getStringArray(arrayRes).forEach { res ->
|
|
||||||
onView(withText(res)).check(matches(allOf(isDisplayed(), isNotChecked())))
|
|
||||||
onView(withText(res)).perform(click())
|
|
||||||
onView(withText(res)).check(doesNotExist())
|
|
||||||
openSettingItem()
|
|
||||||
onView(withText(res)).check(matches(allOf(isDisplayed(), isChecked())))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun testAddSourceWithUrl(
|
|
||||||
url: String,
|
|
||||||
sourceName: String,
|
|
||||||
) {
|
|
||||||
onView(withId(R.id.fab))
|
|
||||||
.perform(click())
|
|
||||||
onView(withId(R.id.nameInput))
|
|
||||||
.perform(click())
|
|
||||||
.perform(typeTextIntoFocusedView(sourceName))
|
|
||||||
onView(withId(R.id.sourceUri))
|
|
||||||
.perform(click())
|
|
||||||
.perform(typeTextIntoFocusedView(url))
|
|
||||||
onView(withId(R.id.tags))
|
|
||||||
.perform(click())
|
|
||||||
.perform(typeTextIntoFocusedView("tag1,tag2,tag3"))
|
|
||||||
onView(withId(R.id.spoutsSpinner))
|
|
||||||
.perform(click())
|
|
||||||
onData(hasToString("RSS Feed")).perform(click())
|
|
||||||
onView(withId(R.id.saveBtn))
|
|
||||||
.perform(click())
|
|
||||||
onView(withText(sourceName)).check(matches(isDisplayed()))
|
|
||||||
}
|
|
@ -1,110 +0,0 @@
|
|||||||
package bou.amine.apps.readerforselfossv2.android
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.view.View
|
|
||||||
import android.widget.EditText
|
|
||||||
import android.widget.ImageView
|
|
||||||
import android.widget.RelativeLayout
|
|
||||||
import androidx.annotation.DrawableRes
|
|
||||||
import androidx.annotation.StringRes
|
|
||||||
import androidx.core.graphics.drawable.toBitmap
|
|
||||||
import androidx.test.core.app.ApplicationProvider
|
|
||||||
import androidx.test.espresso.Espresso.openActionBarOverflowOrOptionsMenu
|
|
||||||
import androidx.test.espresso.Root
|
|
||||||
import androidx.test.espresso.matcher.RootMatchers.isPlatformPopup
|
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.hasSibling
|
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.withChild
|
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.withClassName
|
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.withId
|
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.withParent
|
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.withResourceName
|
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.withText
|
|
||||||
import org.hamcrest.CoreMatchers.allOf
|
|
||||||
import org.hamcrest.Description
|
|
||||||
import org.hamcrest.Matcher
|
|
||||||
import org.hamcrest.Matchers
|
|
||||||
import org.hamcrest.TypeSafeMatcher
|
|
||||||
|
|
||||||
fun withError(
|
|
||||||
@StringRes id: Int,
|
|
||||||
): TypeSafeMatcher<View?> {
|
|
||||||
return object : TypeSafeMatcher<View?>() {
|
|
||||||
override fun matchesSafely(view: View?): Boolean {
|
|
||||||
if (view != null && (view !is EditText || view.error == null)) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
val context = view!!.context
|
|
||||||
|
|
||||||
return (view as EditText).error.toString() == context.getString(id)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun describeTo(description: Description?) {
|
|
||||||
// Nothing
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun isPopupWindow(): Matcher<Root> = isPlatformPopup()
|
|
||||||
|
|
||||||
fun withDrawable(
|
|
||||||
@DrawableRes id: Int,
|
|
||||||
) = object : TypeSafeMatcher<View>() {
|
|
||||||
override fun describeTo(description: Description) {
|
|
||||||
description.appendText("ImageView with drawable same as drawable with id $id")
|
|
||||||
}
|
|
||||||
|
|
||||||
@Suppress("detekt:SwallowedException")
|
|
||||||
override fun matchesSafely(view: View): Boolean {
|
|
||||||
val context = view.context
|
|
||||||
val expectedBitmap = context.getDrawable(id)!!.toBitmap()
|
|
||||||
try {
|
|
||||||
return view is ImageView && view.drawable.toBitmap().sameAs(expectedBitmap)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun hasBottombarItemText(
|
|
||||||
@StringRes id: Int,
|
|
||||||
): Matcher<View>? =
|
|
||||||
allOf(
|
|
||||||
withResourceName("fixed_bottom_navigation_icon"),
|
|
||||||
withParent(
|
|
||||||
allOf(
|
|
||||||
withResourceName("fixed_bottom_navigation_icon_container"),
|
|
||||||
hasSibling(withText(id)),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
fun withSettingsCheckboxWidget(
|
|
||||||
@StringRes id: Int,
|
|
||||||
): Matcher<View>? =
|
|
||||||
allOf(
|
|
||||||
withId(android.R.id.switch_widget),
|
|
||||||
withParent(
|
|
||||||
withSettingsCheckboxFrame(id),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
fun withSettingsCheckboxFrame(
|
|
||||||
@StringRes id: Int,
|
|
||||||
): Matcher<View>? =
|
|
||||||
allOf(
|
|
||||||
withId(android.R.id.widget_frame),
|
|
||||||
hasSibling(
|
|
||||||
allOf(
|
|
||||||
withClassName(Matchers.equalTo(RelativeLayout::class.java.name)),
|
|
||||||
withChild(
|
|
||||||
withText(id),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
fun openMenu() {
|
|
||||||
openActionBarOverflowOrOptionsMenu(
|
|
||||||
ApplicationProvider.getApplicationContext<Context>(),
|
|
||||||
)
|
|
||||||
}
|
|
@ -1,118 +0,0 @@
|
|||||||
package bou.amine.apps.readerforselfossv2.android
|
|
||||||
|
|
||||||
import androidx.test.espresso.Espresso.onView
|
|
||||||
import androidx.test.espresso.action.ViewActions
|
|
||||||
import androidx.test.espresso.action.ViewActions.click
|
|
||||||
import androidx.test.espresso.assertion.ViewAssertions.matches
|
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.isClickable
|
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
|
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.isFocused
|
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.isRoot
|
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.isSelected
|
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.withId
|
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.withText
|
|
||||||
import androidx.test.ext.junit.rules.ActivityScenarioRule
|
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
|
||||||
import androidx.test.filters.LargeTest
|
|
||||||
import org.hamcrest.CoreMatchers.not
|
|
||||||
import org.junit.Before
|
|
||||||
import org.junit.Rule
|
|
||||||
import org.junit.Test
|
|
||||||
import org.junit.runner.RunWith
|
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4::class)
|
|
||||||
@LargeTest
|
|
||||||
class HomeActivityTest {
|
|
||||||
@get:Rule
|
|
||||||
val activityRule = ActivityScenarioRule(LoginActivity::class.java)
|
|
||||||
|
|
||||||
@Before
|
|
||||||
fun init() {
|
|
||||||
loginAndInitHome()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testMenu() {
|
|
||||||
onView(withId(R.id.action_search)).check(matches(isDisplayed())).check(
|
|
||||||
matches(
|
|
||||||
isClickable(),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
onView(withId(R.id.action_filter)).check(matches(isDisplayed())).check(
|
|
||||||
matches(
|
|
||||||
isClickable(),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
openMenu()
|
|
||||||
onView(withText(R.string.readAll)).check(matches(isDisplayed()))
|
|
||||||
onView(withText(R.string.menu_home_sources)).check(matches(isDisplayed()))
|
|
||||||
onView(withText(R.string.title_activity_settings)).check(matches(isDisplayed()))
|
|
||||||
onView(withText(R.string.menu_home_refresh)).check(matches(isDisplayed()))
|
|
||||||
onView(withText(R.string.issue_tracker_link)).check(matches(isDisplayed()))
|
|
||||||
onView(withText(R.string.action_disconnect)).check(matches(isDisplayed()))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testMenuActions() {
|
|
||||||
onView(withId(R.id.action_search)).perform(click())
|
|
||||||
onView(
|
|
||||||
withId(com.google.android.material.R.id.search_src_text),
|
|
||||||
).check(matches(isFocused()))
|
|
||||||
onView(isRoot()).perform(ViewActions.pressBack())
|
|
||||||
|
|
||||||
onView(withId(R.id.action_filter)).perform(click())
|
|
||||||
onView(
|
|
||||||
withText(R.string.filter_item_sources),
|
|
||||||
).check(matches(isDisplayed()))
|
|
||||||
onView(
|
|
||||||
withText(R.string.filter_item_tags),
|
|
||||||
).check(matches(isDisplayed()))
|
|
||||||
onView(
|
|
||||||
withId(R.id.floatingActionButton2),
|
|
||||||
).check(matches(isDisplayed()))
|
|
||||||
onView(isRoot()).perform(ViewActions.pressBack())
|
|
||||||
|
|
||||||
openMenu()
|
|
||||||
onView(withText(R.string.readAll)).perform(click())
|
|
||||||
onView(withText(R.string.markall_dialog_message)).check(matches(isDisplayed()))
|
|
||||||
onView(isRoot()).perform(ViewActions.pressBack())
|
|
||||||
openMenu()
|
|
||||||
|
|
||||||
onView(withText(R.string.menu_home_sources)).perform(click())
|
|
||||||
onView(withId(R.id.fab)).check(matches(isDisplayed()))
|
|
||||||
onView(isRoot()).perform(ViewActions.pressBack())
|
|
||||||
openMenu()
|
|
||||||
|
|
||||||
onView(withText(R.string.title_activity_settings)).perform(click())
|
|
||||||
onView(withText(R.string.pref_header_general)).check(matches(isDisplayed()))
|
|
||||||
onView(isRoot()).perform(ViewActions.pressBack())
|
|
||||||
openMenu()
|
|
||||||
|
|
||||||
onView(withText(R.string.menu_home_refresh)).perform(click())
|
|
||||||
onView(withText(R.string.refresh_dialog_message)).check(matches(isDisplayed()))
|
|
||||||
onView(isRoot()).perform(ViewActions.pressBack())
|
|
||||||
openMenu()
|
|
||||||
|
|
||||||
/*onView(withText(R.string.issue_tracker_link)).perform(click())
|
|
||||||
onView(withText(R.string.markall_dialog_message)).check(matches(isDisplayed()))
|
|
||||||
onView(isRoot()).perform(ViewActions.pressBack())
|
|
||||||
openMenu()*/
|
|
||||||
|
|
||||||
onView(withText(R.string.action_disconnect)).perform(click())
|
|
||||||
onView(withText(R.string.confirm_disconnect_title)).check(matches(isDisplayed()))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testEmptyView() {
|
|
||||||
onView(withId(R.id.emptyText)).check(matches(isDisplayed()))
|
|
||||||
onView(
|
|
||||||
hasBottombarItemText(R.string.tab_new),
|
|
||||||
).check(matches(isDisplayed())).check(matches(isSelected()))
|
|
||||||
onView(
|
|
||||||
hasBottombarItemText(R.string.tab_read),
|
|
||||||
).check(matches(isDisplayed())).check(matches(not(isSelected())))
|
|
||||||
onView(
|
|
||||||
hasBottombarItemText(R.string.tab_favs),
|
|
||||||
).check(matches(isDisplayed())).check(matches(not(isSelected())))
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,79 +0,0 @@
|
|||||||
package bou.amine.apps.readerforselfossv2.android
|
|
||||||
|
|
||||||
import androidx.test.espresso.Espresso.onView
|
|
||||||
import androidx.test.espresso.IdlingRegistry
|
|
||||||
import androidx.test.espresso.action.ViewActions.click
|
|
||||||
import androidx.test.espresso.assertion.ViewAssertions.matches
|
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.isClickable
|
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
|
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.isNotChecked
|
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.withId
|
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.withText
|
|
||||||
import androidx.test.ext.junit.rules.ActivityScenarioRule
|
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
|
||||||
import androidx.test.filters.LargeTest
|
|
||||||
import bou.amine.apps.readerforselfossv2.android.testing.CountingIdlingResourceSingleton
|
|
||||||
import org.junit.After
|
|
||||||
import org.junit.Before
|
|
||||||
import org.junit.Rule
|
|
||||||
import org.junit.Test
|
|
||||||
import org.junit.runner.RunWith
|
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4::class)
|
|
||||||
@LargeTest
|
|
||||||
class LoginActivityTest {
|
|
||||||
@get:Rule
|
|
||||||
val activityRule = ActivityScenarioRule(LoginActivity::class.java)
|
|
||||||
|
|
||||||
@Before
|
|
||||||
fun registerIdlingResource() {
|
|
||||||
IdlingRegistry
|
|
||||||
.getInstance()
|
|
||||||
.register(CountingIdlingResourceSingleton.countingIdlingResource)
|
|
||||||
}
|
|
||||||
|
|
||||||
@After
|
|
||||||
fun unregisterIdlingResource() {
|
|
||||||
IdlingRegistry
|
|
||||||
.getInstance()
|
|
||||||
.unregister(CountingIdlingResourceSingleton.countingIdlingResource)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun viewIsInitialized() {
|
|
||||||
onView(withId(R.id.urlView)).check(matches(isDisplayed()))
|
|
||||||
onView(withId(R.id.selfSigned))
|
|
||||||
.check(matches(isDisplayed()))
|
|
||||||
.check(matches(isNotChecked()))
|
|
||||||
.check(
|
|
||||||
matches(isClickable()),
|
|
||||||
)
|
|
||||||
onView(withId(R.id.withLogin))
|
|
||||||
.check(matches(isDisplayed()))
|
|
||||||
.check(matches(isNotChecked()))
|
|
||||||
.check(
|
|
||||||
matches(isClickable()),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun urlError() {
|
|
||||||
performLogin("10.0.2.2:8888")
|
|
||||||
onView(withId(R.id.urlView)).perform(click())
|
|
||||||
onView(withId(R.id.urlView)).check(matches(withError(R.string.wrong_infos)))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun multiError() {
|
|
||||||
onView(withId(R.id.signInButton)).perform(click())
|
|
||||||
onView(withId(R.id.signInButton)).perform(click())
|
|
||||||
onView(withId(R.id.signInButton)).perform(click())
|
|
||||||
onView(withText(R.string.warning_wrong_url)).check(matches(isDisplayed()))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun connect() {
|
|
||||||
performLogin()
|
|
||||||
onView(withText(R.string.gdpr_dialog_title)).check(matches(isDisplayed()))
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,155 +0,0 @@
|
|||||||
package bou.amine.apps.readerforselfossv2.android
|
|
||||||
|
|
||||||
import androidx.test.core.app.ApplicationProvider
|
|
||||||
import androidx.test.espresso.Espresso.onView
|
|
||||||
import androidx.test.espresso.Espresso.openActionBarOverflowOrOptionsMenu
|
|
||||||
import androidx.test.espresso.action.ViewActions
|
|
||||||
import androidx.test.espresso.action.ViewActions.click
|
|
||||||
import androidx.test.espresso.action.ViewActions.replaceText
|
|
||||||
import androidx.test.espresso.action.ViewActions.swipeUp
|
|
||||||
import androidx.test.espresso.action.ViewActions.typeTextIntoFocusedView
|
|
||||||
import androidx.test.espresso.assertion.ViewAssertions.matches
|
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.isChecked
|
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
|
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.isEnabled
|
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.isFocused
|
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.isRoot
|
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.withId
|
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.withText
|
|
||||||
import androidx.test.ext.junit.rules.ActivityScenarioRule
|
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
|
||||||
import androidx.test.filters.LargeTest
|
|
||||||
import org.hamcrest.CoreMatchers.allOf
|
|
||||||
import org.hamcrest.CoreMatchers.not
|
|
||||||
import org.junit.Before
|
|
||||||
import org.junit.Rule
|
|
||||||
import org.junit.Test
|
|
||||||
import org.junit.runner.RunWith
|
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4::class)
|
|
||||||
@LargeTest
|
|
||||||
class SettingsActivityGeneralTest {
|
|
||||||
@get:Rule
|
|
||||||
val activityRule = ActivityScenarioRule(LoginActivity::class.java)
|
|
||||||
|
|
||||||
@Before
|
|
||||||
fun init() {
|
|
||||||
loginAndInitHome()
|
|
||||||
openActionBarOverflowOrOptionsMenu(
|
|
||||||
ApplicationProvider.getApplicationContext(),
|
|
||||||
)
|
|
||||||
onView(withText(R.string.title_activity_settings)).perform(click())
|
|
||||||
onView(withText(R.string.pref_header_general)).perform(click())
|
|
||||||
}
|
|
||||||
|
|
||||||
@Suppress("detekt:LongMethod")
|
|
||||||
@Test
|
|
||||||
fun testGeneral() {
|
|
||||||
onView(withText(R.string.pref_api_items_number_title)).check(matches(isDisplayed()))
|
|
||||||
onView(
|
|
||||||
withSettingsCheckboxWidget(R.string.pref_general_infinite_loading_title),
|
|
||||||
).check(
|
|
||||||
matches(
|
|
||||||
allOf(
|
|
||||||
isDisplayed(),
|
|
||||||
not(isChecked()),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
onView(withText(R.string.pref_general_category_links)).check(matches(isDisplayed()))
|
|
||||||
onView(withSettingsCheckboxWidget(R.string.pref_article_viewer_title)).check(
|
|
||||||
matches(
|
|
||||||
allOf(
|
|
||||||
isDisplayed(),
|
|
||||||
isChecked(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
onView(withText(R.string.pref_general_category_displaying)).check(matches(isDisplayed()))
|
|
||||||
onView(withSettingsCheckboxWidget(R.string.pref_switch_card_view_title)).check(
|
|
||||||
matches(
|
|
||||||
allOf(
|
|
||||||
isDisplayed(),
|
|
||||||
not(isChecked()),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
onView(withSettingsCheckboxWidget(R.string.card_height_title)).check(
|
|
||||||
matches(
|
|
||||||
allOf(
|
|
||||||
isDisplayed(),
|
|
||||||
not(isChecked()),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
onView(withSettingsCheckboxFrame(R.string.card_height_title)).check(
|
|
||||||
matches(
|
|
||||||
not(isEnabled()),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
onView(withSettingsCheckboxWidget(R.string.switch_unread_count_title)).check(
|
|
||||||
matches(
|
|
||||||
allOf(
|
|
||||||
isDisplayed(),
|
|
||||||
isChecked(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
onView(withId(R.id.settings)).perform(swipeUp())
|
|
||||||
onView(withSettingsCheckboxWidget(R.string.display_all_counts_title)).check(
|
|
||||||
matches(
|
|
||||||
allOf(
|
|
||||||
isDisplayed(),
|
|
||||||
not(isChecked()),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Suppress("detekt:ForbiddenComment")
|
|
||||||
@Test
|
|
||||||
fun testGeneralActionsNumberItems() {
|
|
||||||
onView(withText(R.string.pref_api_items_number_title)).perform(click())
|
|
||||||
onView(withId(android.R.id.edit)).check(matches(isFocused()))
|
|
||||||
|
|
||||||
// Value check
|
|
||||||
onView(
|
|
||||||
withId(android.R.id.edit),
|
|
||||||
).perform(replaceText("AVC"))
|
|
||||||
.check(matches(withText("")))
|
|
||||||
// TODO: should check message error. Not working for api level 30+
|
|
||||||
onView(
|
|
||||||
withId(android.R.id.edit),
|
|
||||||
).perform(replaceText("-1"))
|
|
||||||
.check(matches(withText("")))
|
|
||||||
// TODO: should check message error. Not working for api level 30+
|
|
||||||
onView(
|
|
||||||
withId(android.R.id.edit),
|
|
||||||
).perform(replaceText("300"))
|
|
||||||
.check(matches(withText("")))
|
|
||||||
onView(
|
|
||||||
withId(android.R.id.edit),
|
|
||||||
).perform(typeTextIntoFocusedView("300"))
|
|
||||||
.check(matches(withText("30")))
|
|
||||||
onView(
|
|
||||||
withId(android.R.id.edit),
|
|
||||||
).perform(replaceText("10"))
|
|
||||||
.check(matches(withText("10")))
|
|
||||||
onView(isRoot()).perform(ViewActions.pressBack())
|
|
||||||
|
|
||||||
// Value saving
|
|
||||||
changeAndCancelSetting("20", "10") {
|
|
||||||
onView(withText(R.string.pref_api_items_number_title)).perform(click())
|
|
||||||
}
|
|
||||||
changeAndSaveSetting("20", "10") {
|
|
||||||
onView(withText(R.string.pref_api_items_number_title)).perform(click())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testGeneralActionsCheckboxes() {
|
|
||||||
onView(withSettingsCheckboxFrame(R.string.card_height_title)).check(matches(not(isEnabled())))
|
|
||||||
onView(withSettingsCheckboxWidget(R.string.pref_switch_card_view_title)).perform(click())
|
|
||||||
onView(withSettingsCheckboxFrame(R.string.card_height_title)).check(matches(isEnabled()))
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,174 +0,0 @@
|
|||||||
package bou.amine.apps.readerforselfossv2.android
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import androidx.test.core.app.ApplicationProvider
|
|
||||||
import androidx.test.espresso.Espresso.onView
|
|
||||||
import androidx.test.espresso.Espresso.openActionBarOverflowOrOptionsMenu
|
|
||||||
import androidx.test.espresso.action.ViewActions.click
|
|
||||||
import androidx.test.espresso.assertion.ViewAssertions.matches
|
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.isChecked
|
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
|
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.isEnabled
|
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.isNotEnabled
|
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.withText
|
|
||||||
import androidx.test.ext.junit.rules.ActivityScenarioRule
|
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
|
||||||
import androidx.test.filters.LargeTest
|
|
||||||
import org.hamcrest.CoreMatchers.allOf
|
|
||||||
import org.hamcrest.CoreMatchers.not
|
|
||||||
import org.junit.Before
|
|
||||||
import org.junit.Rule
|
|
||||||
import org.junit.Test
|
|
||||||
import org.junit.runner.RunWith
|
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4::class)
|
|
||||||
@LargeTest
|
|
||||||
class SettingsActivityOfflineTest {
|
|
||||||
@get:Rule
|
|
||||||
val activityRule = ActivityScenarioRule(LoginActivity::class.java)
|
|
||||||
|
|
||||||
lateinit var context: Context
|
|
||||||
|
|
||||||
@Before
|
|
||||||
fun init() {
|
|
||||||
activityRule.scenario.onActivity { activity ->
|
|
||||||
context = activity.window.context
|
|
||||||
}
|
|
||||||
loginAndInitHome()
|
|
||||||
openActionBarOverflowOrOptionsMenu(
|
|
||||||
ApplicationProvider.getApplicationContext(),
|
|
||||||
)
|
|
||||||
onView(withText(R.string.title_activity_settings)).perform(click())
|
|
||||||
onView(withText(R.string.pref_header_offline)).perform(click())
|
|
||||||
}
|
|
||||||
|
|
||||||
@Suppress("detekt:LongMethod")
|
|
||||||
@Test
|
|
||||||
fun testOffline() {
|
|
||||||
onView(withSettingsCheckboxWidget(R.string.pref_switch_periodic_refresh)).check(
|
|
||||||
matches(
|
|
||||||
allOf(
|
|
||||||
isDisplayed(),
|
|
||||||
not(isChecked()),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
onView(withSettingsCheckboxWidget(R.string.pref_switch_items_caching)).check(
|
|
||||||
matches(
|
|
||||||
allOf(
|
|
||||||
isDisplayed(),
|
|
||||||
not(isChecked()),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
onView(withSettingsCheckboxFrame(R.string.pref_switch_items_caching)).check(
|
|
||||||
matches(
|
|
||||||
isEnabled(),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
onView(withText(R.string.pref_periodic_refresh_minutes_title)).check(
|
|
||||||
matches(
|
|
||||||
allOf(isNotEnabled(), isDisplayed()),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
onView(withSettingsCheckboxWidget(R.string.pref_switch_refresh_when_charging)).check(
|
|
||||||
matches(
|
|
||||||
allOf(
|
|
||||||
isDisplayed(),
|
|
||||||
not(isChecked()),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
onView(withSettingsCheckboxFrame(R.string.pref_switch_refresh_when_charging)).check(
|
|
||||||
matches(
|
|
||||||
isNotEnabled(),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
onView(withSettingsCheckboxWidget(R.string.pref_switch_notify_new_items)).check(
|
|
||||||
matches(
|
|
||||||
allOf(
|
|
||||||
isDisplayed(),
|
|
||||||
not(isChecked()),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
onView(withSettingsCheckboxFrame(R.string.pref_switch_notify_new_items)).check(
|
|
||||||
matches(
|
|
||||||
isNotEnabled(),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
onView(withSettingsCheckboxWidget(R.string.pref_switch_update_sources)).check(
|
|
||||||
matches(
|
|
||||||
allOf(
|
|
||||||
isDisplayed(),
|
|
||||||
isChecked(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Suppress("detekt:LongMethod")
|
|
||||||
@Test
|
|
||||||
fun testOfflineActions() {
|
|
||||||
onView(withText(R.string.pref_switch_items_caching_off)).check(matches(isDisplayed()))
|
|
||||||
onView(withText(R.string.pref_switch_items_caching)).perform(click())
|
|
||||||
onView(withText(R.string.pref_switch_items_caching_on)).check(matches(isDisplayed()))
|
|
||||||
onView(withSettingsCheckboxFrame(R.string.pref_switch_items_caching)).check(
|
|
||||||
matches(
|
|
||||||
isEnabled(),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
onView(withText(R.string.pref_periodic_refresh_minutes_title)).check(
|
|
||||||
matches(
|
|
||||||
isNotEnabled(),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
onView(withSettingsCheckboxFrame(R.string.pref_switch_refresh_when_charging)).check(
|
|
||||||
matches(
|
|
||||||
isNotEnabled(),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
onView(withSettingsCheckboxFrame(R.string.pref_switch_notify_new_items)).check(
|
|
||||||
matches(
|
|
||||||
isNotEnabled(),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
onView(withText(R.string.pref_switch_periodic_refresh_off)).check(
|
|
||||||
matches(
|
|
||||||
isDisplayed(),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
onView(withSettingsCheckboxWidget(R.string.pref_switch_periodic_refresh)).perform(click())
|
|
||||||
onView(withText(R.string.pref_switch_periodic_refresh_on)).check(
|
|
||||||
matches(
|
|
||||||
isDisplayed(),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
onView(withSettingsCheckboxFrame(R.string.pref_periodic_refresh_minutes_title)).check(
|
|
||||||
matches(
|
|
||||||
isEnabled(),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
onView(withSettingsCheckboxFrame(R.string.pref_switch_refresh_when_charging)).check(
|
|
||||||
matches(
|
|
||||||
isEnabled(),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
onView(withSettingsCheckboxFrame(R.string.pref_switch_notify_new_items)).check(
|
|
||||||
matches(
|
|
||||||
isEnabled(),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
changeAndCancelSetting("360", "123") {
|
|
||||||
onView(withText(R.string.pref_periodic_refresh_minutes_title)).perform(click())
|
|
||||||
}
|
|
||||||
changeAndSaveSetting("360", "123") {
|
|
||||||
onView(withText(R.string.pref_periodic_refresh_minutes_title)).perform(click())
|
|
||||||
}
|
|
||||||
onView(withSettingsCheckboxFrame(R.string.pref_switch_refresh_when_charging)).perform(click())
|
|
||||||
onView(withSettingsCheckboxFrame(R.string.pref_switch_notify_new_items)).perform(click())
|
|
||||||
onView(withSettingsCheckboxWidget(R.string.pref_switch_update_sources)).perform(click())
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,85 +0,0 @@
|
|||||||
package bou.amine.apps.readerforselfossv2.android
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import androidx.test.core.app.ApplicationProvider
|
|
||||||
import androidx.test.espresso.Espresso.onView
|
|
||||||
import androidx.test.espresso.Espresso.openActionBarOverflowOrOptionsMenu
|
|
||||||
import androidx.test.espresso.action.ViewActions.click
|
|
||||||
import androidx.test.espresso.assertion.ViewAssertions.matches
|
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.isChecked
|
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
|
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.withText
|
|
||||||
import androidx.test.ext.junit.rules.ActivityScenarioRule
|
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
|
||||||
import androidx.test.filters.LargeTest
|
|
||||||
import org.hamcrest.CoreMatchers.allOf
|
|
||||||
import org.hamcrest.CoreMatchers.not
|
|
||||||
import org.junit.Before
|
|
||||||
import org.junit.Rule
|
|
||||||
import org.junit.Test
|
|
||||||
import org.junit.runner.RunWith
|
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4::class)
|
|
||||||
@LargeTest
|
|
||||||
class SettingsActivityReaderTest {
|
|
||||||
@get:Rule
|
|
||||||
val activityRule = ActivityScenarioRule(LoginActivity::class.java)
|
|
||||||
|
|
||||||
lateinit var context: Context
|
|
||||||
|
|
||||||
@Before
|
|
||||||
fun init() {
|
|
||||||
activityRule.scenario.onActivity { activity ->
|
|
||||||
context = activity.window.context
|
|
||||||
}
|
|
||||||
loginAndInitHome()
|
|
||||||
openActionBarOverflowOrOptionsMenu(
|
|
||||||
ApplicationProvider.getApplicationContext(),
|
|
||||||
)
|
|
||||||
onView(withText(R.string.title_activity_settings)).perform(click())
|
|
||||||
onView(withText(R.string.pref_header_viewer)).perform(click())
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testReader() {
|
|
||||||
onView(withSettingsCheckboxFrame(R.string.pref_switch_actions_pager_scroll)).check(
|
|
||||||
matches(
|
|
||||||
allOf(
|
|
||||||
isDisplayed(),
|
|
||||||
not(
|
|
||||||
isChecked(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
onView(withText(R.string.pref_content_reader_font_size)).check(matches(isDisplayed()))
|
|
||||||
onView(withText(R.string.settings_reader_font)).check(matches(isDisplayed()))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testReaderActions() {
|
|
||||||
onView(withText(R.string.pref_switch_actions_pager_scroll_off)).check(
|
|
||||||
matches(
|
|
||||||
isDisplayed(),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
onView(withSettingsCheckboxFrame(R.string.pref_switch_actions_pager_scroll)).perform(click())
|
|
||||||
onView(withText(R.string.pref_switch_actions_pager_scroll_on)).check(
|
|
||||||
matches(
|
|
||||||
isDisplayed(),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
onView(withText(R.string.pref_content_reader_font_size)).perform(click())
|
|
||||||
changeAndCancelSetting("16", "10") {
|
|
||||||
onView(withText(R.string.pref_content_reader_font_size)).perform(click())
|
|
||||||
}
|
|
||||||
changeAndSaveSetting("16", "10") {
|
|
||||||
onView(withText(R.string.pref_content_reader_font_size)).perform(click())
|
|
||||||
}
|
|
||||||
|
|
||||||
testPreferencesFromArray(context, R.array.preloaded_fonts_values) {
|
|
||||||
onView(withText(R.string.settings_reader_font)).perform(click())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,92 +0,0 @@
|
|||||||
package bou.amine.apps.readerforselfossv2.android
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import androidx.test.espresso.Espresso.onView
|
|
||||||
import androidx.test.espresso.action.ViewActions.click
|
|
||||||
import androidx.test.espresso.assertion.ViewAssertions.matches
|
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
|
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.isSelected
|
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.withText
|
|
||||||
import androidx.test.ext.junit.rules.ActivityScenarioRule
|
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
|
||||||
import androidx.test.filters.LargeTest
|
|
||||||
import org.hamcrest.CoreMatchers.allOf
|
|
||||||
import org.hamcrest.CoreMatchers.not
|
|
||||||
import org.junit.Before
|
|
||||||
import org.junit.Rule
|
|
||||||
import org.junit.Test
|
|
||||||
import org.junit.runner.RunWith
|
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4::class)
|
|
||||||
@LargeTest
|
|
||||||
class SettingsActivityTest {
|
|
||||||
@get:Rule
|
|
||||||
val activityRule = ActivityScenarioRule(LoginActivity::class.java)
|
|
||||||
lateinit var context: Context
|
|
||||||
|
|
||||||
@Before
|
|
||||||
fun init() {
|
|
||||||
activityRule.scenario.onActivity { activity ->
|
|
||||||
context = activity.window.context
|
|
||||||
}
|
|
||||||
loginAndInitHome()
|
|
||||||
openMenu()
|
|
||||||
onView(withText(R.string.title_activity_settings)).perform(click())
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testAllSettings() {
|
|
||||||
onView(withText(R.string.pref_header_general)).check(matches(isDisplayed()))
|
|
||||||
onView(withText(R.string.pref_header_viewer)).check(matches(isDisplayed()))
|
|
||||||
onView(withText(R.string.pref_header_offline)).check(matches(isDisplayed()))
|
|
||||||
onView(withText(R.string.pref_header_theme)).check(matches(isDisplayed()))
|
|
||||||
onView(withText(R.string.pref_header_links)).check(matches(isDisplayed()))
|
|
||||||
onView(withText(R.string.pref_switch_disable_acra)).check(
|
|
||||||
matches(
|
|
||||||
allOf(
|
|
||||||
isDisplayed(),
|
|
||||||
not(isSelected()),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
onView(withText(R.string.action_about)).check(matches(isDisplayed()))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testThemes() {
|
|
||||||
testPreferencesFromArray(context, R.array.ModeTitles) {
|
|
||||||
onView(withText(R.string.pref_header_theme)).perform(click())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testExperimentail() {
|
|
||||||
onView(withText(R.string.pref_header_experimental)).perform(click())
|
|
||||||
changeAndCancelSetting("", "10") {
|
|
||||||
onView(withText(R.string.pref_api_timeout)).perform(click())
|
|
||||||
}
|
|
||||||
changeAndSaveSetting("", "10") {
|
|
||||||
onView(withText(R.string.pref_api_timeout)).perform(click())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testBugReports() {
|
|
||||||
onView(withText(R.string.pref_switch_disable_acra)).perform(click())
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testLinks() {
|
|
||||||
onView(withText(R.string.pref_header_links)).perform(click())
|
|
||||||
onView(withText(R.string.issue_tracker_link)).check(matches(isDisplayed()))
|
|
||||||
onView(withText(R.string.issue_tracker_summary)).check(matches(isDisplayed()))
|
|
||||||
onView(withText(R.string.source_code)).check(matches(isDisplayed()))
|
|
||||||
onView(withText(R.string.translation)).check(matches(isDisplayed()))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testAbout() {
|
|
||||||
onView(withText(R.string.action_about)).perform(click())
|
|
||||||
onView(withText("ACRA")).check(matches(isDisplayed()))
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,82 +0,0 @@
|
|||||||
package bou.amine.apps.readerforselfossv2.android
|
|
||||||
|
|
||||||
import androidx.test.espresso.AmbiguousViewMatcherException
|
|
||||||
import androidx.test.espresso.Espresso.onView
|
|
||||||
import androidx.test.espresso.action.ViewActions
|
|
||||||
import androidx.test.espresso.action.ViewActions.click
|
|
||||||
import androidx.test.espresso.action.ViewActions.swipeDown
|
|
||||||
import androidx.test.espresso.assertion.ViewAssertions.doesNotExist
|
|
||||||
import androidx.test.espresso.assertion.ViewAssertions.matches
|
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
|
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.isRoot
|
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.withId
|
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.withText
|
|
||||||
import androidx.test.ext.junit.rules.ActivityScenarioRule
|
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
|
||||||
import androidx.test.filters.LargeTest
|
|
||||||
import org.junit.After
|
|
||||||
import org.junit.Before
|
|
||||||
import org.junit.Rule
|
|
||||||
import org.junit.Test
|
|
||||||
import org.junit.runner.RunWith
|
|
||||||
import java.util.UUID
|
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4::class)
|
|
||||||
@LargeTest
|
|
||||||
class SourcesActivityTest {
|
|
||||||
@get:Rule
|
|
||||||
val activityRule = ActivityScenarioRule(LoginActivity::class.java)
|
|
||||||
|
|
||||||
lateinit var sourceName: String
|
|
||||||
|
|
||||||
@Before
|
|
||||||
fun init() {
|
|
||||||
sourceName = UUID.randomUUID().toString().substring(0, 15)
|
|
||||||
|
|
||||||
loginAndInitHome()
|
|
||||||
goToSources()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun addSource() {
|
|
||||||
testAddSourceWithUrl(
|
|
||||||
"https://lorem-rss.herokuapp.com/feed?unit=year&interval=1&length=10",
|
|
||||||
sourceName,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Suppress("detekt:SwallowedException")
|
|
||||||
@Test
|
|
||||||
fun addSourceCheckContent() {
|
|
||||||
testAddSourceWithUrl("https://news.google.com/rss?hl=en-US&gl=US&ceid=US:en", sourceName)
|
|
||||||
onView(isRoot()).perform(ViewActions.pressBack())
|
|
||||||
openMenu()
|
|
||||||
onView(withText(R.string.menu_home_refresh)).perform(click())
|
|
||||||
onView(withText(R.string.refresh_dialog_message)).check(matches(isDisplayed()))
|
|
||||||
onView(
|
|
||||||
withId(android.R.id.button1),
|
|
||||||
).perform(click())
|
|
||||||
Thread.sleep(10000)
|
|
||||||
onView(withId(R.id.swipeRefreshLayout)).perform(swipeDown())
|
|
||||||
Thread.sleep(2000)
|
|
||||||
try {
|
|
||||||
onView(withId(R.id.sourceTitleAndDate)).check(matches(isDisplayed()))
|
|
||||||
} catch (e: AmbiguousViewMatcherException) {
|
|
||||||
assert(true)
|
|
||||||
}
|
|
||||||
goToSources()
|
|
||||||
}
|
|
||||||
|
|
||||||
@After
|
|
||||||
fun deleteTheCreatedSource() {
|
|
||||||
onView(withText(sourceName)).check(matches(isDisplayed()))
|
|
||||||
onView(withId(R.id.deleteBtn)).perform(click())
|
|
||||||
onView(withText(sourceName)).check(doesNotExist())
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun goToSources() {
|
|
||||||
openMenu()
|
|
||||||
onView(withText(R.string.menu_home_sources))
|
|
||||||
.perform(click())
|
|
||||||
}
|
|
||||||
}
|
|
@ -2,7 +2,6 @@
|
|||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools">
|
xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
|
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||||
|
|
||||||
@ -70,8 +69,7 @@
|
|||||||
android:name=".ReaderActivity">
|
android:name=".ReaderActivity">
|
||||||
</activity>
|
</activity>
|
||||||
<activity
|
<activity
|
||||||
android:name=".ImageActivity"
|
android:name=".ImageActivity">
|
||||||
android:theme="@style/Theme.AppCompat.ImageActivity">
|
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<meta-data android:name="android.webkit.WebView.MetricsOptOut"
|
<meta-data android:name="android.webkit.WebView.MetricsOptOut"
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package bou.amine.apps.readerforselfossv2.android.utils.acra
|
package bou.amine.apps.readerforselfossv2.android
|
||||||
|
|
||||||
import org.acra.ACRA
|
import org.acra.ACRA
|
||||||
import org.acra.ktx.sendSilentlyWithAcra
|
import org.acra.ktx.sendSilentlyWithAcra
|
@ -12,11 +12,7 @@ import androidx.appcompat.app.AppCompatActivity
|
|||||||
import androidx.appcompat.widget.SearchView
|
import androidx.appcompat.widget.SearchView
|
||||||
import androidx.core.view.doOnNextLayout
|
import androidx.core.view.doOnNextLayout
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.recyclerview.widget.DividerItemDecoration
|
import androidx.recyclerview.widget.*
|
||||||
import androidx.recyclerview.widget.GridLayoutManager
|
|
||||||
import androidx.recyclerview.widget.ItemTouchHelper
|
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
|
||||||
import androidx.recyclerview.widget.StaggeredGridLayoutManager
|
|
||||||
import androidx.work.Constraints
|
import androidx.work.Constraints
|
||||||
import androidx.work.ExistingPeriodicWorkPolicy
|
import androidx.work.ExistingPeriodicWorkPolicy
|
||||||
import androidx.work.PeriodicWorkRequestBuilder
|
import androidx.work.PeriodicWorkRequestBuilder
|
||||||
@ -28,10 +24,8 @@ import bou.amine.apps.readerforselfossv2.android.background.LoadingWorker
|
|||||||
import bou.amine.apps.readerforselfossv2.android.databinding.ActivityHomeBinding
|
import bou.amine.apps.readerforselfossv2.android.databinding.ActivityHomeBinding
|
||||||
import bou.amine.apps.readerforselfossv2.android.fragments.FilterSheetFragment
|
import bou.amine.apps.readerforselfossv2.android.fragments.FilterSheetFragment
|
||||||
import bou.amine.apps.readerforselfossv2.android.settings.SettingsActivity
|
import bou.amine.apps.readerforselfossv2.android.settings.SettingsActivity
|
||||||
import bou.amine.apps.readerforselfossv2.android.testing.CountingIdlingResourceSingleton
|
|
||||||
import bou.amine.apps.readerforselfossv2.android.utils.bottombar.maybeShow
|
import bou.amine.apps.readerforselfossv2.android.utils.bottombar.maybeShow
|
||||||
import bou.amine.apps.readerforselfossv2.android.utils.bottombar.removeBadge
|
import bou.amine.apps.readerforselfossv2.android.utils.bottombar.removeBadge
|
||||||
import bou.amine.apps.readerforselfossv2.android.utils.openUrlInBrowserAsNewTask
|
|
||||||
import bou.amine.apps.readerforselfossv2.model.SelfossModel
|
import bou.amine.apps.readerforselfossv2.model.SelfossModel
|
||||||
import bou.amine.apps.readerforselfossv2.repository.Repository
|
import bou.amine.apps.readerforselfossv2.repository.Repository
|
||||||
import bou.amine.apps.readerforselfossv2.service.AppSettingsService
|
import bou.amine.apps.readerforselfossv2.service.AppSettingsService
|
||||||
@ -42,19 +36,15 @@ import com.ashokvarma.bottomnavigation.TextBadgeItem
|
|||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.runBlocking
|
|
||||||
import org.kodein.di.DIAware
|
import org.kodein.di.DIAware
|
||||||
import org.kodein.di.android.closestDI
|
import org.kodein.di.android.closestDI
|
||||||
import org.kodein.di.instance
|
import org.kodein.di.instance
|
||||||
import java.security.MessageDigest
|
import java.security.MessageDigest
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
private const val MIN_WIDTH_CARD_DP = 300
|
|
||||||
|
|
||||||
class HomeActivity :
|
class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAware {
|
||||||
AppCompatActivity(),
|
|
||||||
SearchView.OnQueryTextListener,
|
|
||||||
DIAware {
|
|
||||||
private var items: ArrayList<SelfossModel.Item> = ArrayList()
|
private var items: ArrayList<SelfossModel.Item> = ArrayList()
|
||||||
|
|
||||||
private var elementsShown: ItemType = ItemType.UNREAD
|
private var elementsShown: ItemType = ItemType.UNREAD
|
||||||
@ -68,30 +58,29 @@ class HomeActivity :
|
|||||||
private lateinit var recyclerViewScrollListener: RecyclerView.OnScrollListener
|
private lateinit var recyclerViewScrollListener: RecyclerView.OnScrollListener
|
||||||
private lateinit var binding: ActivityHomeBinding
|
private lateinit var binding: ActivityHomeBinding
|
||||||
|
|
||||||
private var recyclerAdapter: ItemsAdapter<out RecyclerView.ViewHolder>? = null
|
private var recyclerAdapter: RecyclerView.Adapter<*>? = null
|
||||||
|
|
||||||
private var fromTabShortcut: Boolean = false
|
private var fromTabShortcut: Boolean = false
|
||||||
|
|
||||||
private val settingsLauncher =
|
private val settingsLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
||||||
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
appSettingsService.refreshUserSettings()
|
||||||
appSettingsService.refreshUserSettings()
|
}
|
||||||
}
|
|
||||||
|
|
||||||
override val di by closestDI()
|
override val di by closestDI()
|
||||||
private val repository: Repository by instance()
|
private val repository : Repository by instance()
|
||||||
private val appSettingsService: AppSettingsService by instance()
|
private val appSettingsService : AppSettingsService by instance()
|
||||||
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
binding = ActivityHomeBinding.inflate(layoutInflater)
|
binding = ActivityHomeBinding.inflate(layoutInflater)
|
||||||
val view = binding.root
|
val view = binding.root
|
||||||
|
|
||||||
fromTabShortcut = intent.getIntExtra("shortcutTab", -1) != -1
|
fromTabShortcut = intent.getIntExtra("shortcutTab", -1) != -1
|
||||||
repository.offlineOverride = intent.getBooleanExtra("startOffline", false)
|
repository.offlineOverride = intent.getBooleanExtra("startOffline", false)
|
||||||
|
|
||||||
if (fromTabShortcut) {
|
if (fromTabShortcut) {
|
||||||
elementsShown =
|
elementsShown = ItemType.fromInt(intent.getIntExtra("shortcutTab", ItemType.UNREAD.position))
|
||||||
ItemType.fromInt(intent.getIntExtra("shortcutTab", ItemType.UNREAD.position))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setContentView(view)
|
setContentView(view)
|
||||||
@ -102,11 +91,10 @@ class HomeActivity :
|
|||||||
|
|
||||||
handleSwipeRefreshLayout()
|
handleSwipeRefreshLayout()
|
||||||
|
|
||||||
|
|
||||||
if (appSettingsService.isItemCachingEnabled()) {
|
if (appSettingsService.isItemCachingEnabled()) {
|
||||||
CountingIdlingResourceSingleton.increment()
|
|
||||||
CoroutineScope(Dispatchers.Main).launch {
|
CoroutineScope(Dispatchers.Main).launch {
|
||||||
repository.tryToCacheItemsAndGetNewOnes()
|
repository.tryToCacheItemsAndGetNewOnes()
|
||||||
CountingIdlingResourceSingleton.decrement()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -115,54 +103,42 @@ class HomeActivity :
|
|||||||
binding.swipeRefreshLayout.setColorSchemeResources(
|
binding.swipeRefreshLayout.setColorSchemeResources(
|
||||||
R.color.refresh_progress_1,
|
R.color.refresh_progress_1,
|
||||||
R.color.refresh_progress_2,
|
R.color.refresh_progress_2,
|
||||||
R.color.refresh_progress_3,
|
R.color.refresh_progress_3
|
||||||
)
|
)
|
||||||
binding.swipeRefreshLayout.setOnRefreshListener {
|
binding.swipeRefreshLayout.setOnRefreshListener {
|
||||||
repository.offlineOverride = false
|
repository.offlineOverride = false
|
||||||
lastFetchDone = false
|
lastFetchDone = false
|
||||||
CountingIdlingResourceSingleton.increment()
|
|
||||||
CoroutineScope(Dispatchers.Main).launch {
|
CoroutineScope(Dispatchers.Main).launch {
|
||||||
getElementsAccordingToTab()
|
getElementsAccordingToTab()
|
||||||
binding.swipeRefreshLayout.isRefreshing = false
|
binding.swipeRefreshLayout.isRefreshing = false
|
||||||
CountingIdlingResourceSingleton.decrement()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val swipeDirs =
|
|
||||||
if (appSettingsService.getPublicAccess()) {
|
|
||||||
0
|
|
||||||
} else {
|
|
||||||
ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT
|
|
||||||
}
|
|
||||||
|
|
||||||
val simpleItemTouchCallback =
|
val simpleItemTouchCallback =
|
||||||
object : ItemTouchHelper.SimpleCallback(
|
object : ItemTouchHelper.SimpleCallback(
|
||||||
0,
|
0,
|
||||||
swipeDirs,
|
ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT
|
||||||
) {
|
) {
|
||||||
override fun getSwipeDirs(
|
override fun getSwipeDirs(
|
||||||
recyclerView: RecyclerView,
|
recyclerView: RecyclerView,
|
||||||
viewHolder: RecyclerView.ViewHolder,
|
viewHolder: RecyclerView.ViewHolder
|
||||||
): Int =
|
): Int =
|
||||||
if (elementsShown == ItemType.STARRED) {
|
if (elementsShown == ItemType.STARRED) {
|
||||||
0
|
0
|
||||||
} else {
|
} else {
|
||||||
super.getSwipeDirs(
|
super.getSwipeDirs(
|
||||||
recyclerView,
|
recyclerView,
|
||||||
viewHolder,
|
viewHolder
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onMove(
|
override fun onMove(
|
||||||
recyclerView: RecyclerView,
|
recyclerView: RecyclerView,
|
||||||
viewHolder: RecyclerView.ViewHolder,
|
viewHolder: RecyclerView.ViewHolder,
|
||||||
target: RecyclerView.ViewHolder,
|
target: RecyclerView.ViewHolder
|
||||||
): Boolean = false
|
): Boolean = false
|
||||||
|
|
||||||
override fun onSwiped(
|
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, swipeDir: Int) {
|
||||||
viewHolder: RecyclerView.ViewHolder,
|
|
||||||
swipeDir: Int,
|
|
||||||
) {
|
|
||||||
val position = viewHolder.bindingAdapterPosition
|
val position = viewHolder.bindingAdapterPosition
|
||||||
val i = items.elementAtOrNull(position)
|
val i = items.elementAtOrNull(position)
|
||||||
|
|
||||||
@ -176,12 +152,11 @@ class HomeActivity :
|
|||||||
getElementsAccordingToTab()
|
getElementsAccordingToTab()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Toast
|
Toast.makeText(
|
||||||
.makeText(
|
this@HomeActivity,
|
||||||
this@HomeActivity,
|
"Found null when swiping at positon $position.",
|
||||||
"Found null when swiping at positon $position.",
|
Toast.LENGTH_LONG
|
||||||
Toast.LENGTH_LONG,
|
).show()
|
||||||
).show()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -189,10 +164,7 @@ class HomeActivity :
|
|||||||
ItemTouchHelper(simpleItemTouchCallback).attachToRecyclerView(binding.recyclerView)
|
ItemTouchHelper(simpleItemTouchCallback).attachToRecyclerView(binding.recyclerView)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateBottomBarBadgeCount(
|
private fun updateBottomBarBadgeCount(badge: TextBadgeItem, count: Int) {
|
||||||
badge: TextBadgeItem,
|
|
||||||
count: Int,
|
|
||||||
) {
|
|
||||||
if (count > 0) {
|
if (count > 0) {
|
||||||
badge
|
badge
|
||||||
.setText(count.toString())
|
.setText(count.toString())
|
||||||
@ -202,23 +174,17 @@ class HomeActivity :
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("detekt:LongMethod")
|
|
||||||
private fun handleBottomBar() {
|
private fun handleBottomBar() {
|
||||||
tabNewBadge =
|
|
||||||
TextBadgeItem()
|
tabNewBadge = TextBadgeItem()
|
||||||
.setText("")
|
.setText("")
|
||||||
.setHideOnSelect(false)
|
.setHideOnSelect(false).hide(false)
|
||||||
.hide(false)
|
tabArchiveBadge = TextBadgeItem()
|
||||||
tabArchiveBadge =
|
.setText("")
|
||||||
TextBadgeItem()
|
.setHideOnSelect(false).hide(false)
|
||||||
.setText("")
|
tabStarredBadge = TextBadgeItem()
|
||||||
.setHideOnSelect(false)
|
.setText("")
|
||||||
.hide(false)
|
.setHideOnSelect(false).hide(false)
|
||||||
tabStarredBadge =
|
|
||||||
TextBadgeItem()
|
|
||||||
.setText("")
|
|
||||||
.setHideOnSelect(false)
|
|
||||||
.hide(false)
|
|
||||||
|
|
||||||
if (appSettingsService.isDisplayUnreadCountEnabled()) {
|
if (appSettingsService.isDisplayUnreadCountEnabled()) {
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
@ -245,17 +211,19 @@ class HomeActivity :
|
|||||||
val tabNew =
|
val tabNew =
|
||||||
BottomNavigationItem(
|
BottomNavigationItem(
|
||||||
R.drawable.ic_tab_fiber_new_black_24dp,
|
R.drawable.ic_tab_fiber_new_black_24dp,
|
||||||
getString(R.string.tab_new),
|
getString(R.string.tab_new)
|
||||||
).setBadgeItem(tabNewBadge)
|
)
|
||||||
|
.setBadgeItem(tabNewBadge)
|
||||||
val tabArchive =
|
val tabArchive =
|
||||||
BottomNavigationItem(
|
BottomNavigationItem(
|
||||||
R.drawable.ic_tab_archive_black_24dp,
|
R.drawable.ic_tab_archive_black_24dp,
|
||||||
getString(R.string.tab_read),
|
getString(R.string.tab_read)
|
||||||
).setBadgeItem(tabArchiveBadge)
|
)
|
||||||
|
.setBadgeItem(tabArchiveBadge)
|
||||||
val tabStarred =
|
val tabStarred =
|
||||||
BottomNavigationItem(
|
BottomNavigationItem(
|
||||||
R.drawable.ic_tab_favorite_black_24dp,
|
R.drawable.ic_tab_favorite_black_24dp,
|
||||||
getString(R.string.tab_favs),
|
getString(R.string.tab_favs)
|
||||||
).setActiveColorResource(R.color.pink)
|
).setActiveColorResource(R.color.pink)
|
||||||
.setBadgeItem(tabStarredBadge)
|
.setBadgeItem(tabStarredBadge)
|
||||||
|
|
||||||
@ -285,28 +253,28 @@ class HomeActivity :
|
|||||||
|
|
||||||
handleBottomBarActions()
|
handleBottomBarActions()
|
||||||
|
|
||||||
handleGdprDialog(appSettingsService.settings.getBoolean("GDPR_shown", false))
|
handleGDPRDialog(appSettingsService.settings.getBoolean("GDPR_shown", false))
|
||||||
|
|
||||||
handleRecurringTask()
|
handleRecurringTask()
|
||||||
CountingIdlingResourceSingleton.increment()
|
|
||||||
CoroutineScope(Dispatchers.Main).launch {
|
CoroutineScope(Dispatchers.Main).launch {
|
||||||
repository.handleDBActions()
|
repository.handleDBActions()
|
||||||
CountingIdlingResourceSingleton.decrement()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getElementsAccordingToTab()
|
getElementsAccordingToTab()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleGdprDialog(gdprShown: Boolean) {
|
|
||||||
|
private fun handleGDPRDialog(GDPRShown: Boolean) {
|
||||||
val messageDigest: MessageDigest = MessageDigest.getInstance("SHA-256")
|
val messageDigest: MessageDigest = MessageDigest.getInstance("SHA-256")
|
||||||
messageDigest.update(appSettingsService.getBaseUrl().toByteArray())
|
messageDigest.update(appSettingsService.getBaseUrl().toByteArray())
|
||||||
if (!gdprShown) {
|
if (!GDPRShown) {
|
||||||
val alertDialog = AlertDialog.Builder(this).create()
|
val alertDialog = AlertDialog.Builder(this).create()
|
||||||
alertDialog.setTitle(getString(R.string.gdpr_dialog_title))
|
alertDialog.setTitle(getString(R.string.gdpr_dialog_title))
|
||||||
alertDialog.setMessage(getString(R.string.gdpr_dialog_message))
|
alertDialog.setMessage(getString(R.string.gdpr_dialog_message))
|
||||||
alertDialog.setButton(
|
alertDialog.setButton(
|
||||||
AlertDialog.BUTTON_NEUTRAL,
|
AlertDialog.BUTTON_NEUTRAL,
|
||||||
"OK",
|
"OK"
|
||||||
) { dialog, _ ->
|
) { dialog, _ ->
|
||||||
appSettingsService.settings.putBoolean("GDPR_shown", true)
|
appSettingsService.settings.putBoolean("GDPR_shown", true)
|
||||||
dialog.dismiss()
|
dialog.dismiss()
|
||||||
@ -317,86 +285,83 @@ class HomeActivity :
|
|||||||
|
|
||||||
private fun reloadLayoutManager() {
|
private fun reloadLayoutManager() {
|
||||||
val currentManager = binding.recyclerView.layoutManager
|
val currentManager = binding.recyclerView.layoutManager
|
||||||
|
val layoutManager: RecyclerView.LayoutManager
|
||||||
|
|
||||||
fun gridLayoutManager() {
|
// This will only update the layout manager if settings changed
|
||||||
val layoutManager =
|
|
||||||
GridLayoutManager(
|
|
||||||
this,
|
|
||||||
calculateNoOfColumns(),
|
|
||||||
)
|
|
||||||
binding.recyclerView.layoutManager = layoutManager
|
|
||||||
}
|
|
||||||
|
|
||||||
fun staggererdGridLayoutManager() {
|
|
||||||
var layoutManager =
|
|
||||||
StaggeredGridLayoutManager(
|
|
||||||
calculateNoOfColumns(),
|
|
||||||
StaggeredGridLayoutManager.VERTICAL,
|
|
||||||
)
|
|
||||||
layoutManager.gapStrategy =
|
|
||||||
StaggeredGridLayoutManager.GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS
|
|
||||||
binding.recyclerView.layoutManager = layoutManager
|
|
||||||
}
|
|
||||||
|
|
||||||
when (currentManager) {
|
when (currentManager) {
|
||||||
is StaggeredGridLayoutManager ->
|
is StaggeredGridLayoutManager ->
|
||||||
if (!appSettingsService.isCardViewEnabled()) {
|
if (!appSettingsService.isCardViewEnabled()) {
|
||||||
gridLayoutManager()
|
layoutManager = GridLayoutManager(
|
||||||
|
this,
|
||||||
|
calculateNoOfColumns()
|
||||||
|
)
|
||||||
|
binding.recyclerView.layoutManager = layoutManager
|
||||||
}
|
}
|
||||||
|
|
||||||
is GridLayoutManager ->
|
is GridLayoutManager ->
|
||||||
if (appSettingsService.isCardViewEnabled()) {
|
if (appSettingsService.isCardViewEnabled()) {
|
||||||
staggererdGridLayoutManager()
|
layoutManager = StaggeredGridLayoutManager(
|
||||||
|
calculateNoOfColumns(),
|
||||||
|
StaggeredGridLayoutManager.VERTICAL
|
||||||
|
)
|
||||||
|
layoutManager.gapStrategy =
|
||||||
|
StaggeredGridLayoutManager.GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS
|
||||||
|
binding.recyclerView.layoutManager = layoutManager
|
||||||
}
|
}
|
||||||
|
|
||||||
else ->
|
else ->
|
||||||
if (currentManager == null) {
|
if (currentManager == null) {
|
||||||
if (!appSettingsService.isCardViewEnabled()) {
|
if (!appSettingsService.isCardViewEnabled()) {
|
||||||
gridLayoutManager()
|
layoutManager = GridLayoutManager(
|
||||||
|
this,
|
||||||
|
calculateNoOfColumns()
|
||||||
|
)
|
||||||
|
binding.recyclerView.layoutManager = layoutManager
|
||||||
} else {
|
} else {
|
||||||
staggererdGridLayoutManager()
|
layoutManager = StaggeredGridLayoutManager(
|
||||||
|
calculateNoOfColumns(),
|
||||||
|
StaggeredGridLayoutManager.VERTICAL
|
||||||
|
)
|
||||||
|
layoutManager.gapStrategy =
|
||||||
|
StaggeredGridLayoutManager.GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS
|
||||||
|
binding.recyclerView.layoutManager = layoutManager
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleBottomBarActions() {
|
private fun handleBottomBarActions() {
|
||||||
binding.bottomBar.setTabSelectedListener(
|
binding.bottomBar.setTabSelectedListener(object : BottomNavigationBar.OnTabSelectedListener {
|
||||||
object : BottomNavigationBar.OnTabSelectedListener {
|
override fun onTabUnselected(position: Int) = Unit
|
||||||
override fun onTabUnselected(position: Int) = Unit
|
|
||||||
|
|
||||||
override fun onTabReselected(position: Int) {
|
override fun onTabReselected(position: Int) {
|
||||||
when (val layoutManager = binding.recyclerView.adapter) {
|
|
||||||
is StaggeredGridLayoutManager ->
|
|
||||||
if (layoutManager.findFirstCompletelyVisibleItemPositions(null)[0] == 0) {
|
|
||||||
getElementsAccordingToTab()
|
|
||||||
} else {
|
|
||||||
layoutManager.scrollToPositionWithOffset(0, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
is GridLayoutManager ->
|
when (val layoutManager = binding.recyclerView.adapter) {
|
||||||
if (layoutManager.findFirstCompletelyVisibleItemPosition() == 0) {
|
is StaggeredGridLayoutManager ->
|
||||||
getElementsAccordingToTab()
|
if (layoutManager.findFirstCompletelyVisibleItemPositions(null)[0] == 0) {
|
||||||
} else {
|
getElementsAccordingToTab()
|
||||||
layoutManager.scrollToPositionWithOffset(0, 0)
|
} else {
|
||||||
}
|
layoutManager.scrollToPositionWithOffset(0, 0)
|
||||||
|
}
|
||||||
else -> Unit
|
is GridLayoutManager ->
|
||||||
}
|
if (layoutManager.findFirstCompletelyVisibleItemPosition() == 0) {
|
||||||
|
getElementsAccordingToTab()
|
||||||
|
} else {
|
||||||
|
layoutManager.scrollToPositionWithOffset(0, 0)
|
||||||
|
}
|
||||||
|
else -> Unit
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun onTabSelected(position: Int) {
|
override fun onTabSelected(position: Int) {
|
||||||
offset = 0
|
offset = 0
|
||||||
lastFetchDone = false
|
lastFetchDone = false
|
||||||
|
|
||||||
elementsShown = ItemType.fromInt(position + 1)
|
elementsShown = ItemType.fromInt(position + 1)
|
||||||
getElementsAccordingToTab()
|
getElementsAccordingToTab()
|
||||||
binding.recyclerView.scrollToPosition(0)
|
binding.recyclerView.scrollToPosition(0)
|
||||||
|
|
||||||
fetchOnEmptyList()
|
fetchOnEmptyList()
|
||||||
}
|
}
|
||||||
},
|
})
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun fetchOnEmptyList() {
|
fun fetchOnEmptyList() {
|
||||||
@ -406,38 +371,31 @@ class HomeActivity :
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun handleInfiniteScroll() {
|
private fun handleInfiniteScroll() {
|
||||||
recyclerViewScrollListener =
|
recyclerViewScrollListener = object : RecyclerView.OnScrollListener() {
|
||||||
object : RecyclerView.OnScrollListener() {
|
override fun onScrolled(localRecycler: RecyclerView, dx: Int, dy: Int) {
|
||||||
override fun onScrolled(
|
if (dy > 0) {
|
||||||
localRecycler: RecyclerView,
|
val lastVisibleItem = getLastVisibleItem()
|
||||||
dx: Int,
|
|
||||||
dy: Int,
|
|
||||||
) {
|
|
||||||
if (dy > 0) {
|
|
||||||
val lastVisibleItem = getLastVisibleItem()
|
|
||||||
|
|
||||||
if (lastVisibleItem == (items.size - 1) && items.size < maxItemNumber()) {
|
if (lastVisibleItem == (items.size - 1) && items.size < maxItemNumber()) {
|
||||||
getElementsAccordingToTab(appendResults = true)
|
getElementsAccordingToTab(appendResults = true)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
binding.recyclerView.clearOnScrollListeners()
|
binding.recyclerView.clearOnScrollListeners()
|
||||||
binding.recyclerView.addOnScrollListener(recyclerViewScrollListener)
|
binding.recyclerView.addOnScrollListener(recyclerViewScrollListener)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getLastVisibleItem(): Int =
|
private fun getLastVisibleItem() : Int {
|
||||||
when (val manager = binding.recyclerView.layoutManager) {
|
return when (val manager = binding.recyclerView.layoutManager) {
|
||||||
is StaggeredGridLayoutManager ->
|
is StaggeredGridLayoutManager -> manager.findLastCompletelyVisibleItemPositions(
|
||||||
manager
|
null
|
||||||
.findLastCompletelyVisibleItemPositions(
|
).last()
|
||||||
null,
|
|
||||||
).last()
|
|
||||||
|
|
||||||
is GridLayoutManager -> manager.findLastCompletelyVisibleItemPosition()
|
is GridLayoutManager -> manager.findLastCompletelyVisibleItemPosition()
|
||||||
else -> 0
|
else -> 0
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun mayBeEmpty() =
|
private fun mayBeEmpty() =
|
||||||
if (items.isEmpty()) {
|
if (items.isEmpty()) {
|
||||||
@ -446,87 +404,73 @@ class HomeActivity :
|
|||||||
binding.emptyText.visibility = View.GONE
|
binding.emptyText.visibility = View.GONE
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getElementsAccordingToTab(appendResults: Boolean = false) {
|
fun getElementsAccordingToTab(
|
||||||
offset =
|
appendResults: Boolean = false
|
||||||
if (appendResults && items.size > 0) {
|
) {
|
||||||
items.size - 1
|
offset = if (appendResults && items.size > 0) {
|
||||||
} else {
|
items.size - 1
|
||||||
0
|
} else {
|
||||||
}
|
0
|
||||||
|
}
|
||||||
firstVisible = if (appendResults) firstVisible else 0
|
firstVisible = if (appendResults) firstVisible else 0
|
||||||
|
|
||||||
getItems(appendResults, elementsShown)
|
getItems(appendResults, elementsShown)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getItems(
|
private fun getItems(appendResults: Boolean, itemType: ItemType) {
|
||||||
appendResults: Boolean,
|
|
||||||
itemType: ItemType,
|
|
||||||
) {
|
|
||||||
CountingIdlingResourceSingleton.increment()
|
|
||||||
CoroutineScope(Dispatchers.Main).launch {
|
CoroutineScope(Dispatchers.Main).launch {
|
||||||
binding.swipeRefreshLayout.isRefreshing = true
|
binding.swipeRefreshLayout.isRefreshing = true
|
||||||
repository.displayedItems = itemType
|
repository.displayedItems = itemType
|
||||||
items =
|
items = if (appendResults) {
|
||||||
if (appendResults) {
|
repository.getOlderItems()
|
||||||
repository.getOlderItems()
|
} else {
|
||||||
} else {
|
repository.getNewerItems()
|
||||||
repository.getNewerItems()
|
}
|
||||||
}
|
|
||||||
binding.swipeRefreshLayout.isRefreshing = false
|
binding.swipeRefreshLayout.isRefreshing = false
|
||||||
handleListResult()
|
handleListResult()
|
||||||
CountingIdlingResourceSingleton.decrement()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleListResult(appendResults: Boolean = false) {
|
private fun handleListResult(appendResults: Boolean = false) {
|
||||||
val oldManager = binding.recyclerView.layoutManager
|
|
||||||
if (appendResults) {
|
if (appendResults) {
|
||||||
firstVisible =
|
val oldManager = binding.recyclerView.layoutManager
|
||||||
when (oldManager) {
|
firstVisible = when (oldManager) {
|
||||||
is StaggeredGridLayoutManager ->
|
is StaggeredGridLayoutManager ->
|
||||||
oldManager.findFirstCompletelyVisibleItemPositions(null).last()
|
oldManager.findFirstCompletelyVisibleItemPositions(null).last()
|
||||||
|
is GridLayoutManager ->
|
||||||
is GridLayoutManager ->
|
oldManager.findFirstCompletelyVisibleItemPosition()
|
||||||
oldManager.findFirstCompletelyVisibleItemPosition()
|
else -> 0
|
||||||
|
}
|
||||||
else -> 0
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("detekt:ComplexCondition")
|
if (recyclerAdapter == null) {
|
||||||
if (recyclerAdapter == null ||
|
|
||||||
(
|
|
||||||
(recyclerAdapter is ItemListAdapter && appSettingsService.isCardViewEnabled()) ||
|
|
||||||
(recyclerAdapter is ItemCardAdapter && !appSettingsService.isCardViewEnabled())
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
if (appSettingsService.isCardViewEnabled()) {
|
if (appSettingsService.isCardViewEnabled()) {
|
||||||
recyclerAdapter =
|
recyclerAdapter =
|
||||||
ItemCardAdapter(
|
ItemCardAdapter(
|
||||||
this,
|
this,
|
||||||
items,
|
items,
|
||||||
) {
|
) {
|
||||||
updateItems(it)
|
updateItems(it)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
recyclerAdapter =
|
recyclerAdapter =
|
||||||
ItemListAdapter(
|
ItemListAdapter(
|
||||||
this,
|
this,
|
||||||
items,
|
items,
|
||||||
) {
|
) {
|
||||||
updateItems(it)
|
updateItems(it)
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.recyclerView.addItemDecoration(
|
binding.recyclerView.addItemDecoration(
|
||||||
DividerItemDecoration(
|
DividerItemDecoration(
|
||||||
this@HomeActivity,
|
this@HomeActivity,
|
||||||
DividerItemDecoration.VERTICAL,
|
DividerItemDecoration.VERTICAL
|
||||||
),
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
binding.recyclerView.adapter = recyclerAdapter
|
binding.recyclerView.adapter = recyclerAdapter
|
||||||
} else {
|
} else {
|
||||||
recyclerAdapter!!.updateAllItems(items)
|
(recyclerAdapter as ItemsAdapter<*>).updateAllItems(items)
|
||||||
}
|
}
|
||||||
|
|
||||||
reloadBadges()
|
reloadBadges()
|
||||||
@ -535,10 +479,8 @@ class HomeActivity :
|
|||||||
|
|
||||||
private fun reloadBadges() {
|
private fun reloadBadges() {
|
||||||
if (appSettingsService.isDisplayUnreadCountEnabled() || appSettingsService.isDisplayAllCountEnabled()) {
|
if (appSettingsService.isDisplayUnreadCountEnabled() || appSettingsService.isDisplayAllCountEnabled()) {
|
||||||
CountingIdlingResourceSingleton.increment()
|
|
||||||
CoroutineScope(Dispatchers.IO).launch {
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
repository.reloadBadges()
|
repository.reloadBadges()
|
||||||
CountingIdlingResourceSingleton.decrement()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -546,7 +488,7 @@ class HomeActivity :
|
|||||||
private fun calculateNoOfColumns(): Int {
|
private fun calculateNoOfColumns(): Int {
|
||||||
val displayMetrics = resources.displayMetrics
|
val displayMetrics = resources.displayMetrics
|
||||||
val dpWidth = displayMetrics.widthPixels / displayMetrics.density
|
val dpWidth = displayMetrics.widthPixels / displayMetrics.density
|
||||||
return (dpWidth / MIN_WIDTH_CARD_DP).toInt()
|
return (dpWidth / 300).toInt()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onQueryTextChange(p0: String?): Boolean {
|
override fun onQueryTextChange(p0: String?): Boolean {
|
||||||
@ -568,25 +510,16 @@ class HomeActivity :
|
|||||||
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
||||||
val inflater = menuInflater
|
val inflater = menuInflater
|
||||||
inflater.inflate(R.menu.home_menu, menu)
|
inflater.inflate(R.menu.home_menu, menu)
|
||||||
if (appSettingsService.getPublicAccess()) {
|
|
||||||
menu.removeItem(R.id.readAll)
|
|
||||||
menu.removeItem(R.id.action_sources)
|
|
||||||
}
|
|
||||||
|
|
||||||
val searchItem = menu.findItem(R.id.action_search)
|
val searchItem = menu.findItem(R.id.action_search)
|
||||||
val searchView = searchItem.actionView as SearchView
|
val searchView = searchItem.getActionView() as SearchView
|
||||||
searchView.setOnQueryTextListener(this)
|
searchView.setOnQueryTextListener(this)
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun needsConfirmation(
|
private fun needsConfirmation(titleRes: Int, messageRes: Int, doFn: () -> Unit) {
|
||||||
titleRes: Int,
|
AlertDialog.Builder(this@HomeActivity)
|
||||||
messageRes: Int,
|
|
||||||
doFn: () -> Unit,
|
|
||||||
) {
|
|
||||||
AlertDialog
|
|
||||||
.Builder(this@HomeActivity)
|
|
||||||
.setMessage(messageRes)
|
.setMessage(messageRes)
|
||||||
.setTitle(titleRes)
|
.setTitle(titleRes)
|
||||||
.setPositiveButton(android.R.string.ok) { _, _ -> doFn() }
|
.setPositiveButton(android.R.string.ok) { _, _ -> doFn() }
|
||||||
@ -595,106 +528,82 @@ class HomeActivity :
|
|||||||
.show()
|
.show()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("detekt:ReturnCount", "detekt:LongMethod")
|
|
||||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||||
when (item.itemId) {
|
when (item.itemId) {
|
||||||
R.id.issue_tracker -> {
|
|
||||||
baseContext.openUrlInBrowserAsNewTask(AppSettingsService.BUG_URL)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
R.id.action_filter -> {
|
R.id.action_filter -> {
|
||||||
val filterSheetFragment = FilterSheetFragment()
|
val filterSheetFragment = FilterSheetFragment()
|
||||||
filterSheetFragment.show(supportFragmentManager, FilterSheetFragment.TAG)
|
filterSheetFragment.show(supportFragmentManager, FilterSheetFragment.TAG)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
R.id.refresh -> {
|
R.id.refresh -> {
|
||||||
needsConfirmation(R.string.menu_home_refresh, R.string.refresh_dialog_message) {
|
needsConfirmation(R.string.menu_home_refresh, R.string.refresh_dialog_message) {
|
||||||
Toast.makeText(this, R.string.refresh_in_progress, Toast.LENGTH_SHORT).show()
|
Toast.makeText(this, R.string.refresh_in_progress, Toast.LENGTH_SHORT).show()
|
||||||
CountingIdlingResourceSingleton.increment()
|
|
||||||
CoroutineScope(Dispatchers.Main).launch {
|
CoroutineScope(Dispatchers.Main).launch {
|
||||||
val updatedRemote = repository.updateRemote()
|
val updatedRemote = repository.updateRemote()
|
||||||
if (updatedRemote) {
|
if (updatedRemote) {
|
||||||
Toast
|
Toast.makeText(
|
||||||
.makeText(
|
this@HomeActivity,
|
||||||
this@HomeActivity,
|
R.string.refresh_success_response, Toast.LENGTH_LONG
|
||||||
R.string.refresh_success_response,
|
)
|
||||||
Toast.LENGTH_LONG,
|
.show()
|
||||||
).show()
|
|
||||||
} else {
|
} else {
|
||||||
Toast
|
Toast.makeText(
|
||||||
.makeText(
|
this@HomeActivity,
|
||||||
this@HomeActivity,
|
R.string.refresh_failer_message,
|
||||||
R.string.refresh_failer_message,
|
Toast.LENGTH_SHORT
|
||||||
Toast.LENGTH_SHORT,
|
).show()
|
||||||
).show()
|
|
||||||
}
|
}
|
||||||
CountingIdlingResourceSingleton.decrement()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
R.id.readAll -> {
|
R.id.readAll -> {
|
||||||
if (elementsShown == ItemType.UNREAD) {
|
if (elementsShown == ItemType.UNREAD) {
|
||||||
needsConfirmation(R.string.readAll, R.string.markall_dialog_message) {
|
needsConfirmation(R.string.readAll, R.string.markall_dialog_message) {
|
||||||
binding.swipeRefreshLayout.isRefreshing = true
|
binding.swipeRefreshLayout.isRefreshing = true
|
||||||
CountingIdlingResourceSingleton.increment()
|
|
||||||
CoroutineScope(Dispatchers.Main).launch {
|
CoroutineScope(Dispatchers.Main).launch {
|
||||||
val success = repository.markAllAsRead(items)
|
val success = repository.markAllAsRead(items)
|
||||||
if (success) {
|
if (success) {
|
||||||
Toast
|
Toast.makeText(
|
||||||
.makeText(
|
this@HomeActivity,
|
||||||
this@HomeActivity,
|
R.string.all_posts_read,
|
||||||
R.string.all_posts_read,
|
Toast.LENGTH_SHORT
|
||||||
Toast.LENGTH_SHORT,
|
).show()
|
||||||
).show()
|
|
||||||
tabNewBadge.removeBadge()
|
tabNewBadge.removeBadge()
|
||||||
|
|
||||||
getElementsAccordingToTab()
|
getElementsAccordingToTab()
|
||||||
} else {
|
} else {
|
||||||
Toast
|
Toast.makeText(
|
||||||
.makeText(
|
this@HomeActivity,
|
||||||
this@HomeActivity,
|
R.string.all_posts_not_read,
|
||||||
R.string.all_posts_not_read,
|
Toast.LENGTH_SHORT
|
||||||
Toast.LENGTH_SHORT,
|
).show()
|
||||||
).show()
|
|
||||||
}
|
}
|
||||||
handleListResult()
|
handleListResult()
|
||||||
binding.swipeRefreshLayout.isRefreshing = false
|
binding.swipeRefreshLayout.isRefreshing = false
|
||||||
CountingIdlingResourceSingleton.decrement()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
R.id.action_disconnect -> {
|
R.id.action_disconnect -> {
|
||||||
needsConfirmation(
|
CoroutineScope(Dispatchers.Main).launch {
|
||||||
R.string.confirm_disconnect_title,
|
repository.logout()
|
||||||
R.string.confirm_disconnect_description,
|
|
||||||
) {
|
|
||||||
runBlocking {
|
|
||||||
repository.logout()
|
|
||||||
}
|
|
||||||
val intent = Intent(this, LoginActivity::class.java)
|
|
||||||
this.startActivity(intent)
|
|
||||||
finish()
|
|
||||||
}
|
}
|
||||||
|
this@HomeActivity.finish()
|
||||||
|
val intent = Intent(this, LoginActivity::class.java)
|
||||||
|
this.startActivity(intent)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
R.id.action_settings -> {
|
R.id.action_settings -> {
|
||||||
settingsLauncher.launch(Intent(this, SettingsActivity::class.java))
|
settingsLauncher.launch(Intent(this, SettingsActivity::class.java))
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
R.id.action_sources -> {
|
R.id.action_sources -> {
|
||||||
startActivity(Intent(this, SourcesActivity::class.java))
|
startActivity(Intent(this, SourcesActivity::class.java))
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> return super.onOptionsItemSelected(item)
|
else -> return super.onOptionsItemSelected(item)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -713,30 +622,20 @@ class HomeActivity :
|
|||||||
|
|
||||||
private fun handleRecurringTask() {
|
private fun handleRecurringTask() {
|
||||||
if (appSettingsService.isPeriodicRefreshEnabled()) {
|
if (appSettingsService.isPeriodicRefreshEnabled()) {
|
||||||
val myConstraints =
|
val myConstraints = Constraints.Builder()
|
||||||
Constraints
|
.setRequiresBatteryNotLow(true)
|
||||||
.Builder()
|
.setRequiresCharging(appSettingsService.isRefreshWhenChargingOnlyEnabled())
|
||||||
.setRequiresBatteryNotLow(true)
|
.setRequiresStorageNotLow(true)
|
||||||
.setRequiresCharging(appSettingsService.isRefreshWhenChargingOnlyEnabled())
|
.build()
|
||||||
.setRequiresStorageNotLow(true)
|
|
||||||
.build()
|
|
||||||
|
|
||||||
val backgroundWork =
|
val backgroundWork =
|
||||||
PeriodicWorkRequestBuilder<LoadingWorker>(
|
PeriodicWorkRequestBuilder<LoadingWorker>(appSettingsService.getRefreshMinutes(), TimeUnit.MINUTES)
|
||||||
appSettingsService.getRefreshMinutes(),
|
.setConstraints(myConstraints)
|
||||||
TimeUnit.MINUTES,
|
|
||||||
).setConstraints(myConstraints)
|
|
||||||
.addTag("selfoss-loading")
|
.addTag("selfoss-loading")
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
WorkManager
|
WorkManager.getInstance(baseContext).enqueueUniquePeriodicWork("selfoss-loading", ExistingPeriodicWorkPolicy.KEEP, backgroundWork)
|
||||||
.getInstance(
|
|
||||||
baseContext,
|
|
||||||
).enqueueUniquePeriodicWork(
|
|
||||||
"selfoss-loading",
|
|
||||||
ExistingPeriodicWorkPolicy.KEEP,
|
|
||||||
backgroundWork,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,7 +3,6 @@ package bou.amine.apps.readerforselfossv2.android
|
|||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.constraintlayout.motion.widget.MotionLayout
|
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.fragment.app.FragmentActivity
|
import androidx.fragment.app.FragmentActivity
|
||||||
import androidx.viewpager2.adapter.FragmentStateAdapter
|
import androidx.viewpager2.adapter.FragmentStateAdapter
|
||||||
@ -11,8 +10,8 @@ import bou.amine.apps.readerforselfossv2.android.databinding.ActivityImageBindin
|
|||||||
import bou.amine.apps.readerforselfossv2.android.fragments.ImageFragment
|
import bou.amine.apps.readerforselfossv2.android.fragments.ImageFragment
|
||||||
|
|
||||||
class ImageActivity : AppCompatActivity() {
|
class ImageActivity : AppCompatActivity() {
|
||||||
private lateinit var allImages: ArrayList<String>
|
private lateinit var allImages : ArrayList<String>
|
||||||
private var position: Int = 0
|
private var position : Int = 0
|
||||||
|
|
||||||
private lateinit var binding: ActivityImageBinding
|
private lateinit var binding: ActivityImageBinding
|
||||||
|
|
||||||
@ -24,6 +23,7 @@ class ImageActivity : AppCompatActivity() {
|
|||||||
setContentView(view)
|
setContentView(view)
|
||||||
|
|
||||||
setSupportActionBar(binding.toolBar)
|
setSupportActionBar(binding.toolBar)
|
||||||
|
supportActionBar?.setDisplayShowTitleEnabled(false)
|
||||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||||
|
|
||||||
allImages = intent.getStringArrayListExtra("allImages") as ArrayList<String>
|
allImages = intent.getStringArrayListExtra("allImages") as ArrayList<String>
|
||||||
@ -31,52 +31,12 @@ class ImageActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
binding.pager.adapter = ScreenSlidePagerAdapter(this)
|
binding.pager.adapter = ScreenSlidePagerAdapter(this)
|
||||||
binding.pager.setCurrentItem(position, false)
|
binding.pager.setCurrentItem(position, false)
|
||||||
|
|
||||||
val transitionListener =
|
|
||||||
object : MotionLayout.TransitionListener {
|
|
||||||
override fun onTransitionStarted(
|
|
||||||
motionLayout: MotionLayout?,
|
|
||||||
startId: Int,
|
|
||||||
endId: Int,
|
|
||||||
) {
|
|
||||||
// Nothing
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onTransitionChange(
|
|
||||||
motionLayout: MotionLayout?,
|
|
||||||
startId: Int,
|
|
||||||
endId: Int,
|
|
||||||
progress: Float,
|
|
||||||
) {
|
|
||||||
// Nothing
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onTransitionCompleted(
|
|
||||||
motionLayout: MotionLayout?,
|
|
||||||
currentId: Int,
|
|
||||||
) {
|
|
||||||
if (motionLayout?.currentState == binding.root.endState) {
|
|
||||||
onBackPressedDispatcher.onBackPressed()
|
|
||||||
overridePendingTransition(0, 0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onTransitionTrigger(
|
|
||||||
motionLayout: MotionLayout?,
|
|
||||||
triggerId: Int,
|
|
||||||
positive: Boolean,
|
|
||||||
progress: Float,
|
|
||||||
) {
|
|
||||||
// Nothing
|
|
||||||
}
|
|
||||||
}
|
|
||||||
binding.root.setTransitionListener(transitionListener)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||||
when (item.itemId) {
|
when (item.itemId) {
|
||||||
android.R.id.home -> {
|
android.R.id.home -> {
|
||||||
onBackPressedDispatcher.onBackPressed()
|
onBackPressed()
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -84,9 +44,8 @@ class ImageActivity : AppCompatActivity() {
|
|||||||
return super.onOptionsItemSelected(item)
|
return super.onOptionsItemSelected(item)
|
||||||
}
|
}
|
||||||
|
|
||||||
private inner class ScreenSlidePagerAdapter(
|
private inner class ScreenSlidePagerAdapter(fa: FragmentActivity) : FragmentStateAdapter(fa) {
|
||||||
fa: FragmentActivity,
|
|
||||||
) : FragmentStateAdapter(fa) {
|
|
||||||
override fun getItemCount(): Int = allImages.size
|
override fun getItemCount(): Int = allImages.size
|
||||||
|
|
||||||
override fun createFragment(position: Int): Fragment = ImageFragment.newInstance(allImages[position])
|
override fun createFragment(position: Int): Fragment = ImageFragment.newInstance(allImages[position])
|
||||||
|
@ -4,7 +4,6 @@ import android.animation.Animator
|
|||||||
import android.animation.AnimatorListenerAdapter
|
import android.animation.AnimatorListenerAdapter
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.net.Uri
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.text.TextUtils
|
import android.text.TextUtils
|
||||||
import android.view.Menu
|
import android.view.Menu
|
||||||
@ -17,7 +16,6 @@ import androidx.appcompat.app.AlertDialog
|
|||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.appcompat.app.AppCompatDelegate
|
import androidx.appcompat.app.AppCompatDelegate
|
||||||
import bou.amine.apps.readerforselfossv2.android.databinding.ActivityLoginBinding
|
import bou.amine.apps.readerforselfossv2.android.databinding.ActivityLoginBinding
|
||||||
import bou.amine.apps.readerforselfossv2.android.testing.CountingIdlingResourceSingleton
|
|
||||||
import bou.amine.apps.readerforselfossv2.android.utils.isBaseUrlInvalid
|
import bou.amine.apps.readerforselfossv2.android.utils.isBaseUrlInvalid
|
||||||
import bou.amine.apps.readerforselfossv2.repository.Repository
|
import bou.amine.apps.readerforselfossv2.repository.Repository
|
||||||
import bou.amine.apps.readerforselfossv2.service.AppSettingsService
|
import bou.amine.apps.readerforselfossv2.service.AppSettingsService
|
||||||
@ -30,11 +28,9 @@ import org.kodein.di.DIAware
|
|||||||
import org.kodein.di.android.closestDI
|
import org.kodein.di.android.closestDI
|
||||||
import org.kodein.di.instance
|
import org.kodein.di.instance
|
||||||
|
|
||||||
private const val MAX_INVALID_LOGIN_BEFORE_ALERT_DISPLAYED = 3
|
|
||||||
|
|
||||||
class LoginActivity :
|
class LoginActivity : AppCompatActivity(), DIAware {
|
||||||
AppCompatActivity(),
|
|
||||||
DIAware {
|
|
||||||
private var inValidCount: Int = 0
|
private var inValidCount: Int = 0
|
||||||
private var isWithLogin = false
|
private var isWithLogin = false
|
||||||
|
|
||||||
@ -44,6 +40,7 @@ class LoginActivity :
|
|||||||
private val repository: Repository by instance()
|
private val repository: Repository by instance()
|
||||||
private val appSettingsService: AppSettingsService by instance()
|
private val appSettingsService: AppSettingsService by instance()
|
||||||
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
@ -60,7 +57,29 @@ class LoginActivity :
|
|||||||
|
|
||||||
if (appSettingsService.getBaseUrl().isNotEmpty()) {
|
if (appSettingsService.getBaseUrl().isNotEmpty()) {
|
||||||
showProgress(true)
|
showProgress(true)
|
||||||
goToMain()
|
// This should be reverted when "old" users connected with a non-selfoss rss
|
||||||
|
// are handled. Revert to "simple" way.
|
||||||
|
CoroutineScope(Dispatchers.Main).launch {
|
||||||
|
try {
|
||||||
|
val (errorFetching, displaySelfossOnly) = repository.shouldBeSelfossInstance()
|
||||||
|
if (!errorFetching && !displaySelfossOnly) {
|
||||||
|
goToMain()
|
||||||
|
} else {
|
||||||
|
showProgress(false)
|
||||||
|
if (displaySelfossOnly) {
|
||||||
|
Toast.makeText(
|
||||||
|
applicationContext,
|
||||||
|
R.string.application_selfoss_only,
|
||||||
|
Toast.LENGTH_LONG
|
||||||
|
).show()
|
||||||
|
}
|
||||||
|
repository.logout()
|
||||||
|
}
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
repository.logout()
|
||||||
|
showProgress(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleActions()
|
handleActions()
|
||||||
@ -72,6 +91,7 @@ class LoginActivity :
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun handleActions() {
|
private fun handleActions() {
|
||||||
|
|
||||||
binding.passwordView.setOnEditorActionListener(
|
binding.passwordView.setOnEditorActionListener(
|
||||||
TextView.OnEditorActionListener { _, id, _ ->
|
TextView.OnEditorActionListener { _, id, _ ->
|
||||||
if (id == R.id.loginView || id == EditorInfo.IME_NULL) {
|
if (id == R.id.loginView || id == EditorInfo.IME_NULL) {
|
||||||
@ -79,7 +99,7 @@ class LoginActivity :
|
|||||||
return@OnEditorActionListener true
|
return@OnEditorActionListener true
|
||||||
}
|
}
|
||||||
false
|
false
|
||||||
},
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
binding.signInButton.setOnClickListener { attemptLogin() }
|
binding.signInButton.setOnClickListener { attemptLogin() }
|
||||||
@ -100,21 +120,16 @@ class LoginActivity :
|
|||||||
alertDialog.setMessage(getString(R.string.base_url_error))
|
alertDialog.setMessage(getString(R.string.base_url_error))
|
||||||
alertDialog.setButton(
|
alertDialog.setButton(
|
||||||
AlertDialog.BUTTON_NEUTRAL,
|
AlertDialog.BUTTON_NEUTRAL,
|
||||||
"OK",
|
"OK"
|
||||||
) { dialog, _ -> dialog.dismiss() }
|
) { dialog, _ -> dialog.dismiss() }
|
||||||
alertDialog.show()
|
alertDialog.show()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun goToMain() {
|
private fun goToMain() {
|
||||||
CountingIdlingResourceSingleton.increment()
|
|
||||||
CoroutineScope(Dispatchers.Main).launch {
|
CoroutineScope(Dispatchers.Main).launch {
|
||||||
repository.updateApiInformation()
|
repository.updateApiVersion()
|
||||||
ACRA.errorReporter.putCustomData(
|
ACRA.errorReporter.putCustomData("SELFOSS_API_VERSION", appSettingsService.getApiVersion().toString())
|
||||||
"SELFOSS_API_VERSION",
|
|
||||||
appSettingsService.getApiVersion().toString(),
|
|
||||||
)
|
|
||||||
CountingIdlingResourceSingleton.decrement()
|
|
||||||
}
|
}
|
||||||
val intent = Intent(this, HomeActivity::class.java)
|
val intent = Intent(this, HomeActivity::class.java)
|
||||||
startActivity(intent)
|
startActivity(intent)
|
||||||
@ -130,69 +145,50 @@ class LoginActivity :
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun attemptLogin() {
|
private fun attemptLogin() {
|
||||||
|
|
||||||
// Reset errors.
|
// Reset errors.
|
||||||
binding.urlView.error = null
|
binding.urlView.error = null
|
||||||
binding.loginView.error = null
|
binding.loginView.error = null
|
||||||
binding.passwordView.error = null
|
binding.passwordView.error = null
|
||||||
|
|
||||||
// Store values at the time of the login attempt.
|
// Store values at the time of the login attempt.
|
||||||
val url =
|
val url = binding.urlView.text.toString().trim()
|
||||||
binding.urlView.text
|
val login = binding.loginView.text.toString().trim()
|
||||||
.toString()
|
val password = binding.passwordView.text.toString().trim()
|
||||||
.trim()
|
|
||||||
val login =
|
|
||||||
binding.loginView.text
|
|
||||||
.toString()
|
|
||||||
.trim()
|
|
||||||
val password =
|
|
||||||
binding.passwordView.text
|
|
||||||
.toString()
|
|
||||||
.trim()
|
|
||||||
|
|
||||||
failInvalidUrl(url)
|
failInvalidUrl(url)
|
||||||
failLoginDetails(password, login)
|
failLoginDetails(password, login)
|
||||||
|
|
||||||
showProgress(true)
|
showProgress(true)
|
||||||
|
|
||||||
appSettingsService.updateSelfSigned(binding.selfSigned.isChecked)
|
|
||||||
|
|
||||||
repository.refreshLoginInformation(url, login, password)
|
repository.refreshLoginInformation(url, login, password)
|
||||||
|
|
||||||
CountingIdlingResourceSingleton.increment()
|
|
||||||
CoroutineScope(Dispatchers.Main).launch {
|
CoroutineScope(Dispatchers.Main).launch {
|
||||||
try {
|
|
||||||
repository.updateApiInformation()
|
|
||||||
} catch (e: Exception) {
|
|
||||||
if (e.message?.startsWith("No transformation found") == true) {
|
|
||||||
Toast
|
|
||||||
.makeText(
|
|
||||||
applicationContext,
|
|
||||||
R.string.application_selfoss_only,
|
|
||||||
Toast.LENGTH_LONG,
|
|
||||||
).show()
|
|
||||||
preferenceError()
|
|
||||||
showProgress(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val result = repository.login()
|
val result = repository.login()
|
||||||
if (result) {
|
if (result) {
|
||||||
val errorFetching = repository.checkIfFetchFails()
|
val (errorFetching, displaySelfossOnly) = repository.shouldBeSelfossInstance()
|
||||||
if (!errorFetching) {
|
if (!errorFetching && !displaySelfossOnly) {
|
||||||
goToMain()
|
goToMain()
|
||||||
} else {
|
} else {
|
||||||
|
if (displaySelfossOnly) {
|
||||||
|
Toast.makeText(
|
||||||
|
applicationContext,
|
||||||
|
R.string.application_selfoss_only,
|
||||||
|
Toast.LENGTH_LONG
|
||||||
|
).show()
|
||||||
|
}
|
||||||
preferenceError()
|
preferenceError()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
preferenceError()
|
preferenceError()
|
||||||
}
|
}
|
||||||
showProgress(false)
|
showProgress(false)
|
||||||
CountingIdlingResourceSingleton.decrement()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun failLoginDetails(
|
private fun failLoginDetails(
|
||||||
password: String,
|
password: String,
|
||||||
login: String,
|
login: String
|
||||||
) {
|
) {
|
||||||
var lastFocusedView: View? = null
|
var lastFocusedView: View? = null
|
||||||
var cancel = false
|
var cancel = false
|
||||||
@ -219,13 +215,13 @@ class LoginActivity :
|
|||||||
cancel = true
|
cancel = true
|
||||||
binding.urlView.error = getString(R.string.login_url_problem)
|
binding.urlView.error = getString(R.string.login_url_problem)
|
||||||
inValidCount++
|
inValidCount++
|
||||||
if (inValidCount == MAX_INVALID_LOGIN_BEFORE_ALERT_DISPLAYED) {
|
if (inValidCount == 3) {
|
||||||
val alertDialog = AlertDialog.Builder(this).create()
|
val alertDialog = AlertDialog.Builder(this).create()
|
||||||
alertDialog.setTitle(getString(R.string.warning_wrong_url))
|
alertDialog.setTitle(getString(R.string.warning_wrong_url))
|
||||||
alertDialog.setMessage(getString(R.string.text_wrong_url))
|
alertDialog.setMessage(getString(R.string.text_wrong_url))
|
||||||
alertDialog.setButton(
|
alertDialog.setButton(
|
||||||
AlertDialog.BUTTON_NEUTRAL,
|
AlertDialog.BUTTON_NEUTRAL,
|
||||||
"OK",
|
"OK"
|
||||||
) { dialog, _ -> dialog.dismiss() }
|
) { dialog, _ -> dialog.dismiss() }
|
||||||
alertDialog.show()
|
alertDialog.show()
|
||||||
inValidCount = 0
|
inValidCount = 0
|
||||||
@ -234,10 +230,7 @@ class LoginActivity :
|
|||||||
maybeCancelAndFocusView(cancel, focusView)
|
maybeCancelAndFocusView(cancel, focusView)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun maybeCancelAndFocusView(
|
private fun maybeCancelAndFocusView(cancel: Boolean, focusView: View?) {
|
||||||
cancel: Boolean,
|
|
||||||
focusView: View?,
|
|
||||||
) {
|
|
||||||
if (cancel) {
|
if (cancel) {
|
||||||
focusView?.requestFocus()
|
focusView?.requestFocus()
|
||||||
}
|
}
|
||||||
@ -251,13 +244,12 @@ class LoginActivity :
|
|||||||
.animate()
|
.animate()
|
||||||
.setDuration(shortAnimTime.toLong())
|
.setDuration(shortAnimTime.toLong())
|
||||||
.alpha(
|
.alpha(
|
||||||
if (show) 0F else 1F,
|
if (show) 0F else 1F
|
||||||
).setListener(
|
).setListener(object : AnimatorListenerAdapter() {
|
||||||
object : AnimatorListenerAdapter() {
|
override fun onAnimationEnd(animation: Animator) {
|
||||||
override fun onAnimationEnd(animation: Animator) {
|
binding.loginForm.visibility = if (show) View.GONE else View.VISIBLE
|
||||||
binding.loginForm.visibility = if (show) View.GONE else View.VISIBLE
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
)
|
)
|
||||||
|
|
||||||
binding.loginProgress.visibility = if (show) View.VISIBLE else View.GONE
|
binding.loginProgress.visibility = if (show) View.VISIBLE else View.GONE
|
||||||
@ -265,13 +257,12 @@ class LoginActivity :
|
|||||||
.animate()
|
.animate()
|
||||||
.setDuration(shortAnimTime.toLong())
|
.setDuration(shortAnimTime.toLong())
|
||||||
.alpha(
|
.alpha(
|
||||||
if (show) 1F else 0F,
|
if (show) 1F else 0F
|
||||||
).setListener(
|
).setListener(object : AnimatorListenerAdapter() {
|
||||||
object : AnimatorListenerAdapter() {
|
override fun onAnimationEnd(animation: Animator) {
|
||||||
override fun onAnimationEnd(animation: Animator) {
|
binding.loginProgress.visibility = if (show) View.VISIBLE else View.GONE
|
||||||
binding.loginProgress.visibility = if (show) View.VISIBLE else View.GONE
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -282,25 +273,13 @@ class LoginActivity :
|
|||||||
|
|
||||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||||
return when (item.itemId) {
|
return when (item.itemId) {
|
||||||
R.id.issue_tracker -> {
|
|
||||||
val browserIntent =
|
|
||||||
Intent(Intent.ACTION_VIEW, Uri.parse(AppSettingsService.BUG_URL))
|
|
||||||
startActivity(browserIntent)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
R.id.about -> {
|
R.id.about -> {
|
||||||
LibsBuilder()
|
LibsBuilder()
|
||||||
.withAboutIconShown(true)
|
.withAboutIconShown(true)
|
||||||
.withAboutVersionShown(true)
|
.withAboutVersionShown(true)
|
||||||
.withAboutSpecial2("Bug reports")
|
|
||||||
.withAboutSpecial2Description(AppSettingsService.BUG_URL)
|
|
||||||
.withAboutSpecial1("Project Page")
|
|
||||||
.withAboutSpecial1Description(AppSettingsService.SOURCE_URL)
|
|
||||||
.start(this)
|
.start(this)
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> super.onOptionsItemSelected(item)
|
else -> super.onOptionsItemSelected(item)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@ import bou.amine.apps.readerforselfossv2.android.databinding.ActivityMainBinding
|
|||||||
class MainActivity : AppCompatActivity() {
|
class MainActivity : AppCompatActivity() {
|
||||||
private lateinit var binding: ActivityMainBinding
|
private lateinit var binding: ActivityMainBinding
|
||||||
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
binding = ActivityMainBinding.inflate(layoutInflater)
|
binding = ActivityMainBinding.inflate(layoutInflater)
|
||||||
|
@ -9,11 +9,10 @@ import androidx.lifecycle.DefaultLifecycleObserver
|
|||||||
import androidx.lifecycle.LifecycleOwner
|
import androidx.lifecycle.LifecycleOwner
|
||||||
import androidx.lifecycle.ProcessLifecycleOwner
|
import androidx.lifecycle.ProcessLifecycleOwner
|
||||||
import androidx.multidex.MultiDexApplication
|
import androidx.multidex.MultiDexApplication
|
||||||
import bou.amine.apps.readerforselfossv2.android.testing.TestingHelper
|
import bou.amine.apps.readerforselfossv2.DI.networkModule
|
||||||
import bou.amine.apps.readerforselfossv2.android.viewmodel.AppViewModel
|
import bou.amine.apps.readerforselfossv2.android.viewmodel.AppViewModel
|
||||||
import bou.amine.apps.readerforselfossv2.dao.DriverFactory
|
import bou.amine.apps.readerforselfossv2.dao.DriverFactory
|
||||||
import bou.amine.apps.readerforselfossv2.dao.ReaderForSelfossDB
|
import bou.amine.apps.readerforselfossv2.dao.ReaderForSelfossDB
|
||||||
import bou.amine.apps.readerforselfossv2.di.networkModule
|
|
||||||
import bou.amine.apps.readerforselfossv2.repository.Repository
|
import bou.amine.apps.readerforselfossv2.repository.Repository
|
||||||
import bou.amine.apps.readerforselfossv2.service.AppSettingsService
|
import bou.amine.apps.readerforselfossv2.service.AppSettingsService
|
||||||
import com.github.ln_12.library.ConnectivityStatus
|
import com.github.ln_12.library.ConnectivityStatus
|
||||||
@ -30,29 +29,22 @@ import org.acra.config.toast
|
|||||||
import org.acra.data.StringFormat
|
import org.acra.data.StringFormat
|
||||||
import org.acra.ktx.initAcra
|
import org.acra.ktx.initAcra
|
||||||
import org.acra.sender.HttpSender
|
import org.acra.sender.HttpSender
|
||||||
import org.kodein.di.DI
|
import org.kodein.di.*
|
||||||
import org.kodein.di.DIAware
|
|
||||||
import org.kodein.di.bind
|
class MyApp : MultiDexApplication(), DIAware {
|
||||||
import org.kodein.di.instance
|
|
||||||
import org.kodein.di.singleton
|
|
||||||
|
|
||||||
class MyApp :
|
|
||||||
MultiDexApplication(),
|
|
||||||
DIAware {
|
|
||||||
override val di by DI.lazy {
|
override val di by DI.lazy {
|
||||||
bind<AppSettingsService>() with singleton { AppSettingsService(ACRA.isACRASenderServiceProcess() || TestingHelper().isUnitTest()) }
|
|
||||||
import(networkModule)
|
import(networkModule)
|
||||||
bind<DriverFactory>() with singleton { DriverFactory(applicationContext) }
|
bind<DriverFactory>() with singleton { DriverFactory(applicationContext) }
|
||||||
bind<ReaderForSelfossDB>() with singleton { ReaderForSelfossDB(driverFactory.createDriver()) }
|
bind<ReaderForSelfossDB>() with singleton { ReaderForSelfossDB(driverFactory.createDriver()) }
|
||||||
bind<Repository>() with
|
bind<Repository>() with singleton {
|
||||||
singleton {
|
Repository(
|
||||||
Repository(
|
instance(),
|
||||||
instance(),
|
instance(),
|
||||||
instance(),
|
isConnectionAvailable,
|
||||||
isConnectionAvailable,
|
instance()
|
||||||
instance(),
|
)
|
||||||
)
|
}
|
||||||
}
|
|
||||||
bind<ConnectivityStatus>() with singleton { ConnectivityStatus(applicationContext) }
|
bind<ConnectivityStatus>() with singleton { ConnectivityStatus(applicationContext) }
|
||||||
bind<AppViewModel>() with singleton { AppViewModel(repository = instance()) }
|
bind<AppViewModel>() with singleton { AppViewModel(repository = instance()) }
|
||||||
}
|
}
|
||||||
@ -62,7 +54,6 @@ class MyApp :
|
|||||||
private val connectivityStatus: ConnectivityStatus by instance()
|
private val connectivityStatus: ConnectivityStatus by instance()
|
||||||
private val driverFactory: DriverFactory by instance()
|
private val driverFactory: DriverFactory by instance()
|
||||||
|
|
||||||
@Suppress("detekt:ForbiddenComment")
|
|
||||||
// TODO: handle with the "previous" way
|
// TODO: handle with the "previous" way
|
||||||
private val isConnectionAvailable: MutableStateFlow<Boolean> = MutableStateFlow(true)
|
private val isConnectionAvailable: MutableStateFlow<Boolean> = MutableStateFlow(true)
|
||||||
|
|
||||||
@ -78,26 +69,24 @@ class MyApp :
|
|||||||
ProcessLifecycleOwner.get().lifecycle.addObserver(
|
ProcessLifecycleOwner.get().lifecycle.addObserver(
|
||||||
AppLifeCycleObserver(
|
AppLifeCycleObserver(
|
||||||
connectivityStatus,
|
connectivityStatus,
|
||||||
repository,
|
repository
|
||||||
),
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
CoroutineScope(Dispatchers.Main).launch {
|
CoroutineScope(Dispatchers.Main).launch {
|
||||||
viewModel.networkAvailableProvider.collect { networkAvailable ->
|
viewModel.networkAvailableProvider.collect { networkAvailable ->
|
||||||
val toastMessage =
|
val toastMessage = if (networkAvailable) {
|
||||||
if (networkAvailable) {
|
repository.handleDBActions()
|
||||||
repository.handleDBActions()
|
R.string.network_connectivity_retrieved
|
||||||
R.string.network_connectivity_retrieved
|
} else {
|
||||||
} else {
|
R.string.network_connectivity_lost
|
||||||
R.string.network_connectivity_lost
|
}
|
||||||
}
|
|
||||||
|
|
||||||
Toast
|
Toast.makeText(
|
||||||
.makeText(
|
applicationContext,
|
||||||
applicationContext,
|
toastMessage,
|
||||||
toastMessage,
|
Toast.LENGTH_SHORT
|
||||||
Toast.LENGTH_SHORT,
|
).show()
|
||||||
).show()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -110,40 +99,39 @@ class MyApp :
|
|||||||
|
|
||||||
initAcra {
|
initAcra {
|
||||||
reportFormat = StringFormat.JSON
|
reportFormat = StringFormat.JSON
|
||||||
reportContent =
|
reportContent = listOf(
|
||||||
listOf(
|
ReportField.REPORT_ID,
|
||||||
ReportField.REPORT_ID,
|
ReportField.INSTALLATION_ID,
|
||||||
ReportField.INSTALLATION_ID,
|
ReportField.APP_VERSION_CODE,
|
||||||
ReportField.APP_VERSION_CODE,
|
ReportField.APP_VERSION_NAME,
|
||||||
ReportField.APP_VERSION_NAME,
|
ReportField.BUILD,
|
||||||
ReportField.BUILD,
|
ReportField.ANDROID_VERSION,
|
||||||
ReportField.ANDROID_VERSION,
|
ReportField.BRAND,
|
||||||
ReportField.BRAND,
|
ReportField.PHONE_MODEL,
|
||||||
ReportField.PHONE_MODEL,
|
ReportField.AVAILABLE_MEM_SIZE,
|
||||||
ReportField.AVAILABLE_MEM_SIZE,
|
ReportField.TOTAL_MEM_SIZE,
|
||||||
ReportField.TOTAL_MEM_SIZE,
|
ReportField.STACK_TRACE,
|
||||||
ReportField.STACK_TRACE,
|
ReportField.APPLICATION_LOG,
|
||||||
ReportField.APPLICATION_LOG,
|
ReportField.LOGCAT,
|
||||||
ReportField.LOGCAT,
|
ReportField.INITIAL_CONFIGURATION,
|
||||||
ReportField.INITIAL_CONFIGURATION,
|
ReportField.CRASH_CONFIGURATION,
|
||||||
ReportField.CRASH_CONFIGURATION,
|
ReportField.IS_SILENT,
|
||||||
ReportField.IS_SILENT,
|
ReportField.USER_APP_START_DATE,
|
||||||
ReportField.USER_APP_START_DATE,
|
ReportField.USER_COMMENT,
|
||||||
ReportField.USER_COMMENT,
|
ReportField.USER_CRASH_DATE,
|
||||||
ReportField.USER_CRASH_DATE,
|
ReportField.USER_EMAIL,
|
||||||
ReportField.USER_EMAIL,
|
ReportField.CUSTOM_DATA
|
||||||
ReportField.CUSTOM_DATA,
|
)
|
||||||
)
|
|
||||||
toast {
|
toast {
|
||||||
// required
|
//required
|
||||||
text = getString(R.string.crash_toast_text)
|
text = getString(R.string.crash_toast_text)
|
||||||
length = Toast.LENGTH_SHORT
|
length = Toast.LENGTH_SHORT
|
||||||
}
|
}
|
||||||
httpSender {
|
httpSender {
|
||||||
uri =
|
uri =
|
||||||
"https://bugs.amine-bouabdallaoui.fr/report" // best guess, you may need to adjust this
|
"https://bugs.amine-louveau.fr/report" /*best guess, you may need to adjust this*/
|
||||||
basicAuthLogin = "qMEscjj89Gwt6cPR"
|
basicAuthLogin = "LMTlLZuazADohTCm"
|
||||||
basicAuthPassword = "Yo58QFlGzFaWlBzP"
|
basicAuthPassword = "he6ghHp83F0PYPfh"
|
||||||
httpMethod = HttpSender.Method.POST
|
httpMethod = HttpSender.Method.POST
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -155,16 +143,15 @@ class MyApp :
|
|||||||
|
|
||||||
val name = getString(R.string.notification_channel_sync)
|
val name = getString(R.string.notification_channel_sync)
|
||||||
val importance = NotificationManager.IMPORTANCE_LOW
|
val importance = NotificationManager.IMPORTANCE_LOW
|
||||||
val mChannel = NotificationChannel(AppSettingsService.SYNC_CHANNEL_ID, name, importance)
|
val mChannel = NotificationChannel(AppSettingsService.syncChannelId, name, importance)
|
||||||
|
|
||||||
val newItemsChannelname = getString(R.string.new_items_channel_sync)
|
val newItemsChannelname = getString(R.string.new_items_channel_sync)
|
||||||
val newItemsChannelimportance = NotificationManager.IMPORTANCE_DEFAULT
|
val newItemsChannelimportance = NotificationManager.IMPORTANCE_DEFAULT
|
||||||
val newItemsChannelmChannel =
|
val newItemsChannelmChannel = NotificationChannel(
|
||||||
NotificationChannel(
|
AppSettingsService.newItemsChannelId,
|
||||||
AppSettingsService.NEW_ITEMS_CHANNEL,
|
newItemsChannelname,
|
||||||
newItemsChannelname,
|
newItemsChannelimportance
|
||||||
newItemsChannelimportance,
|
)
|
||||||
)
|
|
||||||
|
|
||||||
notificationManager.createNotificationChannel(mChannel)
|
notificationManager.createNotificationChannel(mChannel)
|
||||||
notificationManager.createNotificationChannel(newItemsChannelmChannel)
|
notificationManager.createNotificationChannel(newItemsChannelmChannel)
|
||||||
@ -175,11 +162,9 @@ class MyApp :
|
|||||||
val oldHandler = Thread.getDefaultUncaughtExceptionHandler()
|
val oldHandler = Thread.getDefaultUncaughtExceptionHandler()
|
||||||
|
|
||||||
Thread.setDefaultUncaughtExceptionHandler { thread, e ->
|
Thread.setDefaultUncaughtExceptionHandler { thread, e ->
|
||||||
if (e is NoClassDefFoundError &&
|
if (e is NoClassDefFoundError && e.stackTrace.asList().any {
|
||||||
e.stackTrace.asList().any {
|
|
||||||
it.toString().contains("android.view.ViewDebug")
|
it.toString().contains("android.view.ViewDebug")
|
||||||
}
|
}) {
|
||||||
) {
|
|
||||||
// Nothing
|
// Nothing
|
||||||
} else {
|
} else {
|
||||||
oldHandler.uncaughtException(thread, e)
|
oldHandler.uncaughtException(thread, e)
|
||||||
@ -189,8 +174,9 @@ class MyApp :
|
|||||||
|
|
||||||
class AppLifeCycleObserver(
|
class AppLifeCycleObserver(
|
||||||
val connectivityStatus: ConnectivityStatus,
|
val connectivityStatus: ConnectivityStatus,
|
||||||
val repository: Repository,
|
val repository: Repository
|
||||||
) : DefaultLifecycleObserver {
|
) : DefaultLifecycleObserver {
|
||||||
|
|
||||||
override fun onResume(owner: LifecycleOwner) {
|
override fun onResume(owner: LifecycleOwner) {
|
||||||
super.onResume(owner)
|
super.onResume(owner)
|
||||||
repository.connectionMonitored = true
|
repository.connectionMonitored = true
|
||||||
|
@ -22,9 +22,8 @@ import org.kodein.di.DIAware
|
|||||||
import org.kodein.di.android.closestDI
|
import org.kodein.di.android.closestDI
|
||||||
import org.kodein.di.instance
|
import org.kodein.di.instance
|
||||||
|
|
||||||
class ReaderActivity :
|
class ReaderActivity : AppCompatActivity(), DIAware {
|
||||||
AppCompatActivity(),
|
|
||||||
DIAware {
|
|
||||||
private var currentItem: Int = 0
|
private var currentItem: Int = 0
|
||||||
|
|
||||||
private lateinit var toolbarMenu: Menu
|
private lateinit var toolbarMenu: Menu
|
||||||
@ -53,7 +52,6 @@ class ReaderActivity :
|
|||||||
showMenuItem(false)
|
showMenuItem(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("detekt:SwallowedException")
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
binding = ActivityReaderBinding.inflate(layoutInflater)
|
binding = ActivityReaderBinding.inflate(layoutInflater)
|
||||||
@ -73,11 +71,7 @@ class ReaderActivity :
|
|||||||
finish()
|
finish()
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
readItem(allItems[currentItem])
|
||||||
readItem(allItems[currentItem])
|
|
||||||
} catch (e: IndexOutOfBoundsException) {
|
|
||||||
finish()
|
|
||||||
}
|
|
||||||
|
|
||||||
binding.pager.adapter = ScreenSlidePagerAdapter(this)
|
binding.pager.adapter = ScreenSlidePagerAdapter(this)
|
||||||
binding.pager.setCurrentItem(currentItem, false)
|
binding.pager.setCurrentItem(currentItem, false)
|
||||||
@ -90,7 +84,7 @@ class ReaderActivity :
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun readItem(item: SelfossModel.Item) {
|
private fun readItem(item: SelfossModel.Item) {
|
||||||
if (appSettingsService.isMarkOnScrollEnabled() && !appSettingsService.getPublicAccess()) {
|
if (appSettingsService.isMarkOnScrollEnabled()) {
|
||||||
CoroutineScope(Dispatchers.IO).launch {
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
repository.markAsRead(item)
|
repository.markAsRead(item)
|
||||||
}
|
}
|
||||||
@ -102,37 +96,35 @@ class ReaderActivity :
|
|||||||
oldInstanceState.clear()
|
oldInstanceState.clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
private inner class ScreenSlidePagerAdapter(
|
private inner class ScreenSlidePagerAdapter(fa: FragmentActivity) :
|
||||||
fa: FragmentActivity,
|
FragmentStateAdapter(fa) {
|
||||||
) : FragmentStateAdapter(fa) {
|
|
||||||
override fun getItemCount(): Int = allItems.size
|
override fun getItemCount(): Int = allItems.size
|
||||||
|
|
||||||
override fun createFragment(position: Int): Fragment = ArticleFragment.newInstance(allItems[position])
|
override fun createFragment(position: Int): Fragment =
|
||||||
|
ArticleFragment.newInstance(allItems[position])
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onKeyDown(
|
override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
|
||||||
keyCode: Int,
|
return when (keyCode) {
|
||||||
event: KeyEvent?,
|
|
||||||
): Boolean =
|
|
||||||
when (keyCode) {
|
|
||||||
KeyEvent.KEYCODE_VOLUME_DOWN -> {
|
KeyEvent.KEYCODE_VOLUME_DOWN -> {
|
||||||
val currentFragment =
|
val currentFragment =
|
||||||
supportFragmentManager.findFragmentByTag("f" + binding.pager.currentItem) as ArticleFragment
|
supportFragmentManager.findFragmentByTag("f" + binding.pager.currentItem) as ArticleFragment
|
||||||
currentFragment.volumeButtonScrollDown()
|
currentFragment.scrollDown()
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
KeyEvent.KEYCODE_VOLUME_UP -> {
|
KeyEvent.KEYCODE_VOLUME_UP -> {
|
||||||
val currentFragment =
|
val currentFragment =
|
||||||
supportFragmentManager.findFragmentByTag("f" + binding.pager.currentItem) as ArticleFragment
|
supportFragmentManager.findFragmentByTag("f" + binding.pager.currentItem) as ArticleFragment
|
||||||
currentFragment.volumeButtonScrollUp()
|
currentFragment.scrollUp()
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
super.onKeyDown(keyCode, event)
|
super.onKeyDown(keyCode, event)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun alignmentMenu() {
|
private fun alignmentMenu() {
|
||||||
val showJustify = appSettingsService.getActiveAllignment() == AppSettingsService.ALIGN_LEFT
|
val showJustify = appSettingsService.getActiveAllignment() == AppSettingsService.ALIGN_LEFT
|
||||||
@ -145,34 +137,28 @@ class ReaderActivity :
|
|||||||
inflater.inflate(R.menu.reader_menu, menu)
|
inflater.inflate(R.menu.reader_menu, menu)
|
||||||
toolbarMenu = menu
|
toolbarMenu = menu
|
||||||
|
|
||||||
|
if (allItems.isNotEmpty() && allItems[currentItem].starred) {
|
||||||
|
canRemoveFromFavorite()
|
||||||
|
} else {
|
||||||
|
canFavorite()
|
||||||
|
}
|
||||||
alignmentMenu()
|
alignmentMenu()
|
||||||
|
|
||||||
if (appSettingsService.getPublicAccess()) {
|
binding.pager.registerOnPageChangeCallback(
|
||||||
menu.removeItem(R.id.star)
|
object : ViewPager2.OnPageChangeCallback() {
|
||||||
} else {
|
|
||||||
if (allItems.isNotEmpty() && allItems[currentItem].starred) {
|
|
||||||
canRemoveFromFavorite()
|
|
||||||
} else {
|
|
||||||
canFavorite()
|
|
||||||
}
|
|
||||||
|
|
||||||
binding.pager.registerOnPageChangeCallback(
|
override fun onPageSelected(position: Int) {
|
||||||
object : ViewPager2.OnPageChangeCallback() {
|
super.onPageSelected(position)
|
||||||
override fun onPageSelected(position: Int) {
|
|
||||||
super.onPageSelected(position)
|
|
||||||
|
|
||||||
if (!allItems.isNullOrEmpty() && allItems.size >= position) {
|
if (allItems[position].starred) {
|
||||||
if (allItems[position].starred) {
|
canRemoveFromFavorite()
|
||||||
canRemoveFromFavorite()
|
} else {
|
||||||
} else {
|
canFavorite()
|
||||||
canFavorite()
|
|
||||||
}
|
|
||||||
readItem(allItems[position])
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
readItem(allItems[position])
|
||||||
)
|
}
|
||||||
}
|
}
|
||||||
|
)
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@ -191,10 +177,9 @@ class ReaderActivity :
|
|||||||
|
|
||||||
when (item.itemId) {
|
when (item.itemId) {
|
||||||
android.R.id.home -> {
|
android.R.id.home -> {
|
||||||
onBackPressedDispatcher.onBackPressed()
|
onBackPressed()
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
R.id.star -> {
|
R.id.star -> {
|
||||||
if (allItems[binding.pager.currentItem].starred) {
|
if (allItems[binding.pager.currentItem].starred) {
|
||||||
CoroutineScope(Dispatchers.IO).launch {
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
@ -208,12 +193,10 @@ class ReaderActivity :
|
|||||||
afterSave()
|
afterSave()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
R.id.align_left -> {
|
R.id.align_left -> {
|
||||||
switchAlignmentSetting(AppSettingsService.ALIGN_LEFT)
|
switchAlignmentSetting(AppSettingsService.ALIGN_LEFT)
|
||||||
refreshFragment()
|
refreshFragment()
|
||||||
}
|
}
|
||||||
|
|
||||||
R.id.align_justify -> {
|
R.id.align_justify -> {
|
||||||
switchAlignmentSetting(AppSettingsService.JUSTIFY)
|
switchAlignmentSetting(AppSettingsService.JUSTIFY)
|
||||||
refreshFragment()
|
refreshFragment()
|
||||||
|
@ -8,7 +8,6 @@ import androidx.appcompat.app.AppCompatActivity
|
|||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import bou.amine.apps.readerforselfossv2.android.adapters.SourcesListAdapter
|
import bou.amine.apps.readerforselfossv2.android.adapters.SourcesListAdapter
|
||||||
import bou.amine.apps.readerforselfossv2.android.databinding.ActivitySourcesBinding
|
import bou.amine.apps.readerforselfossv2.android.databinding.ActivitySourcesBinding
|
||||||
import bou.amine.apps.readerforselfossv2.android.testing.CountingIdlingResourceSingleton
|
|
||||||
import bou.amine.apps.readerforselfossv2.model.SelfossModel
|
import bou.amine.apps.readerforselfossv2.model.SelfossModel
|
||||||
import bou.amine.apps.readerforselfossv2.repository.Repository
|
import bou.amine.apps.readerforselfossv2.repository.Repository
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
@ -18,13 +17,12 @@ import org.kodein.di.DIAware
|
|||||||
import org.kodein.di.android.closestDI
|
import org.kodein.di.android.closestDI
|
||||||
import org.kodein.di.instance
|
import org.kodein.di.instance
|
||||||
|
|
||||||
class SourcesActivity :
|
class SourcesActivity : AppCompatActivity(), DIAware {
|
||||||
AppCompatActivity(),
|
|
||||||
DIAware {
|
|
||||||
private lateinit var binding: ActivitySourcesBinding
|
private lateinit var binding: ActivitySourcesBinding
|
||||||
|
|
||||||
override val di by closestDI()
|
override val di by closestDI()
|
||||||
private val repository: Repository by instance()
|
private val repository : Repository by instance()
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
binding = ActivitySourcesBinding.inflate(layoutInflater)
|
binding = ActivitySourcesBinding.inflate(layoutInflater)
|
||||||
@ -39,8 +37,7 @@ class SourcesActivity :
|
|||||||
supportActionBar?.setDisplayShowHomeEnabled(true)
|
supportActionBar?.setDisplayShowHomeEnabled(true)
|
||||||
|
|
||||||
binding.fab.rippleColor = resources.getColor(R.color.colorAccentDark)
|
binding.fab.rippleColor = resources.getColor(R.color.colorAccentDark)
|
||||||
binding.fab.backgroundTintList =
|
binding.fab.backgroundTintList = ColorStateList.valueOf(resources.getColor(R.color.colorAccent))
|
||||||
ColorStateList.valueOf(resources.getColor(R.color.colorAccent))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onStop() {
|
override fun onStop() {
|
||||||
@ -52,32 +49,27 @@ class SourcesActivity :
|
|||||||
super.onResume()
|
super.onResume()
|
||||||
val mLayoutManager = LinearLayoutManager(this)
|
val mLayoutManager = LinearLayoutManager(this)
|
||||||
|
|
||||||
var items: ArrayList<SelfossModel.SourceDetail>
|
var items: ArrayList<SelfossModel.Source>
|
||||||
|
|
||||||
binding.recyclerView.setHasFixedSize(true)
|
binding.recyclerView.setHasFixedSize(true)
|
||||||
binding.recyclerView.layoutManager = mLayoutManager
|
binding.recyclerView.layoutManager = mLayoutManager
|
||||||
|
|
||||||
CountingIdlingResourceSingleton.increment()
|
|
||||||
CoroutineScope(Dispatchers.Main).launch {
|
CoroutineScope(Dispatchers.Main).launch {
|
||||||
val response = repository.getSourcesDetails()
|
val response = repository.getSources()
|
||||||
if (response.isNotEmpty()) {
|
if (response.isNotEmpty()) {
|
||||||
items = response
|
items = response
|
||||||
val mAdapter =
|
val mAdapter = SourcesListAdapter(
|
||||||
SourcesListAdapter(
|
this@SourcesActivity, items
|
||||||
this@SourcesActivity,
|
)
|
||||||
items,
|
|
||||||
)
|
|
||||||
binding.recyclerView.adapter = mAdapter
|
binding.recyclerView.adapter = mAdapter
|
||||||
mAdapter.notifyDataSetChanged()
|
mAdapter.notifyDataSetChanged()
|
||||||
} else {
|
} else {
|
||||||
Toast
|
Toast.makeText(
|
||||||
.makeText(
|
this@SourcesActivity,
|
||||||
this@SourcesActivity,
|
R.string.cant_get_sources,
|
||||||
R.string.cant_get_sources,
|
Toast.LENGTH_SHORT
|
||||||
Toast.LENGTH_SHORT,
|
).show()
|
||||||
).show()
|
|
||||||
}
|
}
|
||||||
CountingIdlingResourceSingleton.decrement()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.fab.setOnClickListener {
|
binding.fab.setOnClickListener {
|
||||||
|
@ -21,10 +21,10 @@ import org.kodein.di.DIAware
|
|||||||
import org.kodein.di.android.closestDI
|
import org.kodein.di.android.closestDI
|
||||||
import org.kodein.di.instance
|
import org.kodein.di.instance
|
||||||
|
|
||||||
class UpsertSourceActivity :
|
|
||||||
AppCompatActivity(),
|
class UpsertSourceActivity : AppCompatActivity(), DIAware {
|
||||||
DIAware {
|
|
||||||
private var existingSource: SelfossModel.SourceDetail? = null
|
private var existingSource: SelfossModel.Source? = null
|
||||||
private var mSpoutsValue: String? = null
|
private var mSpoutsValue: String? = null
|
||||||
|
|
||||||
private lateinit var binding: ActivityUpsertSourceBinding
|
private lateinit var binding: ActivityUpsertSourceBinding
|
||||||
@ -58,6 +58,7 @@ class UpsertSourceActivity :
|
|||||||
supportActionBar?.setDisplayShowHomeEnabled(true)
|
supportActionBar?.setDisplayShowHomeEnabled(true)
|
||||||
supportActionBar?.title = resources.getString(title)
|
supportActionBar?.title = resources.getString(title)
|
||||||
|
|
||||||
|
|
||||||
maybeGetDetailsFromIntentSharing(intent)
|
maybeGetDetailsFromIntentSharing(intent)
|
||||||
|
|
||||||
binding.saveBtn.setOnClickListener {
|
binding.saveBtn.setOnClickListener {
|
||||||
@ -67,7 +68,7 @@ class UpsertSourceActivity :
|
|||||||
|
|
||||||
private fun initFields(items: Map<String, SelfossModel.Spout>) {
|
private fun initFields(items: Map<String, SelfossModel.Spout>) {
|
||||||
binding.nameInput.setText(existingSource!!.title)
|
binding.nameInput.setText(existingSource!!.title)
|
||||||
binding.tags.setText(existingSource!!.tags?.joinToString(", "))
|
binding.tags.setText(existingSource!!.tags.joinToString(", "))
|
||||||
binding.sourceUri.setText(existingSource!!.params?.url)
|
binding.sourceUri.setText(existingSource!!.params?.url)
|
||||||
binding.spoutsSpinner.setSelection(items.keys.indexOf(existingSource!!.spout))
|
binding.spoutsSpinner.setSelection(items.keys.indexOf(existingSource!!.spout))
|
||||||
binding.progress.visibility = View.GONE
|
binding.progress.visibility = View.GONE
|
||||||
@ -85,35 +86,28 @@ class UpsertSourceActivity :
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("detekt:SwallowedException")
|
|
||||||
private fun handleSpoutsSpinner() {
|
private fun handleSpoutsSpinner() {
|
||||||
val spoutsKV = HashMap<String, String>()
|
val spoutsKV = HashMap<String, String>()
|
||||||
binding.spoutsSpinner.onItemSelectedListener =
|
binding.spoutsSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
|
||||||
object : AdapterView.OnItemSelectedListener {
|
override fun onItemSelected(adapterView: AdapterView<*>, view: View?, i: Int, l: Long) {
|
||||||
override fun onItemSelected(
|
if (view != null) {
|
||||||
adapterView: AdapterView<*>,
|
val spoutName = (view as TextView).text.toString()
|
||||||
view: View?,
|
mSpoutsValue = spoutsKV[spoutName]
|
||||||
i: Int,
|
|
||||||
l: Long,
|
|
||||||
) {
|
|
||||||
if (view != null) {
|
|
||||||
val spoutName = (view as TextView).text.toString()
|
|
||||||
mSpoutsValue = spoutsKV[spoutName]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onNothingSelected(adapterView: AdapterView<*>) {
|
|
||||||
mSpoutsValue = null
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onNothingSelected(adapterView: AdapterView<*>) {
|
||||||
|
mSpoutsValue = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
fun handleSpoutFailure(networkIssue: Boolean = false) {
|
fun handleSpoutFailure(networkIssue: Boolean = false) {
|
||||||
Toast
|
Toast.makeText(
|
||||||
.makeText(
|
this@UpsertSourceActivity,
|
||||||
this@UpsertSourceActivity,
|
if (networkIssue) R.string.cant_get_spouts_no_network else R.string.cant_get_spouts,
|
||||||
if (networkIssue) R.string.cant_get_spouts_no_network else R.string.cant_get_spouts,
|
Toast.LENGTH_SHORT
|
||||||
Toast.LENGTH_SHORT,
|
).show()
|
||||||
).show()
|
|
||||||
binding.progress.visibility = View.GONE
|
binding.progress.visibility = View.GONE
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -133,7 +127,7 @@ class UpsertSourceActivity :
|
|||||||
ArrayAdapter(
|
ArrayAdapter(
|
||||||
this@UpsertSourceActivity,
|
this@UpsertSourceActivity,
|
||||||
android.R.layout.simple_spinner_item,
|
android.R.layout.simple_spinner_item,
|
||||||
itemsStrings,
|
itemsStrings
|
||||||
)
|
)
|
||||||
spinnerArrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
|
spinnerArrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
|
||||||
binding.spoutsSpinner.adapter = spinnerArrayAdapter
|
binding.spoutsSpinner.adapter = spinnerArrayAdapter
|
||||||
@ -150,7 +144,9 @@ class UpsertSourceActivity :
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun maybeGetDetailsFromIntentSharing(intent: Intent) {
|
private fun maybeGetDetailsFromIntentSharing(
|
||||||
|
intent: Intent
|
||||||
|
) {
|
||||||
if (Intent.ACTION_SEND == intent.action && "text/plain" == intent.type) {
|
if (Intent.ACTION_SEND == intent.action && "text/plain" == intent.type) {
|
||||||
binding.sourceUri.setText(intent.getStringExtra(Intent.EXTRA_TEXT))
|
binding.sourceUri.setText(intent.getStringExtra(Intent.EXTRA_TEXT))
|
||||||
binding.nameInput.setText(intent.getStringExtra(Intent.EXTRA_TITLE))
|
binding.nameInput.setText(intent.getStringExtra(Intent.EXTRA_TITLE))
|
||||||
@ -174,35 +170,32 @@ class UpsertSourceActivity :
|
|||||||
sourceDetailsUnavailable -> {
|
sourceDetailsUnavailable -> {
|
||||||
Toast.makeText(this, R.string.form_not_complete, Toast.LENGTH_SHORT).show()
|
Toast.makeText(this, R.string.form_not_complete, Toast.LENGTH_SHORT).show()
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
CoroutineScope(Dispatchers.Main).launch {
|
CoroutineScope(Dispatchers.Main).launch {
|
||||||
val successfullyAddedSource =
|
val successfullyAddedSource = if (existingSource != null) {
|
||||||
if (existingSource != null) {
|
repository.updateSource(
|
||||||
repository.updateSource(
|
existingSource!!.id,
|
||||||
existingSource!!.id,
|
binding.nameInput.text.toString(),
|
||||||
binding.nameInput.text.toString(),
|
url,
|
||||||
url,
|
mSpoutsValue!!,
|
||||||
mSpoutsValue!!,
|
binding.tags.text.toString()
|
||||||
binding.tags.text.toString(),
|
)
|
||||||
)
|
} else {
|
||||||
} else {
|
repository.createSource(
|
||||||
repository.createSource(
|
binding.nameInput.text.toString(),
|
||||||
binding.nameInput.text.toString(),
|
url,
|
||||||
url,
|
mSpoutsValue!!,
|
||||||
mSpoutsValue!!,
|
binding.tags.text.toString(),
|
||||||
binding.tags.text.toString(),
|
)
|
||||||
)
|
}
|
||||||
}
|
|
||||||
if (successfullyAddedSource) {
|
if (successfullyAddedSource) {
|
||||||
finish()
|
finish()
|
||||||
} else {
|
} else {
|
||||||
Toast
|
Toast.makeText(
|
||||||
.makeText(
|
this@UpsertSourceActivity,
|
||||||
this@UpsertSourceActivity,
|
R.string.cant_create_source,
|
||||||
R.string.cant_create_source,
|
Toast.LENGTH_SHORT
|
||||||
Toast.LENGTH_SHORT,
|
).show()
|
||||||
).show()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package bou.amine.apps.readerforselfossv2.android.adapters
|
package bou.amine.apps.readerforselfossv2.android.adapters
|
||||||
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
|
import android.content.Context
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
@ -8,11 +9,12 @@ import android.widget.ImageView.ScaleType
|
|||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import bou.amine.apps.readerforselfossv2.android.R
|
import bou.amine.apps.readerforselfossv2.android.R
|
||||||
import bou.amine.apps.readerforselfossv2.android.databinding.CardItemBinding
|
import bou.amine.apps.readerforselfossv2.android.databinding.CardItemBinding
|
||||||
|
import bou.amine.apps.readerforselfossv2.android.model.toTextDrawableString
|
||||||
import bou.amine.apps.readerforselfossv2.android.utils.LinkOnTouchListener
|
import bou.amine.apps.readerforselfossv2.android.utils.LinkOnTouchListener
|
||||||
import bou.amine.apps.readerforselfossv2.android.utils.acra.sendSilentlyWithAcraWithName
|
|
||||||
import bou.amine.apps.readerforselfossv2.android.utils.glide.bitmapCenterCrop
|
import bou.amine.apps.readerforselfossv2.android.utils.glide.bitmapCenterCrop
|
||||||
import bou.amine.apps.readerforselfossv2.android.utils.glide.circularDrawable
|
import bou.amine.apps.readerforselfossv2.android.utils.glide.circularBitmapDrawable
|
||||||
import bou.amine.apps.readerforselfossv2.android.utils.openItemUrlInBrowserAsNewTask
|
import bou.amine.apps.readerforselfossv2.android.utils.openInBrowserAsNewTask
|
||||||
|
import bou.amine.apps.readerforselfossv2.android.utils.openItemUrl
|
||||||
import bou.amine.apps.readerforselfossv2.android.utils.shareLink
|
import bou.amine.apps.readerforselfossv2.android.utils.shareLink
|
||||||
import bou.amine.apps.readerforselfossv2.model.SelfossModel
|
import bou.amine.apps.readerforselfossv2.model.SelfossModel
|
||||||
import bou.amine.apps.readerforselfossv2.repository.Repository
|
import bou.amine.apps.readerforselfossv2.repository.Repository
|
||||||
@ -20,6 +22,8 @@ import bou.amine.apps.readerforselfossv2.service.AppSettingsService
|
|||||||
import bou.amine.apps.readerforselfossv2.utils.getHtmlDecoded
|
import bou.amine.apps.readerforselfossv2.utils.getHtmlDecoded
|
||||||
import bou.amine.apps.readerforselfossv2.utils.getIcon
|
import bou.amine.apps.readerforselfossv2.utils.getIcon
|
||||||
import bou.amine.apps.readerforselfossv2.utils.getThumbnail
|
import bou.amine.apps.readerforselfossv2.utils.getThumbnail
|
||||||
|
import com.amulyakhare.textdrawable.TextDrawable
|
||||||
|
import com.amulyakhare.textdrawable.util.ColorGenerator
|
||||||
import com.bumptech.glide.Glide
|
import com.bumptech.glide.Glide
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
@ -30,10 +34,11 @@ import org.kodein.di.instance
|
|||||||
|
|
||||||
class ItemCardAdapter(
|
class ItemCardAdapter(
|
||||||
override val app: Activity,
|
override val app: Activity,
|
||||||
override val items: ArrayList<SelfossModel.Item>,
|
override var items: ArrayList<SelfossModel.Item>,
|
||||||
override val updateHomeItems: (ArrayList<SelfossModel.Item>) -> Unit,
|
override val updateItems: (ArrayList<SelfossModel.Item>) -> Unit
|
||||||
) : ItemsAdapter<ItemCardAdapter.ViewHolder>() {
|
) : ItemsAdapter<ItemCardAdapter.ViewHolder>() {
|
||||||
override lateinit var binding: CardItemBinding
|
private val c: Context = app.baseContext
|
||||||
|
private val generator: ColorGenerator = ColorGenerator.MATERIAL
|
||||||
private val imageMaxHeight: Int =
|
private val imageMaxHeight: Int =
|
||||||
c.resources.getDimension(R.dimen.card_image_max_height).toInt()
|
c.resources.getDimension(R.dimen.card_image_max_height).toInt()
|
||||||
|
|
||||||
@ -41,71 +46,23 @@ class ItemCardAdapter(
|
|||||||
override val repository: Repository by instance()
|
override val repository: Repository by instance()
|
||||||
override val appSettingsService: AppSettingsService by instance()
|
override val appSettingsService: AppSettingsService by instance()
|
||||||
|
|
||||||
override fun onCreateViewHolder(
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||||
parent: ViewGroup,
|
val binding = CardItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
|
||||||
viewType: Int,
|
|
||||||
): ViewHolder {
|
|
||||||
binding = CardItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
|
|
||||||
return ViewHolder(binding)
|
return ViewHolder(binding)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleClickListeners(
|
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||||
holderBinding: CardItemBinding,
|
|
||||||
position: Int,
|
|
||||||
) {
|
|
||||||
holderBinding.favButton.setOnClickListener {
|
|
||||||
val item = items[position]
|
|
||||||
if (item.starred) {
|
|
||||||
CoroutineScope(Dispatchers.IO).launch {
|
|
||||||
repository.unstarr(item)
|
|
||||||
}
|
|
||||||
binding.favButton.isSelected = false
|
|
||||||
} else {
|
|
||||||
CoroutineScope(Dispatchers.IO).launch {
|
|
||||||
repository.starr(item)
|
|
||||||
}
|
|
||||||
binding.favButton.isSelected = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
binding.shareBtn.setOnClickListener {
|
|
||||||
val item = items[position]
|
|
||||||
c.shareLink(item.getLinkDecoded(), item.title.getHtmlDecoded())
|
|
||||||
}
|
|
||||||
|
|
||||||
binding.browserBtn.setOnClickListener {
|
|
||||||
c.openItemUrlInBrowserAsNewTask(items[position])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onBindViewHolder(
|
|
||||||
holder: ViewHolder,
|
|
||||||
position: Int,
|
|
||||||
) {
|
|
||||||
with(holder) {
|
with(holder) {
|
||||||
val itm = items[position]
|
val itm = items[position]
|
||||||
|
|
||||||
handleClickListeners(binding, position)
|
|
||||||
handleLinkOpening(binding, position)
|
|
||||||
|
|
||||||
binding.favButton.isSelected = itm.starred
|
binding.favButton.isSelected = itm.starred
|
||||||
if (appSettingsService.getPublicAccess()) {
|
|
||||||
binding.favButton.visibility = View.GONE
|
|
||||||
}
|
|
||||||
|
|
||||||
binding.title.text = itm.title.getHtmlDecoded()
|
binding.title.text = itm.title.getHtmlDecoded()
|
||||||
|
|
||||||
binding.title.setOnTouchListener(LinkOnTouchListener())
|
binding.title.setOnTouchListener(LinkOnTouchListener())
|
||||||
|
|
||||||
binding.title.setLinkTextColor(c.resources.getColor(R.color.colorAccent))
|
binding.title.setLinkTextColor(c.resources.getColor(R.color.colorAccent))
|
||||||
|
|
||||||
binding.sourceTitleAndDate.text =
|
binding.sourceTitleAndDate.text = itm.sourceAuthorAndDate()
|
||||||
try {
|
|
||||||
itm.sourceAuthorAndDate()
|
|
||||||
} catch (e: Exception) {
|
|
||||||
e.sendSilentlyWithAcraWithName("ItemCardAdapter parse date")
|
|
||||||
itm.sourceAuthorOnly()
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!appSettingsService.isFullHeightCardsEnabled()) {
|
if (!appSettingsService.isFullHeightCardsEnabled()) {
|
||||||
binding.itemImage.maxHeight = imageMaxHeight
|
binding.itemImage.maxHeight = imageMaxHeight
|
||||||
@ -118,18 +75,71 @@ class ItemCardAdapter(
|
|||||||
binding.itemImage.setImageDrawable(null)
|
binding.itemImage.setImageDrawable(null)
|
||||||
} else {
|
} else {
|
||||||
binding.itemImage.visibility = View.VISIBLE
|
binding.itemImage.visibility = View.VISIBLE
|
||||||
c.bitmapCenterCrop(itm.getThumbnail(repository.baseUrl), binding.itemImage, appSettingsService)
|
c.bitmapCenterCrop(itm.getThumbnail(repository.baseUrl), binding.itemImage)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (itm.getIcon(repository.baseUrl).isEmpty()) {
|
if (itm.getIcon(repository.baseUrl).isEmpty()) {
|
||||||
binding.sourceImage.setBackgroundAndText(itm.sourcetitle.getHtmlDecoded())
|
val color = generator.getColor(itm.title.getHtmlDecoded())
|
||||||
|
|
||||||
|
val drawable =
|
||||||
|
TextDrawable
|
||||||
|
.builder()
|
||||||
|
.round()
|
||||||
|
.build(itm.title.getHtmlDecoded().toTextDrawableString(), color)
|
||||||
|
binding.sourceImage.setImageDrawable(drawable)
|
||||||
} else {
|
} else {
|
||||||
c.circularDrawable(itm.getIcon(repository.baseUrl), binding.sourceImage, appSettingsService)
|
c.circularBitmapDrawable(itm.getIcon(repository.baseUrl), binding.sourceImage)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inner class ViewHolder(
|
override fun getItemCount(): Int {
|
||||||
val binding: CardItemBinding,
|
return items.size
|
||||||
) : RecyclerView.ViewHolder(binding.root)
|
}
|
||||||
|
|
||||||
|
inner class ViewHolder(val binding: CardItemBinding) : RecyclerView.ViewHolder(binding.root) {
|
||||||
|
init {
|
||||||
|
handleClickListeners()
|
||||||
|
handleLinkOpening()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleClickListeners() {
|
||||||
|
|
||||||
|
binding.favButton.setOnClickListener {
|
||||||
|
val item = items[bindingAdapterPosition]
|
||||||
|
if (item.starred) {
|
||||||
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
|
repository.unstarr(item)
|
||||||
|
}
|
||||||
|
binding.favButton.isSelected = false
|
||||||
|
} else {
|
||||||
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
|
repository.starr(item)
|
||||||
|
}
|
||||||
|
binding.favButton.isSelected = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.shareBtn.setOnClickListener {
|
||||||
|
val item = items[bindingAdapterPosition]
|
||||||
|
c.shareLink(item.getLinkDecoded(), item.title.getHtmlDecoded())
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.browserBtn.setOnClickListener {
|
||||||
|
c.openInBrowserAsNewTask(items[bindingAdapterPosition])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleLinkOpening() {
|
||||||
|
binding.root.setOnClickListener {
|
||||||
|
repository.setReaderItems(items)
|
||||||
|
c.openItemUrl(
|
||||||
|
bindingAdapterPosition,
|
||||||
|
items[bindingAdapterPosition].getLinkDecoded(),
|
||||||
|
appSettingsService.isArticleViewerEnabled(),
|
||||||
|
app
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,79 +1,97 @@
|
|||||||
package bou.amine.apps.readerforselfossv2.android.adapters
|
package bou.amine.apps.readerforselfossv2.android.adapters
|
||||||
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
|
import android.content.Context
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import bou.amine.apps.readerforselfossv2.android.R
|
import bou.amine.apps.readerforselfossv2.android.R
|
||||||
import bou.amine.apps.readerforselfossv2.android.databinding.ListItemBinding
|
import bou.amine.apps.readerforselfossv2.android.databinding.ListItemBinding
|
||||||
|
import bou.amine.apps.readerforselfossv2.android.model.toTextDrawableString
|
||||||
import bou.amine.apps.readerforselfossv2.android.utils.LinkOnTouchListener
|
import bou.amine.apps.readerforselfossv2.android.utils.LinkOnTouchListener
|
||||||
import bou.amine.apps.readerforselfossv2.android.utils.acra.sendSilentlyWithAcraWithName
|
import bou.amine.apps.readerforselfossv2.android.utils.glide.bitmapCenterCrop
|
||||||
import bou.amine.apps.readerforselfossv2.android.utils.glide.circularDrawable
|
import bou.amine.apps.readerforselfossv2.android.utils.glide.circularBitmapDrawable
|
||||||
|
import bou.amine.apps.readerforselfossv2.android.utils.openItemUrl
|
||||||
import bou.amine.apps.readerforselfossv2.model.SelfossModel
|
import bou.amine.apps.readerforselfossv2.model.SelfossModel
|
||||||
import bou.amine.apps.readerforselfossv2.repository.Repository
|
import bou.amine.apps.readerforselfossv2.repository.Repository
|
||||||
import bou.amine.apps.readerforselfossv2.service.AppSettingsService
|
import bou.amine.apps.readerforselfossv2.service.AppSettingsService
|
||||||
import bou.amine.apps.readerforselfossv2.utils.getHtmlDecoded
|
import bou.amine.apps.readerforselfossv2.utils.getHtmlDecoded
|
||||||
import bou.amine.apps.readerforselfossv2.utils.getIcon
|
import bou.amine.apps.readerforselfossv2.utils.getIcon
|
||||||
import bou.amine.apps.readerforselfossv2.utils.getThumbnail
|
import bou.amine.apps.readerforselfossv2.utils.getThumbnail
|
||||||
|
import com.amulyakhare.textdrawable.TextDrawable
|
||||||
|
import com.amulyakhare.textdrawable.util.ColorGenerator
|
||||||
import org.kodein.di.DI
|
import org.kodein.di.DI
|
||||||
import org.kodein.di.android.closestDI
|
import org.kodein.di.android.closestDI
|
||||||
import org.kodein.di.instance
|
import org.kodein.di.instance
|
||||||
|
|
||||||
class ItemListAdapter(
|
class ItemListAdapter(
|
||||||
override val app: Activity,
|
override val app: Activity,
|
||||||
override val items: ArrayList<SelfossModel.Item>,
|
override var items: ArrayList<SelfossModel.Item>,
|
||||||
override val updateHomeItems: (ArrayList<SelfossModel.Item>) -> Unit,
|
override val updateItems: (ArrayList<SelfossModel.Item>) -> Unit
|
||||||
) : ItemsAdapter<ItemListAdapter.ViewHolder>() {
|
) : ItemsAdapter<ItemListAdapter.ViewHolder>() {
|
||||||
override lateinit var binding: ListItemBinding
|
private val generator: ColorGenerator = ColorGenerator.MATERIAL
|
||||||
|
private val c: Context = app.baseContext
|
||||||
|
|
||||||
override val di: DI by closestDI(app)
|
override val di: DI by closestDI(app)
|
||||||
override val repository: Repository by instance()
|
override val repository : Repository by instance()
|
||||||
override val appSettingsService: AppSettingsService by instance()
|
override val appSettingsService : AppSettingsService by instance()
|
||||||
|
|
||||||
override fun onCreateViewHolder(
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||||
parent: ViewGroup,
|
val binding = ListItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
|
||||||
viewType: Int,
|
|
||||||
): ViewHolder {
|
|
||||||
binding = ListItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
|
|
||||||
return ViewHolder(binding)
|
return ViewHolder(binding)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onBindViewHolder(
|
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||||
holder: ViewHolder,
|
|
||||||
position: Int,
|
|
||||||
) {
|
|
||||||
with(holder) {
|
with(holder) {
|
||||||
val itm = items[position]
|
val itm = items[position]
|
||||||
|
|
||||||
handleLinkOpening(binding, position)
|
|
||||||
|
|
||||||
binding.title.text = itm.title.getHtmlDecoded()
|
binding.title.text = itm.title.getHtmlDecoded()
|
||||||
|
|
||||||
binding.title.setOnTouchListener(LinkOnTouchListener())
|
binding.title.setOnTouchListener(LinkOnTouchListener())
|
||||||
|
|
||||||
binding.title.setLinkTextColor(c.resources.getColor(R.color.colorAccent))
|
binding.title.setLinkTextColor(c.resources.getColor(R.color.colorAccent))
|
||||||
|
|
||||||
binding.sourceTitleAndDate.text =
|
binding.sourceTitleAndDate.text = itm.sourceAuthorAndDate()
|
||||||
try {
|
|
||||||
itm.sourceAuthorAndDate()
|
|
||||||
} catch (e: Exception) {
|
|
||||||
e.sendSilentlyWithAcraWithName("ItemListAdapter parse date")
|
|
||||||
itm.sourceAuthorOnly()
|
|
||||||
}
|
|
||||||
|
|
||||||
if (itm.getThumbnail(repository.baseUrl).isEmpty()) {
|
if (itm.getThumbnail(repository.baseUrl).isEmpty()) {
|
||||||
|
|
||||||
if (itm.getIcon(repository.baseUrl).isEmpty()) {
|
if (itm.getIcon(repository.baseUrl).isEmpty()) {
|
||||||
binding.itemImage.setBackgroundAndText(itm.sourcetitle.getHtmlDecoded())
|
val color = generator.getColor(itm.title.getHtmlDecoded())
|
||||||
|
|
||||||
|
val drawable =
|
||||||
|
TextDrawable
|
||||||
|
.builder()
|
||||||
|
.round()
|
||||||
|
.build(itm.title.getHtmlDecoded().toTextDrawableString(), color)
|
||||||
|
|
||||||
|
binding.itemImage.setImageDrawable(drawable)
|
||||||
} else {
|
} else {
|
||||||
c.circularDrawable(itm.getIcon(repository.baseUrl), binding.itemImage, appSettingsService)
|
c.circularBitmapDrawable(itm.getIcon(repository.baseUrl), binding.itemImage)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
c.circularDrawable(itm.getThumbnail(repository.baseUrl), binding.itemImage, appSettingsService)
|
c.bitmapCenterCrop(itm.getThumbnail(repository.baseUrl), binding.itemImage)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inner class ViewHolder(
|
override fun getItemCount(): Int = items.size
|
||||||
val binding: ListItemBinding,
|
|
||||||
) : RecyclerView.ViewHolder(binding.root)
|
inner class ViewHolder(val binding: ListItemBinding) : RecyclerView.ViewHolder(binding.root) {
|
||||||
|
|
||||||
|
init {
|
||||||
|
handleLinkOpening()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleLinkOpening() {
|
||||||
|
binding.root.setOnClickListener {
|
||||||
|
repository.setReaderItems(items)
|
||||||
|
c.openItemUrl(
|
||||||
|
bindingAdapterPosition,
|
||||||
|
items[bindingAdapterPosition].getLinkDecoded(),
|
||||||
|
appSettingsService.isArticleViewerEnabled(),
|
||||||
|
app
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,10 @@
|
|||||||
package bou.amine.apps.readerforselfossv2.android.adapters
|
package bou.amine.apps.readerforselfossv2.android.adapters
|
||||||
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.content.Context
|
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import androidx.viewbinding.ViewBinding
|
|
||||||
import bou.amine.apps.readerforselfossv2.android.R
|
import bou.amine.apps.readerforselfossv2.android.R
|
||||||
import bou.amine.apps.readerforselfossv2.android.utils.openItemUrl
|
|
||||||
import bou.amine.apps.readerforselfossv2.model.SelfossModel
|
import bou.amine.apps.readerforselfossv2.model.SelfossModel
|
||||||
import bou.amine.apps.readerforselfossv2.repository.Repository
|
import bou.amine.apps.readerforselfossv2.repository.Repository
|
||||||
import bou.amine.apps.readerforselfossv2.service.AppSettingsService
|
import bou.amine.apps.readerforselfossv2.service.AppSettingsService
|
||||||
@ -18,78 +15,51 @@ import kotlinx.coroutines.Dispatchers
|
|||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.kodein.di.DIAware
|
import org.kodein.di.DIAware
|
||||||
|
|
||||||
abstract class ItemsAdapter<VH : RecyclerView.ViewHolder?> :
|
abstract class ItemsAdapter<VH : RecyclerView.ViewHolder?> : RecyclerView.Adapter<VH>(), DIAware {
|
||||||
RecyclerView.Adapter<VH>(),
|
abstract var items: ArrayList<SelfossModel.Item>
|
||||||
DIAware {
|
|
||||||
abstract val items: ArrayList<SelfossModel.Item>
|
|
||||||
abstract val repository: Repository
|
abstract val repository: Repository
|
||||||
abstract val binding: ViewBinding
|
|
||||||
abstract val appSettingsService: AppSettingsService
|
abstract val appSettingsService: AppSettingsService
|
||||||
abstract val app: Activity
|
abstract val app: Activity
|
||||||
abstract val updateHomeItems: (ArrayList<SelfossModel.Item>) -> Unit
|
abstract val updateItems: (ArrayList<SelfossModel.Item>) -> Unit
|
||||||
|
|
||||||
protected val c: Context get() = app.baseContext
|
|
||||||
|
|
||||||
fun updateAllItems(items: ArrayList<SelfossModel.Item>) {
|
fun updateAllItems(items: ArrayList<SelfossModel.Item>) {
|
||||||
this.items.clear()
|
this.items = items
|
||||||
this.items.addAll(items)
|
|
||||||
updateHomeItems(items)
|
|
||||||
notifyDataSetChanged()
|
notifyDataSetChanged()
|
||||||
|
updateItems(this.items)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun unmarkSnackbar(
|
private fun unmarkSnackbar(item: SelfossModel.Item, position: Int) {
|
||||||
item: SelfossModel.Item,
|
val s = Snackbar
|
||||||
position: Int,
|
.make(
|
||||||
) {
|
app.findViewById(R.id.coordLayout),
|
||||||
val s =
|
R.string.marked_as_read,
|
||||||
Snackbar
|
Snackbar.LENGTH_LONG
|
||||||
.make(
|
|
||||||
app.findViewById(R.id.coordLayout),
|
|
||||||
R.string.marked_as_read,
|
|
||||||
Snackbar.LENGTH_LONG,
|
|
||||||
).setAction(R.string.undo_string) {
|
|
||||||
unreadItemAtIndex(item, position, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
val view = s.view
|
|
||||||
val tv: TextView = view.findViewById(com.google.android.material.R.id.snackbar_text)
|
|
||||||
tv.setTextColor(Color.WHITE)
|
|
||||||
s.show()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun markSnackbar(
|
|
||||||
item: SelfossModel.Item,
|
|
||||||
position: Int,
|
|
||||||
) {
|
|
||||||
val s =
|
|
||||||
Snackbar
|
|
||||||
.make(
|
|
||||||
app.findViewById(R.id.coordLayout),
|
|
||||||
R.string.marked_as_unread,
|
|
||||||
Snackbar.LENGTH_LONG,
|
|
||||||
).setAction(R.string.undo_string) {
|
|
||||||
readItemAtIndex(item, position, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
val view = s.view
|
|
||||||
val tv: TextView = view.findViewById(com.google.android.material.R.id.snackbar_text)
|
|
||||||
tv.setTextColor(Color.WHITE)
|
|
||||||
s.show()
|
|
||||||
}
|
|
||||||
|
|
||||||
protected fun handleLinkOpening(
|
|
||||||
holderBinding: ViewBinding,
|
|
||||||
position: Int,
|
|
||||||
) {
|
|
||||||
holderBinding.root.setOnClickListener {
|
|
||||||
repository.setReaderItems(items)
|
|
||||||
c.openItemUrl(
|
|
||||||
position,
|
|
||||||
items[position].getLinkDecoded(),
|
|
||||||
appSettingsService.isArticleViewerEnabled(),
|
|
||||||
app,
|
|
||||||
)
|
)
|
||||||
}
|
.setAction(R.string.undo_string) {
|
||||||
|
unreadItemAtIndex(item, position, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
val view = s.view
|
||||||
|
val tv: TextView = view.findViewById(com.google.android.material.R.id.snackbar_text)
|
||||||
|
tv.setTextColor(Color.WHITE)
|
||||||
|
s.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun markSnackbar(item: SelfossModel.Item, position: Int) {
|
||||||
|
val s = Snackbar
|
||||||
|
.make(
|
||||||
|
app.findViewById(R.id.coordLayout),
|
||||||
|
R.string.marked_as_unread,
|
||||||
|
Snackbar.LENGTH_LONG
|
||||||
|
)
|
||||||
|
.setAction(R.string.undo_string) {
|
||||||
|
readItemAtIndex(item, position, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
val view = s.view
|
||||||
|
val tv: TextView = view.findViewById(com.google.android.material.R.id.snackbar_text)
|
||||||
|
tv.setTextColor(Color.WHITE)
|
||||||
|
s.show()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun handleItemAtIndex(position: Int) {
|
fun handleItemAtIndex(position: Int) {
|
||||||
@ -100,19 +70,14 @@ abstract class ItemsAdapter<VH : RecyclerView.ViewHolder?> :
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun readItemAtIndex(
|
private fun readItemAtIndex(item: SelfossModel.Item, position: Int, showSnackbar: Boolean = true) {
|
||||||
item: SelfossModel.Item,
|
|
||||||
position: Int,
|
|
||||||
showSnackbar: Boolean = true,
|
|
||||||
) {
|
|
||||||
CoroutineScope(Dispatchers.IO).launch {
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
repository.markAsRead(item)
|
repository.markAsRead(item)
|
||||||
}
|
}
|
||||||
if (repository.displayedItems == ItemType.UNREAD) {
|
if (repository.displayedItems == ItemType.UNREAD) {
|
||||||
items.remove(item)
|
items.remove(item)
|
||||||
notifyItemRemoved(position)
|
notifyItemRemoved(position)
|
||||||
notifyItemRangeChanged(position, itemCount)
|
updateItems(items)
|
||||||
updateHomeItems(items)
|
|
||||||
} else {
|
} else {
|
||||||
notifyItemChanged(position)
|
notifyItemChanged(position)
|
||||||
}
|
}
|
||||||
@ -121,13 +86,10 @@ abstract class ItemsAdapter<VH : RecyclerView.ViewHolder?> :
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun unreadItemAtIndex(
|
private fun unreadItemAtIndex(item: SelfossModel.Item, position: Int, showSnackbar: Boolean = true) {
|
||||||
item: SelfossModel.Item,
|
|
||||||
position: Int,
|
|
||||||
showSnackbar: Boolean = true,
|
|
||||||
) {
|
|
||||||
CoroutineScope(Dispatchers.IO).launch {
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
repository.unmarkAsRead(item)
|
repository.unmarkAsRead(item)
|
||||||
|
|
||||||
}
|
}
|
||||||
notifyItemChanged(position)
|
notifyItemChanged(position)
|
||||||
if (showSnackbar) {
|
if (showSnackbar) {
|
||||||
@ -135,21 +97,18 @@ abstract class ItemsAdapter<VH : RecyclerView.ViewHolder?> :
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun addItemAtIndex(
|
fun addItemAtIndex(item: SelfossModel.Item, position: Int) {
|
||||||
item: SelfossModel.Item,
|
|
||||||
position: Int,
|
|
||||||
) {
|
|
||||||
items.add(position, item)
|
items.add(position, item)
|
||||||
notifyItemInserted(position)
|
notifyItemInserted(position)
|
||||||
updateHomeItems(items)
|
updateItems(items)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun addItemsAtEnd(newItems: List<SelfossModel.Item>) {
|
fun addItemsAtEnd(newItems: List<SelfossModel.Item>) {
|
||||||
val oldSize = items.size
|
val oldSize = items.size
|
||||||
items.addAll(newItems)
|
items.addAll(newItems)
|
||||||
notifyItemRangeInserted(oldSize, newItems.size)
|
notifyItemRangeInserted(oldSize, newItems.size)
|
||||||
updateHomeItems(items)
|
updateItems(items)
|
||||||
}
|
|
||||||
|
|
||||||
override fun getItemCount(): Int = items.size
|
}
|
||||||
}
|
}
|
@ -10,15 +10,17 @@ import android.widget.Button
|
|||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.constraintlayout.widget.ConstraintLayout
|
import androidx.constraintlayout.widget.ConstraintLayout
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import bou.amine.apps.readerforselfossv2.android.R
|
|
||||||
import bou.amine.apps.readerforselfossv2.android.UpsertSourceActivity
|
import bou.amine.apps.readerforselfossv2.android.UpsertSourceActivity
|
||||||
|
import bou.amine.apps.readerforselfossv2.android.R
|
||||||
import bou.amine.apps.readerforselfossv2.android.databinding.SourceListItemBinding
|
import bou.amine.apps.readerforselfossv2.android.databinding.SourceListItemBinding
|
||||||
import bou.amine.apps.readerforselfossv2.android.utils.glide.circularDrawable
|
import bou.amine.apps.readerforselfossv2.android.model.toTextDrawableString
|
||||||
|
import bou.amine.apps.readerforselfossv2.android.utils.glide.circularBitmapDrawable
|
||||||
import bou.amine.apps.readerforselfossv2.model.SelfossModel
|
import bou.amine.apps.readerforselfossv2.model.SelfossModel
|
||||||
import bou.amine.apps.readerforselfossv2.repository.Repository
|
import bou.amine.apps.readerforselfossv2.repository.Repository
|
||||||
import bou.amine.apps.readerforselfossv2.service.AppSettingsService
|
|
||||||
import bou.amine.apps.readerforselfossv2.utils.getHtmlDecoded
|
import bou.amine.apps.readerforselfossv2.utils.getHtmlDecoded
|
||||||
import bou.amine.apps.readerforselfossv2.utils.getIcon
|
import bou.amine.apps.readerforselfossv2.utils.getIcon
|
||||||
|
import com.amulyakhare.textdrawable.TextDrawable
|
||||||
|
import com.amulyakhare.textdrawable.util.ColorGenerator
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
@ -29,65 +31,37 @@ import org.kodein.di.instance
|
|||||||
|
|
||||||
class SourcesListAdapter(
|
class SourcesListAdapter(
|
||||||
private val app: Activity,
|
private val app: Activity,
|
||||||
private val items: ArrayList<SelfossModel.SourceDetail>,
|
private val items: ArrayList<SelfossModel.Source>
|
||||||
) : RecyclerView.Adapter<SourcesListAdapter.ViewHolder>(),
|
) : RecyclerView.Adapter<SourcesListAdapter.ViewHolder>(), DIAware {
|
||||||
DIAware {
|
|
||||||
private val c: Context = app.baseContext
|
private val c: Context = app.baseContext
|
||||||
|
private val generator: ColorGenerator = ColorGenerator.MATERIAL
|
||||||
private lateinit var binding: SourceListItemBinding
|
private lateinit var binding: SourceListItemBinding
|
||||||
|
|
||||||
override val di: DI by closestDI(app)
|
override val di: DI by closestDI(app)
|
||||||
private val repository: Repository by instance()
|
private val repository : Repository by instance()
|
||||||
private val appSettingsService: AppSettingsService by instance()
|
|
||||||
|
|
||||||
override fun onCreateViewHolder(
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||||
parent: ViewGroup,
|
|
||||||
viewType: Int,
|
|
||||||
): ViewHolder {
|
|
||||||
binding = SourceListItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
|
binding = SourceListItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
|
||||||
return ViewHolder(binding.root)
|
return ViewHolder(binding.root)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onBindViewHolder(
|
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||||
holder: ViewHolder,
|
|
||||||
position: Int,
|
|
||||||
) {
|
|
||||||
val itm = items[position]
|
val itm = items[position]
|
||||||
|
|
||||||
val deleteBtn: Button = holder.mView.findViewById(R.id.deleteBtn)
|
|
||||||
|
|
||||||
deleteBtn.setOnClickListener {
|
|
||||||
val (id, title) = items[position]
|
|
||||||
CoroutineScope(Dispatchers.IO).launch {
|
|
||||||
val successfullyDeletedSource = repository.deleteSource(id, title)
|
|
||||||
if (successfullyDeletedSource) {
|
|
||||||
items.removeAt(position)
|
|
||||||
notifyItemRemoved(position)
|
|
||||||
notifyItemRangeChanged(position, itemCount)
|
|
||||||
} else {
|
|
||||||
Toast
|
|
||||||
.makeText(
|
|
||||||
app,
|
|
||||||
R.string.can_delete_source,
|
|
||||||
Toast.LENGTH_SHORT,
|
|
||||||
).show()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
holder.mView.setOnClickListener {
|
|
||||||
val source = items[position]
|
|
||||||
|
|
||||||
repository.setSelectedSource(source)
|
|
||||||
app.startActivity(Intent(app, UpsertSourceActivity::class.java))
|
|
||||||
}
|
|
||||||
|
|
||||||
if (itm.getIcon(repository.baseUrl).isEmpty()) {
|
if (itm.getIcon(repository.baseUrl).isEmpty()) {
|
||||||
binding.itemImage.setBackgroundAndText(itm.title.getHtmlDecoded())
|
val color = generator.getColor(itm.title.getHtmlDecoded())
|
||||||
|
|
||||||
|
val drawable =
|
||||||
|
TextDrawable
|
||||||
|
.builder()
|
||||||
|
.round()
|
||||||
|
.build(itm.title.getHtmlDecoded().toTextDrawableString(), color)
|
||||||
|
binding.itemImage.setImageDrawable(drawable)
|
||||||
} else {
|
} else {
|
||||||
c.circularDrawable(itm.getIcon(repository.baseUrl), binding.itemImage, appSettingsService)
|
c.circularBitmapDrawable(itm.getIcon(repository.baseUrl), binding.itemImage)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!itm.error.isNullOrBlank()) {
|
if (itm.error.isNotBlank()) {
|
||||||
binding.errorText.visibility = View.VISIBLE
|
binding.errorText.visibility = View.VISIBLE
|
||||||
binding.errorText.text = itm.error
|
binding.errorText.text = itm.error
|
||||||
} else {
|
} else {
|
||||||
@ -103,7 +77,41 @@ class SourcesListAdapter(
|
|||||||
|
|
||||||
override fun getItemCount(): Int = items.size
|
override fun getItemCount(): Int = items.size
|
||||||
|
|
||||||
inner class ViewHolder(
|
inner class ViewHolder(private val mView: ConstraintLayout) : RecyclerView.ViewHolder(mView) {
|
||||||
val mView: ConstraintLayout,
|
|
||||||
) : RecyclerView.ViewHolder(mView)
|
init {
|
||||||
|
handleClickListeners()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleClickListeners() {
|
||||||
|
|
||||||
|
val deleteBtn: Button = mView.findViewById(R.id.deleteBtn)
|
||||||
|
|
||||||
|
deleteBtn.setOnClickListener {
|
||||||
|
val (id, title) = items[bindingAdapterPosition]
|
||||||
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
|
val successfullyDeletedSource = repository.deleteSource(id, title)
|
||||||
|
if (successfullyDeletedSource) {
|
||||||
|
items.removeAt(bindingAdapterPosition)
|
||||||
|
notifyItemRemoved(bindingAdapterPosition)
|
||||||
|
notifyItemRangeChanged(bindingAdapterPosition, itemCount)
|
||||||
|
} else {
|
||||||
|
Toast.makeText(
|
||||||
|
app,
|
||||||
|
R.string.can_delete_source,
|
||||||
|
Toast.LENGTH_SHORT
|
||||||
|
).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mView.setOnClickListener {
|
||||||
|
val source = items[bindingAdapterPosition]
|
||||||
|
|
||||||
|
repository.setSelectedSource(source)
|
||||||
|
app.startActivity(Intent(app, UpsertSourceActivity::class.java))
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,34 +23,30 @@ import kotlinx.coroutines.Dispatchers
|
|||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.kodein.di.DIAware
|
import org.kodein.di.DIAware
|
||||||
import org.kodein.di.instance
|
import org.kodein.di.instance
|
||||||
import java.util.Timer
|
import java.util.*
|
||||||
import kotlin.concurrent.schedule
|
import kotlin.concurrent.schedule
|
||||||
|
|
||||||
private const val NOTIFICATION_DELAY = 4000L
|
class LoadingWorker(val context: Context, params: WorkerParameters) : Worker(context, params),
|
||||||
|
|
||||||
class LoadingWorker(
|
|
||||||
val context: Context,
|
|
||||||
params: WorkerParameters,
|
|
||||||
) : Worker(context, params),
|
|
||||||
DIAware {
|
DIAware {
|
||||||
|
|
||||||
override val di by lazy { (applicationContext as MyApp).di }
|
override val di by lazy { (applicationContext as MyApp).di }
|
||||||
private val repository: Repository by instance()
|
private val repository: Repository by instance()
|
||||||
private val appSettingsService: AppSettingsService by instance()
|
private val appSettingsService: AppSettingsService by instance()
|
||||||
|
|
||||||
override fun doWork(): Result {
|
override fun doWork(): Result {
|
||||||
if (appSettingsService.isPeriodicRefreshEnabled() && isNetworkAccessible(context)) {
|
if (appSettingsService.isPeriodicRefreshEnabled() && isNetworkAccessible(context)) {
|
||||||
|
|
||||||
CoroutineScope(Dispatchers.IO).launch {
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
val notificationManager =
|
val notificationManager =
|
||||||
applicationContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
applicationContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||||
|
|
||||||
val notification =
|
val notification =
|
||||||
NotificationCompat
|
NotificationCompat.Builder(applicationContext, AppSettingsService.syncChannelId)
|
||||||
.Builder(applicationContext, AppSettingsService.SYNC_CHANNEL_ID)
|
|
||||||
.setContentTitle(context.getString(R.string.loading_notification_title))
|
.setContentTitle(context.getString(R.string.loading_notification_title))
|
||||||
.setContentText(context.getString(R.string.loading_notification_text))
|
.setContentText(context.getString(R.string.loading_notification_text))
|
||||||
.setOngoing(true)
|
.setOngoing(true)
|
||||||
.setPriority(PRIORITY_LOW)
|
.setPriority(PRIORITY_LOW)
|
||||||
.setChannelId(AppSettingsService.SYNC_CHANNEL_ID)
|
.setChannelId(AppSettingsService.syncChannelId)
|
||||||
.setSmallIcon(R.drawable.ic_stat_cloud_download_black_24dp)
|
.setSmallIcon(R.drawable.ic_stat_cloud_download_black_24dp)
|
||||||
|
|
||||||
notificationManager.notify(1, notification.build())
|
notificationManager.notify(1, notification.build())
|
||||||
@ -63,7 +59,7 @@ class LoadingWorker(
|
|||||||
handleNewItemsNotification(apiItems, notificationManager)
|
handleNewItemsNotification(apiItems, notificationManager)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
apiItems.map { it.preloadImages(context, appSettingsService) }
|
apiItems.map { it.preloadImages(context) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Result.success()
|
return Result.success()
|
||||||
@ -71,48 +67,49 @@ class LoadingWorker(
|
|||||||
|
|
||||||
private fun handleNewItemsNotification(
|
private fun handleNewItemsNotification(
|
||||||
newItems: List<SelfossModel.Item>?,
|
newItems: List<SelfossModel.Item>?,
|
||||||
notificationManager: NotificationManager,
|
notificationManager: NotificationManager
|
||||||
) {
|
) {
|
||||||
CoroutineScope(Dispatchers.IO).launch {
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
val apiItems = newItems.orEmpty()
|
val apiItems = newItems.orEmpty()
|
||||||
|
|
||||||
|
|
||||||
val newSize = apiItems.filter { it.unread }.size
|
val newSize = apiItems.filter { it.unread }.size
|
||||||
if (newSize > 0) {
|
if (newSize > 0) {
|
||||||
val intent =
|
|
||||||
Intent(context, MainActivity::class.java).apply {
|
val intent = Intent(context, MainActivity::class.java).apply {
|
||||||
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
|
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
|
||||||
}
|
}
|
||||||
val pflags =
|
val pflags = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
PendingIntent.FLAG_IMMUTABLE
|
||||||
PendingIntent.FLAG_IMMUTABLE
|
} else {
|
||||||
} else {
|
0
|
||||||
0
|
}
|
||||||
}
|
|
||||||
val pendingIntent: PendingIntent =
|
val pendingIntent: PendingIntent =
|
||||||
PendingIntent.getActivity(context, 0, intent, pflags)
|
PendingIntent.getActivity(context, 0, intent, pflags)
|
||||||
|
|
||||||
val newItemsNotification =
|
val newItemsNotification =
|
||||||
NotificationCompat
|
NotificationCompat.Builder(
|
||||||
.Builder(
|
applicationContext,
|
||||||
applicationContext,
|
AppSettingsService.newItemsChannelId
|
||||||
AppSettingsService.NEW_ITEMS_CHANNEL,
|
)
|
||||||
).setContentTitle(context.getString(R.string.new_items_notification_title))
|
.setContentTitle(context.getString(R.string.new_items_notification_title))
|
||||||
.setContentText(
|
.setContentText(
|
||||||
context.getString(
|
context.getString(
|
||||||
R.string.new_items_notification_text,
|
R.string.new_items_notification_text,
|
||||||
newSize,
|
newSize
|
||||||
),
|
)
|
||||||
).setPriority(PRIORITY_DEFAULT)
|
)
|
||||||
.setChannelId(AppSettingsService.NEW_ITEMS_CHANNEL)
|
.setPriority(PRIORITY_DEFAULT)
|
||||||
|
.setChannelId(AppSettingsService.newItemsChannelId)
|
||||||
.setContentIntent(pendingIntent)
|
.setContentIntent(pendingIntent)
|
||||||
.setAutoCancel(true)
|
.setAutoCancel(true)
|
||||||
.setSmallIcon(R.drawable.ic_tab_fiber_new_black_24dp)
|
.setSmallIcon(R.drawable.ic_tab_fiber_new_black_24dp)
|
||||||
|
|
||||||
Timer("", false).schedule(NOTIFICATION_DELAY) {
|
Timer("", false).schedule(4000) {
|
||||||
notificationManager.notify(2, newItemsNotification.build())
|
notificationManager.notify(2, newItemsNotification.build())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Timer("", false).schedule(NOTIFICATION_DELAY) {
|
Timer("", false).schedule(4000) {
|
||||||
notificationManager.cancel(1)
|
notificationManager.cancel(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,24 +1,22 @@
|
|||||||
package bou.amine.apps.readerforselfossv2.android.fragments
|
package bou.amine.apps.readerforselfossv2.android.fragments
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.content.res.ColorStateList
|
||||||
import android.content.res.TypedArray
|
import android.content.res.TypedArray
|
||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
import android.graphics.Typeface
|
import android.graphics.Typeface
|
||||||
|
import android.graphics.drawable.ColorDrawable
|
||||||
|
import android.net.Uri
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.util.TypedValue.DATA_NULL_UNDEFINED
|
import android.util.TypedValue
|
||||||
import android.view.GestureDetector
|
import android.view.*
|
||||||
import android.view.InflateException
|
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.view.MotionEvent
|
|
||||||
import android.view.View
|
|
||||||
import android.view.ViewGroup
|
|
||||||
import android.webkit.WebResourceResponse
|
import android.webkit.WebResourceResponse
|
||||||
import android.webkit.WebSettings
|
import android.webkit.WebSettings
|
||||||
import android.webkit.WebView
|
import android.webkit.WebView
|
||||||
import android.webkit.WebViewClient
|
import android.webkit.WebViewClient
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.appcompat.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
|
import androidx.core.widget.NestedScrollView
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import bou.amine.apps.readerforselfossv2.android.ImageActivity
|
import bou.amine.apps.readerforselfossv2.android.ImageActivity
|
||||||
import bou.amine.apps.readerforselfossv2.android.R
|
import bou.amine.apps.readerforselfossv2.android.R
|
||||||
@ -26,19 +24,13 @@ import bou.amine.apps.readerforselfossv2.android.databinding.FragmentArticleBind
|
|||||||
import bou.amine.apps.readerforselfossv2.android.model.ParecelableItem
|
import bou.amine.apps.readerforselfossv2.android.model.ParecelableItem
|
||||||
import bou.amine.apps.readerforselfossv2.android.model.toModel
|
import bou.amine.apps.readerforselfossv2.android.model.toModel
|
||||||
import bou.amine.apps.readerforselfossv2.android.model.toParcelable
|
import bou.amine.apps.readerforselfossv2.android.model.toParcelable
|
||||||
import bou.amine.apps.readerforselfossv2.android.utils.acra.sendSilentlyWithAcraWithName
|
import bou.amine.apps.readerforselfossv2.android.sendSilentlyWithAcraWithName
|
||||||
import bou.amine.apps.readerforselfossv2.android.utils.bottombar.addHomeMadeActionItem
|
|
||||||
import bou.amine.apps.readerforselfossv2.android.utils.getColorFromAttr
|
|
||||||
import bou.amine.apps.readerforselfossv2.android.utils.glide.bitmapFitCenter
|
|
||||||
import bou.amine.apps.readerforselfossv2.android.utils.glide.getBitmapInputStream
|
import bou.amine.apps.readerforselfossv2.android.utils.glide.getBitmapInputStream
|
||||||
import bou.amine.apps.readerforselfossv2.android.utils.glide.getGlideImageForResource
|
import bou.amine.apps.readerforselfossv2.android.utils.openInBrowserAsNewTask
|
||||||
import bou.amine.apps.readerforselfossv2.android.utils.isUrlValid
|
|
||||||
import bou.amine.apps.readerforselfossv2.android.utils.maybeIfContext
|
|
||||||
import bou.amine.apps.readerforselfossv2.android.utils.openItemUrlInBrowserAsNewTask
|
|
||||||
import bou.amine.apps.readerforselfossv2.android.utils.openUrlInBrowserAsNewTask
|
|
||||||
import bou.amine.apps.readerforselfossv2.android.utils.shareLink
|
import bou.amine.apps.readerforselfossv2.android.utils.shareLink
|
||||||
import bou.amine.apps.readerforselfossv2.model.MercuryModel
|
import bou.amine.apps.readerforselfossv2.model.MercuryModel
|
||||||
import bou.amine.apps.readerforselfossv2.model.SelfossModel
|
import bou.amine.apps.readerforselfossv2.model.SelfossModel
|
||||||
|
import bou.amine.apps.readerforselfossv2.model.StatusAndData
|
||||||
import bou.amine.apps.readerforselfossv2.repository.Repository
|
import bou.amine.apps.readerforselfossv2.repository.Repository
|
||||||
import bou.amine.apps.readerforselfossv2.rest.MercuryApi
|
import bou.amine.apps.readerforselfossv2.rest.MercuryApi
|
||||||
import bou.amine.apps.readerforselfossv2.service.AppSettingsService
|
import bou.amine.apps.readerforselfossv2.service.AppSettingsService
|
||||||
@ -46,34 +38,29 @@ import bou.amine.apps.readerforselfossv2.utils.getHtmlDecoded
|
|||||||
import bou.amine.apps.readerforselfossv2.utils.getImages
|
import bou.amine.apps.readerforselfossv2.utils.getImages
|
||||||
import bou.amine.apps.readerforselfossv2.utils.getThumbnail
|
import bou.amine.apps.readerforselfossv2.utils.getThumbnail
|
||||||
import bou.amine.apps.readerforselfossv2.utils.isEmptyOrNullOrNullString
|
import bou.amine.apps.readerforselfossv2.utils.isEmptyOrNullOrNullString
|
||||||
import com.leinardi.android.speeddial.SpeedDialView
|
import com.bumptech.glide.Glide
|
||||||
|
import com.bumptech.glide.load.engine.DiskCacheStrategy
|
||||||
|
import com.bumptech.glide.request.RequestOptions
|
||||||
|
import com.github.rubensousa.floatingtoolbar.FloatingToolbar
|
||||||
|
import com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.acra.ktx.sendSilentlyWithAcra
|
|
||||||
import org.kodein.di.DI
|
import org.kodein.di.DI
|
||||||
import org.kodein.di.DIAware
|
import org.kodein.di.DIAware
|
||||||
import org.kodein.di.android.x.closestDI
|
import org.kodein.di.android.x.closestDI
|
||||||
import org.kodein.di.instance
|
import org.kodein.di.instance
|
||||||
import java.net.MalformedURLException
|
import java.net.MalformedURLException
|
||||||
|
import java.net.SocketTimeoutException
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
import java.util.Locale
|
import java.util.*
|
||||||
import java.util.concurrent.ExecutionException
|
import java.util.concurrent.ExecutionException
|
||||||
|
|
||||||
|
|
||||||
private const val IMAGE_JPG = "image/jpg"
|
private const val IMAGE_JPG = "image/jpg"
|
||||||
private const val IMAGE_PNG = "image/png"
|
|
||||||
private const val IMAGE_WEBP = "image/webp"
|
|
||||||
|
|
||||||
private const val WHITE_COLOR_HEX = 0xFFFFFF
|
class ArticleFragment : Fragment(), DIAware {
|
||||||
|
private var fontSize: Int = 16
|
||||||
private const val DEFAULT_FONT_SIZE = 16
|
|
||||||
|
|
||||||
class ArticleFragment :
|
|
||||||
Fragment(),
|
|
||||||
DIAware {
|
|
||||||
private var colorOnSurface: Int = 0
|
|
||||||
private var colorSurface: Int = 0
|
|
||||||
private var fontSize: Int = DEFAULT_FONT_SIZE
|
|
||||||
private lateinit var item: SelfossModel.Item
|
private lateinit var item: SelfossModel.Item
|
||||||
private lateinit var url: String
|
private lateinit var url: String
|
||||||
private lateinit var contentText: String
|
private lateinit var contentText: String
|
||||||
@ -81,7 +68,7 @@ class ArticleFragment :
|
|||||||
private lateinit var contentImage: String
|
private lateinit var contentImage: String
|
||||||
private lateinit var contentTitle: String
|
private lateinit var contentTitle: String
|
||||||
private lateinit var allImages: ArrayList<String>
|
private lateinit var allImages: ArrayList<String>
|
||||||
private lateinit var fab: SpeedDialView
|
private lateinit var fab: FloatingActionButton
|
||||||
private lateinit var textAlignment: String
|
private lateinit var textAlignment: String
|
||||||
private lateinit var binding: FragmentArticleBinding
|
private lateinit var binding: FragmentArticleBinding
|
||||||
|
|
||||||
@ -92,9 +79,11 @@ class ArticleFragment :
|
|||||||
private var typeface: Typeface? = null
|
private var typeface: Typeface? = null
|
||||||
private var resId: Int = 0
|
private var resId: Int = 0
|
||||||
private var font = ""
|
private var font = ""
|
||||||
|
private var staticBar = false
|
||||||
|
|
||||||
private val mercuryApi: MercuryApi by instance()
|
private val mercuryApi: MercuryApi by instance()
|
||||||
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
@ -103,42 +92,39 @@ class ArticleFragment :
|
|||||||
item = pi.toModel()
|
item = pi.toModel()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("detekt:LongMethod")
|
|
||||||
override fun onCreateView(
|
override fun onCreateView(
|
||||||
inflater: LayoutInflater,
|
inflater: LayoutInflater,
|
||||||
container: ViewGroup?,
|
container: ViewGroup?,
|
||||||
savedInstanceState: Bundle?,
|
savedInstanceState: Bundle?
|
||||||
): View {
|
): View {
|
||||||
try {
|
try {
|
||||||
binding = FragmentArticleBinding.inflate(inflater, container, false)
|
binding = FragmentArticleBinding.inflate(inflater, container, false)
|
||||||
|
|
||||||
try {
|
url = item.getLinkDecoded()
|
||||||
url = item.getLinkDecoded()
|
|
||||||
} catch (e: Exception) {
|
|
||||||
e.sendSilentlyWithAcra()
|
|
||||||
}
|
|
||||||
|
|
||||||
colorOnSurface = getColorFromAttr(com.google.android.material.R.attr.colorOnSurface)
|
|
||||||
colorSurface = getColorFromAttr(com.google.android.material.R.attr.colorSurface)
|
|
||||||
|
|
||||||
contentText = item.content
|
contentText = item.content
|
||||||
contentTitle = item.title.getHtmlDecoded()
|
contentTitle = item.title.getHtmlDecoded()
|
||||||
contentImage = item.getThumbnail(repository.baseUrl)
|
contentImage = item.getThumbnail(repository.baseUrl)
|
||||||
contentSource =
|
contentSource = item.sourceAuthorAndDate()
|
||||||
try {
|
|
||||||
item.sourceAuthorAndDate()
|
|
||||||
} catch (e: Exception) {
|
|
||||||
e.sendSilentlyWithAcraWithName("Article Fragment parse date")
|
|
||||||
item.sourceAuthorOnly()
|
|
||||||
}
|
|
||||||
allImages = item.getImages()
|
allImages = item.getImages()
|
||||||
|
|
||||||
fontSize = appSettingsService.getFontSize()
|
fontSize = appSettingsService.getFontSize()
|
||||||
|
staticBar = appSettingsService.isStaticBarEnabled()
|
||||||
font = appSettingsService.getFont()
|
font = appSettingsService.getFont()
|
||||||
|
|
||||||
refreshAlignment()
|
refreshAlignment()
|
||||||
|
|
||||||
handleFloatingToolbar()
|
fab = binding.fab
|
||||||
|
|
||||||
|
fab.backgroundTintList = ColorStateList.valueOf(resources.getColor(R.color.colorAccent))
|
||||||
|
|
||||||
|
fab.rippleColor = resources.getColor(R.color.colorAccentDark)
|
||||||
|
|
||||||
|
val floatingToolbar: FloatingToolbar = handleFloatingToolbar()
|
||||||
|
|
||||||
|
if (staticBar) {
|
||||||
|
fab.hide()
|
||||||
|
floatingToolbar.show()
|
||||||
|
}
|
||||||
|
|
||||||
binding.source.text = contentSource
|
binding.source.text = contentSource
|
||||||
if (typeface != null) {
|
if (typeface != null) {
|
||||||
@ -146,21 +132,35 @@ class ArticleFragment :
|
|||||||
}
|
}
|
||||||
|
|
||||||
handleContent()
|
handleContent()
|
||||||
|
|
||||||
|
binding.nestedScrollView.setOnScrollChangeListener(
|
||||||
|
NestedScrollView.OnScrollChangeListener { _, _, scrollY, _, oldScrollY ->
|
||||||
|
if (scrollY > oldScrollY) {
|
||||||
|
floatingToolbar.hide()
|
||||||
|
fab.hide()
|
||||||
|
} else {
|
||||||
|
if (staticBar) {
|
||||||
|
floatingToolbar.show()
|
||||||
|
} else {
|
||||||
|
if (floatingToolbar.isShowing) floatingToolbar.hide() else fab.show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
} catch (e: InflateException) {
|
} catch (e: InflateException) {
|
||||||
e.sendSilentlyWithAcraWithName("webview not available")
|
e.sendSilentlyWithAcraWithName("webview not available")
|
||||||
maybeIfContext {
|
AlertDialog.Builder(requireContext())
|
||||||
AlertDialog
|
.setMessage(requireContext().getString(R.string.webview_dialog_issue_message))
|
||||||
.Builder(it)
|
.setTitle(requireContext().getString(R.string.webview_dialog_issue_title))
|
||||||
.setMessage(it.getString(R.string.webview_dialog_issue_message))
|
.setPositiveButton(
|
||||||
.setTitle(it.getString(R.string.webview_dialog_issue_title))
|
android.R.string.ok
|
||||||
.setPositiveButton(
|
) { _, _ ->
|
||||||
android.R.string.ok,
|
appSettingsService.disableArticleViewer()
|
||||||
) { _, _ ->
|
requireActivity().finish()
|
||||||
appSettingsService.disableArticleViewer()
|
}
|
||||||
requireActivity().finish()
|
.create()
|
||||||
}.create()
|
.show()
|
||||||
.show()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return binding.root
|
return binding.root
|
||||||
@ -181,286 +181,249 @@ class ArticleFragment :
|
|||||||
|
|
||||||
if (!contentImage.isEmptyOrNullOrNullString() && context != null) {
|
if (!contentImage.isEmptyOrNullOrNullString() && context != null) {
|
||||||
binding.imageView.visibility = View.VISIBLE
|
binding.imageView.visibility = View.VISIBLE
|
||||||
maybeIfContext { it.bitmapFitCenter(contentImage, binding.imageView, appSettingsService) }
|
Glide
|
||||||
|
.with(requireContext())
|
||||||
|
.asBitmap()
|
||||||
|
.load(contentImage)
|
||||||
|
.apply(RequestOptions.fitCenterTransform())
|
||||||
|
.into(binding.imageView)
|
||||||
} else {
|
} else {
|
||||||
binding.imageView.visibility = View.GONE
|
binding.imageView.visibility = View.GONE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleFloatingToolbar() {
|
private fun handleFloatingToolbar(): FloatingToolbar {
|
||||||
fab = binding.speedDial
|
val floatingToolbar: FloatingToolbar = binding.floatingToolbar
|
||||||
fab.mainFabClosedIconColor = colorOnSurface
|
floatingToolbar.attachFab(fab)
|
||||||
fab.mainFabOpenedIconColor = colorOnSurface
|
|
||||||
|
|
||||||
maybeIfContext { handleFloatingToolbarActionItems(it) }
|
floatingToolbar.background = ColorDrawable(resources.getColor(R.color.colorAccent))
|
||||||
|
|
||||||
fab.setOnActionSelectedListener { actionItem ->
|
floatingToolbar.setClickListener(
|
||||||
when (actionItem.id) {
|
object : FloatingToolbar.ItemClickListener {
|
||||||
R.id.share_action -> requireActivity().shareLink(url, contentTitle)
|
override fun onItemClick(item: MenuItem) {
|
||||||
R.id.open_action -> requireActivity().openItemUrlInBrowserAsNewTask(this@ArticleFragment.item)
|
when (item.itemId) {
|
||||||
R.id.unread_action ->
|
R.id.share_action -> requireActivity().shareLink(url, contentTitle)
|
||||||
if (this@ArticleFragment.item.unread) {
|
R.id.open_action -> requireActivity().openInBrowserAsNewTask(this@ArticleFragment.item)
|
||||||
CoroutineScope(Dispatchers.IO).launch {
|
R.id.unread_action -> if (context != null) {
|
||||||
repository.markAsRead(this@ArticleFragment.item)
|
if (this@ArticleFragment.item.unread) {
|
||||||
}
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
this@ArticleFragment.item.unread = false
|
repository.markAsRead(this@ArticleFragment.item)
|
||||||
maybeIfContext {
|
}
|
||||||
Toast
|
this@ArticleFragment.item.unread = false
|
||||||
.makeText(
|
Toast.makeText(
|
||||||
it,
|
context,
|
||||||
R.string.marked_as_read,
|
R.string.marked_as_read,
|
||||||
Toast.LENGTH_LONG,
|
Toast.LENGTH_LONG
|
||||||
).show()
|
).show()
|
||||||
}
|
} else {
|
||||||
} else {
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
CoroutineScope(Dispatchers.IO).launch {
|
repository.unmarkAsRead(this@ArticleFragment.item)
|
||||||
repository.unmarkAsRead(this@ArticleFragment.item)
|
}
|
||||||
}
|
this@ArticleFragment.item.unread = true
|
||||||
this@ArticleFragment.item.unread = true
|
Toast.makeText(
|
||||||
maybeIfContext {
|
context,
|
||||||
Toast
|
|
||||||
.makeText(
|
|
||||||
it,
|
|
||||||
R.string.marked_as_unread,
|
R.string.marked_as_unread,
|
||||||
Toast.LENGTH_LONG,
|
Toast.LENGTH_LONG
|
||||||
).show()
|
).show()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
else -> Unit
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
else -> Unit
|
override fun onItemLongClick(item: MenuItem?) {
|
||||||
|
// We do nothing
|
||||||
|
}
|
||||||
}
|
}
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun handleFloatingToolbarActionItems(c: Context) {
|
|
||||||
fab.addHomeMadeActionItem(
|
|
||||||
R.id.share_action,
|
|
||||||
resources.getDrawable(R.drawable.ic_share_white_24dp),
|
|
||||||
R.string.reader_action_share,
|
|
||||||
colorOnSurface,
|
|
||||||
colorSurface,
|
|
||||||
c,
|
|
||||||
)
|
|
||||||
fab.addHomeMadeActionItem(
|
|
||||||
R.id.open_action,
|
|
||||||
resources.getDrawable(R.drawable.ic_open_in_browser_white_24dp),
|
|
||||||
R.string.reader_action_open,
|
|
||||||
colorOnSurface,
|
|
||||||
colorSurface,
|
|
||||||
c,
|
|
||||||
)
|
|
||||||
fab.addHomeMadeActionItem(
|
|
||||||
R.id.unread_action,
|
|
||||||
resources.getDrawable(R.drawable.ic_baseline_white_eye_24dp),
|
|
||||||
R.string.unmark,
|
|
||||||
colorOnSurface,
|
|
||||||
colorSurface,
|
|
||||||
c,
|
|
||||||
)
|
)
|
||||||
|
return floatingToolbar
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun refreshAlignment() {
|
private fun refreshAlignment() {
|
||||||
textAlignment =
|
textAlignment = when (appSettingsService.getActiveAllignment()) {
|
||||||
when (appSettingsService.getActiveAllignment()) {
|
1 -> "justify"
|
||||||
1 -> "justify"
|
2 -> "left"
|
||||||
2 -> "left"
|
else -> "justify"
|
||||||
else -> "justify"
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("detekt:SwallowedException")
|
|
||||||
private fun getContentFromMercury() {
|
private fun getContentFromMercury() {
|
||||||
binding.progressBar.visibility = View.VISIBLE
|
binding.progressBar.visibility = View.VISIBLE
|
||||||
|
|
||||||
CoroutineScope(Dispatchers.Main).launch {
|
CoroutineScope(Dispatchers.Main).launch {
|
||||||
try {
|
try {
|
||||||
val response = mercuryApi.query(url)
|
val response = mercuryApi.query(url)
|
||||||
if (response.success && response.data != null) {
|
if (response.success && response.data != null && !response.data?.content.isNullOrEmpty()) {
|
||||||
handleMercuryData(response.data!!)
|
binding.titleView.text = response.data!!.title.orEmpty()
|
||||||
|
if (typeface != null) {
|
||||||
|
binding.titleView.typeface = typeface
|
||||||
|
}
|
||||||
|
URL(response.data!!.url)
|
||||||
|
url = response.data!!.url
|
||||||
|
|
||||||
|
contentText = response.data!!.content.orEmpty()
|
||||||
|
htmlToWebview()
|
||||||
|
|
||||||
|
handleLeadImage(response)
|
||||||
|
|
||||||
|
binding.nestedScrollView.scrollTo(0, 0)
|
||||||
|
binding.progressBar.visibility = View.GONE
|
||||||
} else {
|
} else {
|
||||||
openInBrowserAfterFailing()
|
openInBrowserAfterFailing()
|
||||||
}
|
}
|
||||||
|
} catch (e: SocketTimeoutException) {
|
||||||
|
openInBrowserAfterFailing()
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
|
e.sendSilentlyWithAcraWithName("getContentFromMercury > $url")
|
||||||
openInBrowserAfterFailing()
|
openInBrowserAfterFailing()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleMercuryData(data: MercuryModel.ParsedContent) {
|
private fun handleLeadImage(response: StatusAndData<MercuryModel.ParsedContent>) {
|
||||||
if (data.error == true || data.failed == true) {
|
if (!response.data?.lead_image_url.isNullOrEmpty() && context != null) {
|
||||||
openInBrowserAfterFailing()
|
binding.imageView.visibility = View.VISIBLE
|
||||||
} else {
|
Glide
|
||||||
binding.titleView.text = data.title.orEmpty()
|
.with(requireContext())
|
||||||
if (typeface != null) {
|
.asBitmap()
|
||||||
binding.titleView.typeface = typeface
|
.load(
|
||||||
}
|
response.data!!.lead_image_url.orEmpty()
|
||||||
URL(data.url)
|
)
|
||||||
url = data.url!!
|
.apply(RequestOptions.fitCenterTransform())
|
||||||
|
.into(binding.imageView)
|
||||||
contentText = data.content.orEmpty()
|
|
||||||
htmlToWebview()
|
|
||||||
|
|
||||||
handleLeadImage(data.lead_image_url)
|
|
||||||
|
|
||||||
binding.nestedScrollView.scrollTo(0, 0)
|
|
||||||
binding.progressBar.visibility = View.GONE
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun handleLeadImage(leadImageUrl: String?) {
|
|
||||||
if (!leadImageUrl.isNullOrEmpty()) {
|
|
||||||
maybeIfContext {
|
|
||||||
binding.imageView.visibility = View.VISIBLE
|
|
||||||
it.bitmapFitCenter(leadImageUrl, binding.imageView, appSettingsService)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
binding.imageView.visibility = View.GONE
|
binding.imageView.visibility = View.GONE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleImageLoading() {
|
private fun handleImageLoading() {
|
||||||
binding.webcontent.webViewClient =
|
binding.webcontent.webViewClient = object : WebViewClient() {
|
||||||
object : WebViewClient() {
|
@Deprecated("Deprecated in Java")
|
||||||
@Deprecated("Deprecated in Java")
|
override fun shouldOverrideUrlLoading(view: WebView?, url: String): Boolean {
|
||||||
override fun shouldOverrideUrlLoading(
|
if (binding.webcontent.hitTestResult.type != WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE) {
|
||||||
view: WebView?,
|
requireContext().startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(url)))
|
||||||
url: String,
|
}
|
||||||
): Boolean =
|
return true
|
||||||
if (url.isUrlValid() &&
|
}
|
||||||
binding.webcontent.hitTestResult.type != WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE
|
|
||||||
) {
|
|
||||||
maybeIfContext { it.openUrlInBrowserAsNewTask(url) }
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
@Suppress("detekt:SwallowedException", "detekt:ReturnCount")
|
|
||||||
@Deprecated("Deprecated in Java")
|
|
||||||
override fun shouldInterceptRequest(
|
|
||||||
view: WebView,
|
|
||||||
url: String,
|
|
||||||
): WebResourceResponse? {
|
|
||||||
val (mime: String?, compression: Bitmap.CompressFormat) =
|
|
||||||
if (url
|
|
||||||
.lowercase(Locale.US)
|
|
||||||
.contains(".jpg") ||
|
|
||||||
url.lowercase(Locale.US).contains(".jpeg")
|
|
||||||
) {
|
|
||||||
Pair(IMAGE_JPG, Bitmap.CompressFormat.JPEG)
|
|
||||||
} else if (url.lowercase(Locale.US).contains(".png")) {
|
|
||||||
Pair(IMAGE_PNG, Bitmap.CompressFormat.PNG)
|
|
||||||
} else if (url.lowercase(Locale.US).contains(".webp")) {
|
|
||||||
Pair(IMAGE_WEBP, Bitmap.CompressFormat.WEBP)
|
|
||||||
} else {
|
|
||||||
return super.shouldInterceptRequest(view, url)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
@Deprecated("Deprecated in Java")
|
||||||
|
override fun shouldInterceptRequest(view: WebView, url: String): WebResourceResponse? {
|
||||||
|
val glideOptions = RequestOptions.diskCacheStrategyOf(DiskCacheStrategy.ALL)
|
||||||
|
if (url.lowercase(Locale.US).contains(".jpg") || url.lowercase(Locale.US)
|
||||||
|
.contains(".jpeg")
|
||||||
|
) {
|
||||||
try {
|
try {
|
||||||
val image = view.getGlideImageForResource(url, appSettingsService)
|
val image =
|
||||||
|
Glide.with(view).asBitmap().apply(glideOptions).load(url).submit().get()
|
||||||
return WebResourceResponse(
|
return WebResourceResponse(
|
||||||
mime,
|
IMAGE_JPG,
|
||||||
"UTF-8",
|
"UTF-8",
|
||||||
getBitmapInputStream(image, compression),
|
getBitmapInputStream(image, Bitmap.CompressFormat.JPEG)
|
||||||
)
|
)
|
||||||
} catch (e: ExecutionException) {
|
} catch (e: ExecutionException) {
|
||||||
return super.shouldInterceptRequest(view, url)
|
e.sendSilentlyWithAcraWithName("shouldInterceptRequest > jpeg > $url")
|
||||||
|
}
|
||||||
|
} else if (url.lowercase(Locale.US).contains(".png")) {
|
||||||
|
try {
|
||||||
|
val image =
|
||||||
|
Glide.with(view).asBitmap().apply(glideOptions).load(url).submit().get()
|
||||||
|
return WebResourceResponse(
|
||||||
|
IMAGE_JPG,
|
||||||
|
"UTF-8",
|
||||||
|
getBitmapInputStream(image, Bitmap.CompressFormat.PNG)
|
||||||
|
)
|
||||||
|
} catch (e: ExecutionException) {
|
||||||
|
e.sendSilentlyWithAcraWithName("shouldInterceptRequest > png > $url")
|
||||||
|
}
|
||||||
|
} else if (url.lowercase(Locale.US).contains(".webp")) {
|
||||||
|
try {
|
||||||
|
val image =
|
||||||
|
Glide.with(view).asBitmap().apply(glideOptions).load(url).submit().get()
|
||||||
|
return WebResourceResponse(
|
||||||
|
IMAGE_JPG,
|
||||||
|
"UTF-8",
|
||||||
|
getBitmapInputStream(image, Bitmap.CompressFormat.WEBP)
|
||||||
|
)
|
||||||
|
} catch (e: ExecutionException) {
|
||||||
|
e.sendSilentlyWithAcraWithName("shouldInterceptRequest > webp > $url")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return super.shouldInterceptRequest(view, url)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("detekt:LongMethod", "detekt:ImplicitDefaultLocale")
|
|
||||||
private fun htmlToWebview() {
|
private fun htmlToWebview() {
|
||||||
maybeIfContext {
|
|
||||||
val attrs: IntArray = intArrayOf(android.R.attr.fontFamily)
|
|
||||||
val a: TypedArray = it.obtainStyledAttributes(resId, attrs)
|
|
||||||
|
|
||||||
binding.webcontent.settings.standardFontFamily = a.getString(0)
|
val attrs: IntArray = intArrayOf(android.R.attr.fontFamily)
|
||||||
""
|
val a: TypedArray = requireContext().obtainStyledAttributes(resId, attrs)
|
||||||
}
|
|
||||||
|
|
||||||
|
binding.webcontent.settings.standardFontFamily = a.getString(0)
|
||||||
binding.webcontent.visibility = View.VISIBLE
|
binding.webcontent.visibility = View.VISIBLE
|
||||||
|
|
||||||
val colorSurfaceString =
|
val colorOnSurface = TypedValue()
|
||||||
String.format(
|
requireContext().theme.resolveAttribute(R.attr.colorOnSurface, colorOnSurface, true)
|
||||||
"#%06X",
|
|
||||||
WHITE_COLOR_HEX and (if (colorSurface != DATA_NULL_UNDEFINED) colorSurface else WHITE_COLOR_HEX),
|
|
||||||
)
|
|
||||||
|
|
||||||
val colorOnSurfaceString =
|
val colorSurface = TypedValue()
|
||||||
String.format(
|
requireContext().theme.resolveAttribute(R.attr.colorSurface, colorSurface, true)
|
||||||
"#%06X",
|
|
||||||
WHITE_COLOR_HEX and (if (colorOnSurface != DATA_NULL_UNDEFINED) colorOnSurface else 0),
|
|
||||||
)
|
|
||||||
|
|
||||||
binding.webcontent.settings.useWideViewPort = true
|
binding.webcontent.settings.useWideViewPort = true
|
||||||
binding.webcontent.settings.loadWithOverviewMode = true
|
binding.webcontent.settings.loadWithOverviewMode = true
|
||||||
binding.webcontent.settings.javaScriptEnabled = false
|
binding.webcontent.settings.javaScriptEnabled = false
|
||||||
|
|
||||||
handleImageLoading()
|
handleImageLoading()
|
||||||
try {
|
|
||||||
val gestureDetector =
|
|
||||||
GestureDetector(
|
|
||||||
activity,
|
|
||||||
object : GestureDetector.SimpleOnGestureListener() {
|
|
||||||
override fun onSingleTapUp(e: MotionEvent): Boolean = performClick()
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
binding.webcontent.setOnTouchListener { _, event ->
|
val gestureDetector =
|
||||||
gestureDetector.onTouchEvent(
|
GestureDetector(activity, object : GestureDetector.SimpleOnGestureListener() {
|
||||||
event,
|
override fun onSingleTapUp(e: MotionEvent): Boolean {
|
||||||
)
|
return performClick()
|
||||||
}
|
}
|
||||||
} catch (e: IllegalStateException) {
|
})
|
||||||
e.sendSilentlyWithAcraWithName("Gesture detector issue ?")
|
|
||||||
return
|
binding.webcontent.setOnTouchListener { _, event -> gestureDetector.onTouchEvent(event) }
|
||||||
}
|
|
||||||
|
|
||||||
binding.webcontent.settings.layoutAlgorithm =
|
binding.webcontent.settings.layoutAlgorithm =
|
||||||
WebSettings.LayoutAlgorithm.TEXT_AUTOSIZING
|
WebSettings.LayoutAlgorithm.TEXT_AUTOSIZING
|
||||||
|
|
||||||
var baseUrl: String? = null
|
var baseUrl: String? = null
|
||||||
|
|
||||||
try {
|
try {
|
||||||
val itemUrl = URL(url)
|
val itemUrl = URL(url)
|
||||||
baseUrl = itemUrl.protocol + "://" + itemUrl.host
|
baseUrl = itemUrl.protocol + "://" + itemUrl.host
|
||||||
} catch (e: MalformedURLException) {
|
} catch (e: MalformedURLException) {
|
||||||
e.sendSilentlyWithAcraWithName("htmlToWebview > $url")
|
e.sendSilentlyWithAcraWithName("htmlToWebview > item url")
|
||||||
}
|
}
|
||||||
|
|
||||||
val fontName: String =
|
val fontName = when (font) {
|
||||||
maybeIfContext {
|
getString(R.string.open_sans_font_id) -> "Open Sans"
|
||||||
when (font) {
|
getString(R.string.roboto_font_id) -> "Roboto"
|
||||||
it.getString(R.string.open_sans_font_id) -> "Open Sans"
|
getString(R.string.source_code_pro_font_id) -> "Source Code Pro"
|
||||||
it.getString(R.string.roboto_font_id) -> "Roboto"
|
else -> ""
|
||||||
it.getString(R.string.source_code_pro_font_id) -> "Source Code Pro"
|
}
|
||||||
else -> ""
|
|
||||||
}
|
|
||||||
}?.toString().orEmpty()
|
|
||||||
|
|
||||||
val fontLinkAndStyle =
|
val fontLinkAndStyle = if (font.isNotEmpty()) {
|
||||||
if (fontName.isNotEmpty()) {
|
"""<link href="https://fonts.googleapis.com/css?family=${
|
||||||
"""<link href="https://fonts.googleapis.com/css?family=${
|
fontName.replace(
|
||||||
fontName.replace(
|
" ",
|
||||||
" ",
|
"+"
|
||||||
"+",
|
)
|
||||||
)
|
}" rel="stylesheet">
|
||||||
}" rel="stylesheet">
|
|
||||||
|<style>
|
|<style>
|
||||||
| * {
|
| * {
|
||||||
| font-family: '$fontName';
|
| font-family: '$fontName';
|
||||||
| }
|
| }
|
||||||
|</style>
|
|</style>
|
||||||
""".trimMargin()
|
""".trimMargin()
|
||||||
} else {
|
} else {
|
||||||
""
|
""
|
||||||
}
|
}
|
||||||
try {
|
|
||||||
binding.webcontent.loadDataWithBaseURL(
|
binding.webcontent.loadDataWithBaseURL(
|
||||||
baseUrl,
|
baseUrl,
|
||||||
"""<html>
|
"""<html>
|
||||||
|<head>
|
|<head>
|
||||||
| <meta name="viewport" content="width=device-width, initial-scale=1">
|
| <meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
| <style>
|
| <style>
|
||||||
@ -472,14 +435,14 @@ class ArticleFragment :
|
|||||||
| }
|
| }
|
||||||
| a {
|
| a {
|
||||||
| color: ${
|
| color: ${
|
||||||
String.format(
|
String.format(
|
||||||
"#%06X",
|
"#%06X",
|
||||||
WHITE_COLOR_HEX and (maybeIfContext { it.resources.getColor(R.color.colorAccent) } as Int),
|
0xFFFFFF and resources.getColor(R.color.colorAccent)
|
||||||
)
|
)
|
||||||
} !important;
|
} !important;
|
||||||
| }
|
| }
|
||||||
| *:not(a) {
|
| *:not(a) {
|
||||||
| color: $colorOnSurfaceString;
|
| color: ${String.format("#%06X", 0xFFFFFF and colorOnSurface.data)};
|
||||||
| }
|
| }
|
||||||
| * {
|
| * {
|
||||||
| font-size: ${fontSize}px;
|
| font-size: ${fontSize}px;
|
||||||
@ -487,11 +450,26 @@ class ArticleFragment :
|
|||||||
| word-break: break-word;
|
| word-break: break-word;
|
||||||
| overflow:hidden;
|
| overflow:hidden;
|
||||||
| line-height: 1.5em;
|
| line-height: 1.5em;
|
||||||
| background-color: $colorSurfaceString;
|
| background-color: ${
|
||||||
|
String.format(
|
||||||
|
"#%06X",
|
||||||
|
0xFFFFFF and colorSurface.data
|
||||||
|
)
|
||||||
|
};
|
||||||
| }
|
| }
|
||||||
| body, html {
|
| body, html {
|
||||||
| background-color: $colorSurfaceString !important;
|
| background-color: ${
|
||||||
| border-color: $colorSurfaceString !important;
|
String.format(
|
||||||
|
"#%06X",
|
||||||
|
0xFFFFFF and colorSurface.data
|
||||||
|
)
|
||||||
|
} !important;
|
||||||
|
| border-color: ${
|
||||||
|
String.format(
|
||||||
|
"#%06X",
|
||||||
|
0xFFFFFF and colorSurface.data
|
||||||
|
)
|
||||||
|
} !important;
|
||||||
| padding: 0 !important;
|
| padding: 0 !important;
|
||||||
| margin: 0 !important;
|
| margin: 0 !important;
|
||||||
| }
|
| }
|
||||||
@ -501,45 +479,46 @@ class ArticleFragment :
|
|||||||
| pre, code {
|
| pre, code {
|
||||||
| white-space: pre-wrap;
|
| white-space: pre-wrap;
|
||||||
| width:100%;
|
| width:100%;
|
||||||
| background-color: $colorSurfaceString;
|
| background-color: ${
|
||||||
|
String.format(
|
||||||
|
"#%06X",
|
||||||
|
0xFFFFFF and colorSurface.data
|
||||||
|
)
|
||||||
|
};
|
||||||
| }
|
| }
|
||||||
| </style>
|
| </style>
|
||||||
| $fontLinkAndStyle
|
| $fontLinkAndStyle
|
||||||
|</head>
|
|</head>
|
||||||
|<body>
|
|<body>
|
||||||
| $contentText
|
| $contentText
|
||||||
|</body>
|
|</body>""".trimMargin(),
|
||||||
""".trimMargin(),
|
"text/html",
|
||||||
"text/html",
|
"utf-8",
|
||||||
"utf-8",
|
null
|
||||||
null,
|
)
|
||||||
)
|
|
||||||
} catch (e: IllegalStateException) {
|
|
||||||
e.sendSilentlyWithAcraWithName("Context required is still null ?")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun volumeButtonScrollDown() {
|
fun scrollDown() {
|
||||||
val height = binding.nestedScrollView.measuredHeight
|
val height = binding.nestedScrollView.measuredHeight
|
||||||
binding.nestedScrollView.smoothScrollBy(0, height / 2)
|
binding.nestedScrollView.smoothScrollBy(0, height / 2)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun volumeButtonScrollUp() {
|
fun scrollUp() {
|
||||||
val height = binding.nestedScrollView.measuredHeight
|
val height = binding.nestedScrollView.measuredHeight
|
||||||
binding.nestedScrollView.smoothScrollBy(0, -height / 2)
|
binding.nestedScrollView.smoothScrollBy(0, -height / 2)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun openInBrowserAfterFailing() {
|
private fun openInBrowserAfterFailing() {
|
||||||
binding.progressBar.visibility = View.GONE
|
binding.progressBar.visibility = View.GONE
|
||||||
maybeIfContext {
|
requireActivity().openInBrowserAsNewTask(this@ArticleFragment.item)
|
||||||
it.openItemUrlInBrowserAsNewTask(this@ArticleFragment.item)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val ARG_ITEMS = "items"
|
private const val ARG_ITEMS = "items"
|
||||||
|
|
||||||
fun newInstance(item: SelfossModel.Item): ArticleFragment {
|
fun newInstance(
|
||||||
|
item: SelfossModel.Item
|
||||||
|
): ArticleFragment {
|
||||||
val fragment = ArticleFragment()
|
val fragment = ArticleFragment()
|
||||||
val args = Bundle()
|
val args = Bundle()
|
||||||
args.putParcelable(ARG_ITEMS, item.toParcelable())
|
args.putParcelable(ARG_ITEMS, item.toParcelable())
|
||||||
@ -549,12 +528,10 @@ class ArticleFragment :
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun performClick(): Boolean {
|
fun performClick(): Boolean {
|
||||||
if (allImages != null &&
|
if (binding.webcontent.hitTestResult.type == WebView.HitTestResult.IMAGE_TYPE ||
|
||||||
(
|
binding.webcontent.hitTestResult.type == WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE
|
||||||
binding.webcontent.hitTestResult.type == WebView.HitTestResult.IMAGE_TYPE ||
|
|
||||||
binding.webcontent.hitTestResult.type == WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE
|
|
||||||
)
|
|
||||||
) {
|
) {
|
||||||
|
|
||||||
val position: Int = allImages.indexOf(binding.webcontent.hitTestResult.extra)
|
val position: Int = allImages.indexOf(binding.webcontent.hitTestResult.extra)
|
||||||
|
|
||||||
val intent = Intent(activity, ImageActivity::class.java)
|
val intent = Intent(activity, ImageActivity::class.java)
|
||||||
@ -565,4 +542,6 @@ class ArticleFragment :
|
|||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
package bou.amine.apps.readerforselfossv2.android.fragments
|
package bou.amine.apps.readerforselfossv2.android.fragments
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
import android.graphics.drawable.Drawable
|
import android.graphics.drawable.Drawable
|
||||||
import android.graphics.drawable.GradientDrawable
|
import android.graphics.drawable.GradientDrawable
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.text.TextUtils
|
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.View.GONE
|
import android.view.View.GONE
|
||||||
@ -14,16 +14,15 @@ import android.view.ViewGroup
|
|||||||
import bou.amine.apps.readerforselfossv2.android.HomeActivity
|
import bou.amine.apps.readerforselfossv2.android.HomeActivity
|
||||||
import bou.amine.apps.readerforselfossv2.android.R
|
import bou.amine.apps.readerforselfossv2.android.R
|
||||||
import bou.amine.apps.readerforselfossv2.android.databinding.FilterFragmentBinding
|
import bou.amine.apps.readerforselfossv2.android.databinding.FilterFragmentBinding
|
||||||
import bou.amine.apps.readerforselfossv2.android.utils.acra.sendSilentlyWithAcraWithName
|
import bou.amine.apps.readerforselfossv2.android.sendSilentlyWithAcraWithName
|
||||||
import bou.amine.apps.readerforselfossv2.android.utils.glide.imageIntoViewTarget
|
|
||||||
import bou.amine.apps.readerforselfossv2.android.utils.maybeIfContext
|
|
||||||
import bou.amine.apps.readerforselfossv2.repository.Repository
|
import bou.amine.apps.readerforselfossv2.repository.Repository
|
||||||
import bou.amine.apps.readerforselfossv2.service.AppSettingsService
|
|
||||||
import bou.amine.apps.readerforselfossv2.utils.getColorHexCode
|
|
||||||
import bou.amine.apps.readerforselfossv2.utils.getHtmlDecoded
|
import bou.amine.apps.readerforselfossv2.utils.getHtmlDecoded
|
||||||
import bou.amine.apps.readerforselfossv2.utils.getIcon
|
import bou.amine.apps.readerforselfossv2.utils.getIcon
|
||||||
import com.bumptech.glide.request.target.ViewTarget
|
import com.bumptech.glide.Glide
|
||||||
import com.bumptech.glide.request.transition.Transition
|
import com.bumptech.glide.load.DataSource
|
||||||
|
import com.bumptech.glide.load.engine.GlideException
|
||||||
|
import com.bumptech.glide.request.RequestListener
|
||||||
|
import com.bumptech.glide.request.target.Target
|
||||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
||||||
import com.google.android.material.chip.Chip
|
import com.google.android.material.chip.Chip
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
@ -34,41 +33,41 @@ import org.kodein.di.DIAware
|
|||||||
import org.kodein.di.android.x.closestDI
|
import org.kodein.di.android.x.closestDI
|
||||||
import org.kodein.di.instance
|
import org.kodein.di.instance
|
||||||
|
|
||||||
private const val DRAWABLE_SIZE = 30
|
|
||||||
|
|
||||||
class FilterSheetFragment :
|
class FilterSheetFragment : BottomSheetDialogFragment(), DIAware {
|
||||||
BottomSheetDialogFragment(),
|
|
||||||
DIAware {
|
|
||||||
private lateinit var binding: FilterFragmentBinding
|
private lateinit var binding: FilterFragmentBinding
|
||||||
override val di: DI by closestDI()
|
override val di: DI by closestDI()
|
||||||
private val repository: Repository by instance()
|
private val repository: Repository by instance()
|
||||||
private val appSettingsService: AppSettingsService by instance()
|
|
||||||
|
|
||||||
private var selectedChip: Chip? = null
|
private var selectedChip: Chip? = null
|
||||||
|
|
||||||
override fun onCreateView(
|
override fun onCreateView(
|
||||||
inflater: LayoutInflater,
|
inflater: LayoutInflater,
|
||||||
container: ViewGroup?,
|
container: ViewGroup?,
|
||||||
savedInstanceState: Bundle?,
|
savedInstanceState: Bundle?
|
||||||
): View {
|
): View {
|
||||||
binding =
|
binding =
|
||||||
FilterFragmentBinding.inflate(
|
FilterFragmentBinding.inflate(
|
||||||
inflater,
|
inflater,
|
||||||
container,
|
container,
|
||||||
false,
|
false
|
||||||
)
|
)
|
||||||
|
|
||||||
try {
|
val context: Context? = context
|
||||||
|
|
||||||
|
|
||||||
|
if (context == null) {
|
||||||
|
dismiss()
|
||||||
|
Exception("FilterSheetFragment context is null").sendSilentlyWithAcraWithName("FilterSheetFragment > onCreateView")
|
||||||
|
} else {
|
||||||
CoroutineScope(Dispatchers.Main).launch {
|
CoroutineScope(Dispatchers.Main).launch {
|
||||||
handleTagChips()
|
handleTagChips(context)
|
||||||
handleSourceChips()
|
handleSourceChips(context)
|
||||||
|
|
||||||
binding.progressBar2.visibility = GONE
|
binding.progressBar2.visibility = GONE
|
||||||
binding.filterView.visibility = VISIBLE
|
binding.filterView.visibility = VISIBLE
|
||||||
}
|
}
|
||||||
} catch (e: IllegalStateException) {
|
|
||||||
dismiss()
|
|
||||||
e.sendSilentlyWithAcraWithName("FilterSheetFragment > onCreateView")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.floatingActionButton2.setOnClickListener {
|
binding.floatingActionButton2.setOnClickListener {
|
||||||
@ -79,39 +78,41 @@ class FilterSheetFragment :
|
|||||||
return binding.root
|
return binding.root
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun handleSourceChips() {
|
private suspend fun handleSourceChips(
|
||||||
|
context: Context
|
||||||
|
) {
|
||||||
val sourceGroup = binding.sourcesGroup
|
val sourceGroup = binding.sourcesGroup
|
||||||
|
|
||||||
repository.getSourcesDetailsOrStats().forEachIndexed { _, source ->
|
repository.getSources().forEach { source ->
|
||||||
val c: Chip? =
|
val c = Chip(context)
|
||||||
maybeIfContext {
|
|
||||||
Chip(it)
|
|
||||||
} as Chip?
|
|
||||||
|
|
||||||
if (c == null) {
|
Glide.with(context)
|
||||||
return
|
.load(source.getIcon(repository.baseUrl))
|
||||||
}
|
.listener(object : RequestListener<Drawable?> {
|
||||||
|
override fun onLoadFailed(
|
||||||
|
e: GlideException?,
|
||||||
|
model: Any?,
|
||||||
|
target: Target<Drawable?>?,
|
||||||
|
isFirstResource: Boolean
|
||||||
|
): Boolean {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
c.ellipsize = TextUtils.TruncateAt.END
|
override fun onResourceReady(
|
||||||
|
resource: Drawable?,
|
||||||
maybeIfContext {
|
model: Any?,
|
||||||
it.imageIntoViewTarget(
|
target: Target<Drawable?>?,
|
||||||
source.getIcon(repository.baseUrl),
|
dataSource: DataSource?,
|
||||||
object : ViewTarget<Chip?, Drawable?>(c) {
|
isFirstResource: Boolean
|
||||||
override fun onResourceReady(
|
): Boolean {
|
||||||
resource: Drawable,
|
try {
|
||||||
transition: Transition<in Drawable?>?,
|
c.chipIcon = resource
|
||||||
) {
|
} catch (e: Exception) {
|
||||||
try {
|
e.sendSilentlyWithAcraWithName("sources > onResourceReady")
|
||||||
c.chipIcon = resource
|
|
||||||
} catch (e: Exception) {
|
|
||||||
e.sendSilentlyWithAcraWithName("sources > onResourceReady")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
return false
|
||||||
appSettingsService,
|
}
|
||||||
)
|
}).preload()
|
||||||
}
|
|
||||||
|
|
||||||
c.text = source.title.getHtmlDecoded()
|
c.text = source.title.getHtmlDecoded()
|
||||||
|
|
||||||
@ -132,14 +133,15 @@ class FilterSheetFragment :
|
|||||||
repository.setTagFilter(null)
|
repository.setTagFilter(null)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (repository.sourceFilter.value?.equals(source) == true) {
|
if (repository.sourceFilter.value?.equals(source) == true) {
|
||||||
c.isCloseIconVisible = true
|
c.isCloseIconVisible = true
|
||||||
selectedChip = c
|
selectedChip = c
|
||||||
}
|
}
|
||||||
|
|
||||||
c.isEnabled = source.error.isNullOrBlank()
|
c.isEnabled = source.error.isBlank()
|
||||||
|
|
||||||
if (!source.error.isNullOrBlank() && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
if (source.error.isNotBlank() && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
c.tooltipText = source.error
|
c.tooltipText = source.error
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -147,38 +149,32 @@ class FilterSheetFragment :
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun handleTagChips() {
|
private suspend fun handleTagChips(
|
||||||
|
context: Context,
|
||||||
|
) {
|
||||||
val tagGroup = binding.tagsGroup
|
val tagGroup = binding.tagsGroup
|
||||||
|
|
||||||
val tags = repository.getTags()
|
val tags = repository.getTags()
|
||||||
|
|
||||||
tags.forEachIndexed { _, tag ->
|
tags.forEach { tag ->
|
||||||
val c: Chip? = maybeIfContext { Chip(it) } as Chip?
|
val c = Chip(context)
|
||||||
if (c == null) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
c.ellipsize = TextUtils.TruncateAt.END
|
|
||||||
c.text = tag.tag
|
c.text = tag.tag
|
||||||
|
|
||||||
if (tag.color.isNotEmpty()) {
|
try {
|
||||||
try {
|
val gd = GradientDrawable()
|
||||||
val gd = GradientDrawable()
|
val gdColor = try {
|
||||||
val gdColor =
|
Color.parseColor(tag.color)
|
||||||
try {
|
} catch (e: IllegalArgumentException) {
|
||||||
Color.parseColor(tag.getColorHexCode())
|
e.sendSilentlyWithAcraWithName("color issue " + tag.color)
|
||||||
} catch (e: IllegalArgumentException) {
|
resources.getColor(R.color.colorPrimary)
|
||||||
e.sendSilentlyWithAcraWithName("color issue " + tag.color + " / " + tag.getColorHexCode())
|
|
||||||
resources.getColor(R.color.colorPrimary)
|
|
||||||
}
|
|
||||||
gd.setColor(gdColor)
|
|
||||||
gd.shape = GradientDrawable.RECTANGLE
|
|
||||||
gd.setSize(DRAWABLE_SIZE, DRAWABLE_SIZE)
|
|
||||||
gd.cornerRadius = DRAWABLE_SIZE.toFloat()
|
|
||||||
c.chipIcon = gd
|
|
||||||
} catch (e: Exception) {
|
|
||||||
e.sendSilentlyWithAcraWithName("tags > GradientDrawable")
|
|
||||||
}
|
}
|
||||||
|
gd.setColor(gdColor)
|
||||||
|
gd.shape = GradientDrawable.RECTANGLE
|
||||||
|
gd.setSize(30, 30)
|
||||||
|
gd.cornerRadius = 30F
|
||||||
|
c.chipIcon = gd
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e.sendSilentlyWithAcraWithName("tags > GradientDrawable")
|
||||||
}
|
}
|
||||||
|
|
||||||
c.setOnCloseIconClickListener {
|
c.setOnCloseIconClickListener {
|
||||||
@ -210,4 +206,6 @@ class FilterSheetFragment :
|
|||||||
companion object {
|
companion object {
|
||||||
const val TAG = "FilterModalBottomSheet"
|
const val TAG = "FilterModalBottomSheet"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
@ -6,21 +6,16 @@ import android.view.View
|
|||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import bou.amine.apps.readerforselfossv2.android.databinding.FragmentImageBinding
|
import bou.amine.apps.readerforselfossv2.android.databinding.FragmentImageBinding
|
||||||
import bou.amine.apps.readerforselfossv2.android.utils.glide.bitmapWithCache
|
import com.bumptech.glide.Glide
|
||||||
import bou.amine.apps.readerforselfossv2.service.AppSettingsService
|
import com.bumptech.glide.load.engine.DiskCacheStrategy
|
||||||
import org.kodein.di.DI
|
import com.bumptech.glide.request.RequestOptions
|
||||||
import org.kodein.di.DIAware
|
|
||||||
import org.kodein.di.android.x.closestDI
|
|
||||||
import org.kodein.di.instance
|
|
||||||
|
|
||||||
class ImageFragment :
|
class ImageFragment : Fragment() {
|
||||||
Fragment(),
|
|
||||||
DIAware {
|
private lateinit var imageUrl : String
|
||||||
override val di: DI by closestDI()
|
private val glideOptions = RequestOptions.diskCacheStrategyOf(DiskCacheStrategy.ALL)
|
||||||
private val appSettingsService: AppSettingsService by instance()
|
|
||||||
private lateinit var imageUrl: String
|
|
||||||
private var _binding: FragmentImageBinding? = null
|
private var _binding: FragmentImageBinding? = null
|
||||||
val binding get() = _binding
|
private val binding get() = _binding
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
@ -28,16 +23,16 @@ class ImageFragment :
|
|||||||
imageUrl = requireArguments().getString("imageUrl")!!
|
imageUrl = requireArguments().getString("imageUrl")!!
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateView(
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||||
inflater: LayoutInflater,
|
|
||||||
container: ViewGroup?,
|
|
||||||
savedInstanceState: Bundle?,
|
|
||||||
): View? {
|
|
||||||
_binding = FragmentImageBinding.inflate(inflater, container, false)
|
_binding = FragmentImageBinding.inflate(inflater, container, false)
|
||||||
val view = binding?.root
|
val view = binding?.root
|
||||||
|
|
||||||
binding!!.photoView.visibility = View.VISIBLE
|
binding!!.photoView.visibility = View.VISIBLE
|
||||||
requireActivity().bitmapWithCache(imageUrl, binding!!.photoView, appSettingsService)
|
Glide.with(requireActivity())
|
||||||
|
.asBitmap()
|
||||||
|
.apply(glideOptions)
|
||||||
|
.load(imageUrl)
|
||||||
|
.into(binding!!.photoView)
|
||||||
|
|
||||||
return view
|
return view
|
||||||
}
|
}
|
||||||
@ -50,7 +45,9 @@ class ImageFragment :
|
|||||||
companion object {
|
companion object {
|
||||||
private const val ARG_IMAGE = "imageUrl"
|
private const val ARG_IMAGE = "imageUrl"
|
||||||
|
|
||||||
fun newInstance(imageUrl: String): ImageFragment {
|
fun newInstance(
|
||||||
|
imageUrl : String
|
||||||
|
): ImageFragment {
|
||||||
val fragment = ImageFragment()
|
val fragment = ImageFragment()
|
||||||
val args = Bundle()
|
val args = Bundle()
|
||||||
args.putString(ARG_IMAGE, imageUrl)
|
args.putString(ARG_IMAGE, imageUrl)
|
||||||
|
@ -2,25 +2,28 @@ package bou.amine.apps.readerforselfossv2.android.model
|
|||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.webkit.URLUtil
|
import android.webkit.URLUtil
|
||||||
import bou.amine.apps.readerforselfossv2.android.utils.acra.sendSilentlyWithAcraWithName
|
import bou.amine.apps.readerforselfossv2.android.sendSilentlyWithAcraWithName
|
||||||
import bou.amine.apps.readerforselfossv2.android.utils.glide.preloadImage
|
|
||||||
import bou.amine.apps.readerforselfossv2.model.SelfossModel
|
import bou.amine.apps.readerforselfossv2.model.SelfossModel
|
||||||
import bou.amine.apps.readerforselfossv2.service.AppSettingsService
|
|
||||||
import bou.amine.apps.readerforselfossv2.utils.getImages
|
import bou.amine.apps.readerforselfossv2.utils.getImages
|
||||||
|
import com.bumptech.glide.Glide
|
||||||
|
import com.bumptech.glide.load.engine.DiskCacheStrategy
|
||||||
|
import com.bumptech.glide.request.RequestOptions
|
||||||
|
|
||||||
fun SelfossModel.Item.preloadImages(
|
fun SelfossModel.Item.preloadImages(context: Context) : Boolean {
|
||||||
context: Context,
|
|
||||||
appSettingsService: AppSettingsService,
|
|
||||||
): Boolean {
|
|
||||||
val imageUrls = this.getImages()
|
val imageUrls = this.getImages()
|
||||||
|
|
||||||
|
val glideOptions = RequestOptions.diskCacheStrategyOf(DiskCacheStrategy.ALL).timeout(10000)
|
||||||
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
for (url in imageUrls) {
|
for (url in imageUrls) {
|
||||||
if (URLUtil.isValidUrl(url)) {
|
if ( URLUtil.isValidUrl(url)) {
|
||||||
context.preloadImage(url, appSettingsService)
|
Glide.with(context).asBitmap()
|
||||||
|
.apply(glideOptions)
|
||||||
|
.load(url).submit()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e: Error) {
|
} catch (e : Error) {
|
||||||
e.sendSilentlyWithAcraWithName("preloadImages")
|
e.sendSilentlyWithAcraWithName("preloadImages")
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@ import android.os.Parcel
|
|||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import bou.amine.apps.readerforselfossv2.model.SelfossModel
|
import bou.amine.apps.readerforselfossv2.model.SelfossModel
|
||||||
|
|
||||||
fun SelfossModel.Item.toParcelable(): ParecelableItem =
|
fun SelfossModel.Item.toParcelable() : ParecelableItem =
|
||||||
ParecelableItem(
|
ParecelableItem(
|
||||||
this.id,
|
this.id,
|
||||||
this.datetime,
|
this.datetime,
|
||||||
@ -17,10 +17,9 @@ fun SelfossModel.Item.toParcelable(): ParecelableItem =
|
|||||||
this.link,
|
this.link,
|
||||||
this.sourcetitle,
|
this.sourcetitle,
|
||||||
this.tags.joinToString(","),
|
this.tags.joinToString(","),
|
||||||
this.author,
|
this.author
|
||||||
)
|
)
|
||||||
|
fun ParecelableItem.toModel() : SelfossModel.Item =
|
||||||
fun ParecelableItem.toModel(): SelfossModel.Item =
|
|
||||||
SelfossModel.Item(
|
SelfossModel.Item(
|
||||||
this.id,
|
this.id,
|
||||||
this.datetime,
|
this.datetime,
|
||||||
@ -33,9 +32,8 @@ fun ParecelableItem.toModel(): SelfossModel.Item =
|
|||||||
this.link,
|
this.link,
|
||||||
this.sourcetitle,
|
this.sourcetitle,
|
||||||
this.tags.split(","),
|
this.tags.split(","),
|
||||||
this.author,
|
this.author
|
||||||
)
|
)
|
||||||
|
|
||||||
data class ParecelableItem(
|
data class ParecelableItem(
|
||||||
val id: Int,
|
val id: Int,
|
||||||
val datetime: String,
|
val datetime: String,
|
||||||
@ -48,16 +46,15 @@ data class ParecelableItem(
|
|||||||
val link: String,
|
val link: String,
|
||||||
val sourcetitle: String,
|
val sourcetitle: String,
|
||||||
val tags: String,
|
val tags: String,
|
||||||
val author: String?,
|
val author: String?
|
||||||
) : Parcelable {
|
) : Parcelable {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@JvmField
|
@JvmField
|
||||||
val CREATOR: Parcelable.Creator<ParecelableItem> =
|
val CREATOR: Parcelable.Creator<ParecelableItem> = object : Parcelable.Creator<ParecelableItem> {
|
||||||
object : Parcelable.Creator<ParecelableItem> {
|
override fun createFromParcel(source: Parcel): ParecelableItem = ParecelableItem(source)
|
||||||
override fun createFromParcel(source: Parcel): ParecelableItem = ParecelableItem(source)
|
override fun newArray(size: Int): Array<ParecelableItem?> = arrayOfNulls(size)
|
||||||
|
}
|
||||||
override fun newArray(size: Int): Array<ParecelableItem?> = arrayOfNulls(size)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(source: Parcel) : this(
|
constructor(source: Parcel) : this(
|
||||||
@ -72,15 +69,12 @@ data class ParecelableItem(
|
|||||||
link = source.readString().orEmpty(),
|
link = source.readString().orEmpty(),
|
||||||
sourcetitle = source.readString().orEmpty(),
|
sourcetitle = source.readString().orEmpty(),
|
||||||
tags = source.readString().orEmpty(),
|
tags = source.readString().orEmpty(),
|
||||||
author = source.readString().orEmpty(),
|
author = source.readString().orEmpty()
|
||||||
)
|
)
|
||||||
|
|
||||||
override fun describeContents() = 0
|
override fun describeContents() = 0
|
||||||
|
|
||||||
override fun writeToParcel(
|
override fun writeToParcel(dest: Parcel, flags: Int) {
|
||||||
dest: Parcel,
|
|
||||||
flags: Int,
|
|
||||||
) {
|
|
||||||
dest.writeInt(id)
|
dest.writeInt(id)
|
||||||
dest.writeString(datetime)
|
dest.writeString(datetime)
|
||||||
dest.writeString(title)
|
dest.writeString(title)
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
package bou.amine.apps.readerforselfossv2.android.settings
|
package bou.amine.apps.readerforselfossv2.android.settings
|
||||||
|
|
||||||
|
import android.content.Intent
|
||||||
|
import android.net.Uri
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.text.Editable
|
import android.text.Editable
|
||||||
import android.text.InputFilter
|
import android.text.InputFilter
|
||||||
@ -14,26 +16,16 @@ import androidx.preference.Preference
|
|||||||
import androidx.preference.PreferenceFragmentCompat
|
import androidx.preference.PreferenceFragmentCompat
|
||||||
import bou.amine.apps.readerforselfossv2.android.R
|
import bou.amine.apps.readerforselfossv2.android.R
|
||||||
import bou.amine.apps.readerforselfossv2.android.databinding.ActivitySettingsBinding
|
import bou.amine.apps.readerforselfossv2.android.databinding.ActivitySettingsBinding
|
||||||
import bou.amine.apps.readerforselfossv2.android.utils.acra.sendSilentlyWithAcraWithName
|
import bou.amine.apps.readerforselfossv2.android.sendSilentlyWithAcraWithName
|
||||||
import bou.amine.apps.readerforselfossv2.android.utils.openUrlInBrowser
|
|
||||||
import bou.amine.apps.readerforselfossv2.service.AppSettingsService
|
import bou.amine.apps.readerforselfossv2.service.AppSettingsService
|
||||||
import bou.amine.apps.readerforselfossv2.service.AppSettingsService.Companion.API_ITEMS_NUMBER
|
|
||||||
import bou.amine.apps.readerforselfossv2.service.AppSettingsService.Companion.CURRENT_THEME
|
|
||||||
import bou.amine.apps.readerforselfossv2.service.AppSettingsService.Companion.READER_FONT_SIZE
|
|
||||||
import com.mikepenz.aboutlibraries.LibsBuilder
|
import com.mikepenz.aboutlibraries.LibsBuilder
|
||||||
import org.kodein.di.DIAware
|
import org.kodein.di.DIAware
|
||||||
import org.kodein.di.android.closestDI
|
import org.kodein.di.android.closestDI
|
||||||
|
|
||||||
private const val TITLE_TAG = "settingsActivityTitle"
|
private const val TITLE_TAG = "settingsActivityTitle"
|
||||||
|
|
||||||
const val MAX_ITEMS_NUMBER = 200
|
class SettingsActivity : AppCompatActivity(),
|
||||||
|
PreferenceFragmentCompat.OnPreferenceStartFragmentCallback, DIAware {
|
||||||
private const val MIN_ITEMS_NUMBER = 1
|
|
||||||
|
|
||||||
class SettingsActivity :
|
|
||||||
AppCompatActivity(),
|
|
||||||
PreferenceFragmentCompat.OnPreferenceStartFragmentCallback,
|
|
||||||
DIAware {
|
|
||||||
override val di by closestDI()
|
override val di by closestDI()
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
@ -43,9 +35,9 @@ class SettingsActivity :
|
|||||||
setContentView(binding.root)
|
setContentView(binding.root)
|
||||||
if (savedInstanceState == null) {
|
if (savedInstanceState == null) {
|
||||||
supportFragmentManager
|
supportFragmentManager
|
||||||
.beginTransaction()
|
.beginTransaction()
|
||||||
.replace(R.id.settings, MainPreferenceFragment())
|
.replace(R.id.settings, MainPreferenceFragment())
|
||||||
.commit()
|
.commit()
|
||||||
} else {
|
} else {
|
||||||
title = savedInstanceState.getCharSequence(TITLE_TAG)
|
title = savedInstanceState.getCharSequence(TITLE_TAG)
|
||||||
}
|
}
|
||||||
@ -68,142 +60,106 @@ class SettingsActivity :
|
|||||||
outState.putCharSequence(TITLE_TAG, title)
|
outState.putCharSequence(TITLE_TAG, title)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onSupportNavigateUp(): Boolean =
|
override fun onSupportNavigateUp(): Boolean {
|
||||||
if (supportFragmentManager.popBackStackImmediate()) {
|
return if (supportFragmentManager.popBackStackImmediate()) {
|
||||||
supportActionBar?.title = getText(R.string.title_activity_settings)
|
supportActionBar?.title = getText(R.string.title_activity_settings)
|
||||||
false
|
false
|
||||||
} else {
|
} else {
|
||||||
super.onBackPressed()
|
super.onBackPressed()
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun onPreferenceStartFragment(
|
override fun onPreferenceStartFragment(
|
||||||
caller: PreferenceFragmentCompat,
|
caller: PreferenceFragmentCompat,
|
||||||
pref: Preference,
|
pref: Preference
|
||||||
): Boolean {
|
): Boolean {
|
||||||
// Instantiate the new Fragment
|
// Instantiate the new Fragment
|
||||||
val args = pref.extras
|
val args = pref.extras
|
||||||
val fragment =
|
val fragment = supportFragmentManager.fragmentFactory.instantiate(
|
||||||
supportFragmentManager.fragmentFactory
|
classLoader,
|
||||||
.instantiate(
|
pref.fragment
|
||||||
classLoader,
|
).apply {
|
||||||
pref.fragment.toString(),
|
arguments = args
|
||||||
).apply {
|
setTargetFragment(caller, 0)
|
||||||
arguments = args
|
}
|
||||||
setTargetFragment(caller, 0)
|
|
||||||
}
|
|
||||||
// Replace the existing Fragment with the new Fragment
|
// Replace the existing Fragment with the new Fragment
|
||||||
supportFragmentManager
|
supportFragmentManager.beginTransaction()
|
||||||
.beginTransaction()
|
.replace(R.id.settings, fragment)
|
||||||
.replace(R.id.settings, fragment)
|
.addToBackStack(null)
|
||||||
.addToBackStack(null)
|
.commit()
|
||||||
.commit()
|
|
||||||
title = pref.title
|
title = pref.title
|
||||||
supportActionBar?.title = title
|
supportActionBar?.title = title
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
class MainPreferenceFragment : PreferenceFragmentCompat() {
|
class MainPreferenceFragment : PreferenceFragmentCompat() {
|
||||||
override fun onCreatePreferences(
|
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||||
savedInstanceState: Bundle?,
|
|
||||||
rootKey: String?,
|
|
||||||
) {
|
|
||||||
setPreferencesFromResource(R.xml.pref_main, rootKey)
|
setPreferencesFromResource(R.xml.pref_main, rootKey)
|
||||||
|
|
||||||
preferenceManager.findPreference<Preference>(CURRENT_THEME)?.onPreferenceChangeListener =
|
preferenceManager.findPreference<Preference>("currentMode")?.onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, newValue ->
|
||||||
Preference.OnPreferenceChangeListener { _, newValue ->
|
AppCompatDelegate.setDefaultNightMode(newValue.toString().toInt()) // ListPreference Only takes string-arrays ¯\_(ツ)_/¯
|
||||||
AppCompatDelegate.setDefaultNightMode(
|
true
|
||||||
newValue.toString().toInt(),
|
}
|
||||||
) // ListPreference Only takes string-arrays ¯\_(ツ)_/¯
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
preferenceManager.findPreference<Preference>("action_about")?.onPreferenceClickListener =
|
preferenceManager.findPreference<Preference>("action_about")?.onPreferenceClickListener = Preference.OnPreferenceClickListener { _ ->
|
||||||
Preference.OnPreferenceClickListener { _ ->
|
context?.let {
|
||||||
context?.let {
|
LibsBuilder()
|
||||||
LibsBuilder()
|
.withAboutIconShown(true)
|
||||||
.withAboutIconShown(true)
|
.withAboutVersionShown(true)
|
||||||
.withAboutVersionShown(true)
|
.start(it)
|
||||||
.start(it)
|
|
||||||
}
|
|
||||||
true
|
|
||||||
}
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class GeneralPreferenceFragment : PreferenceFragmentCompat() {
|
class GeneralPreferenceFragment : PreferenceFragmentCompat() {
|
||||||
override fun onCreatePreferences(
|
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||||
savedInstanceState: Bundle?,
|
|
||||||
rootKey: String?,
|
|
||||||
) {
|
|
||||||
setPreferencesFromResource(R.xml.pref_general, rootKey)
|
setPreferencesFromResource(R.xml.pref_general, rootKey)
|
||||||
|
|
||||||
val editTextPreference =
|
val editTextPreference = preferenceManager.findPreference<EditTextPreference>("prefer_api_items_number")
|
||||||
preferenceManager.findPreference<EditTextPreference>(API_ITEMS_NUMBER)
|
|
||||||
editTextPreference?.setOnBindEditTextListener { editText ->
|
editTextPreference?.setOnBindEditTextListener { editText ->
|
||||||
editText.inputType = InputType.TYPE_CLASS_NUMBER
|
editText.inputType = InputType.TYPE_CLASS_NUMBER
|
||||||
editText.filters =
|
editText.filters = arrayOf(
|
||||||
arrayOf(
|
|
||||||
InputFilter { source, _, _, dest, _, _ ->
|
InputFilter { source, _, _, dest, _, _ ->
|
||||||
try {
|
try {
|
||||||
val input: Int = (dest.toString() + source.toString()).toInt()
|
val input: Int = (dest.toString() + source.toString()).toInt()
|
||||||
if (input in MIN_ITEMS_NUMBER..MAX_ITEMS_NUMBER) return@InputFilter null
|
if (input in 1..200) return@InputFilter null
|
||||||
} catch (nfe: NumberFormatException) {
|
} catch (nfe: NumberFormatException) {
|
||||||
Toast
|
nfe.sendSilentlyWithAcraWithName("GeneralPreferenceFragment")
|
||||||
.makeText(
|
Toast.makeText(activity, R.string.items_number_should_be_number, Toast.LENGTH_LONG).show()
|
||||||
activity,
|
|
||||||
R.string.items_number_should_be_number,
|
|
||||||
Toast.LENGTH_LONG,
|
|
||||||
).show()
|
|
||||||
}
|
}
|
||||||
""
|
""
|
||||||
},
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ArticleViewerPreferenceFragment : PreferenceFragmentCompat() {
|
class ArticleViewerPreferenceFragment : PreferenceFragmentCompat() {
|
||||||
override fun onCreatePreferences(
|
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||||
savedInstanceState: Bundle?,
|
|
||||||
rootKey: String?,
|
|
||||||
) {
|
|
||||||
setPreferencesFromResource(R.xml.pref_viewer, rootKey)
|
setPreferencesFromResource(R.xml.pref_viewer, rootKey)
|
||||||
|
|
||||||
val fontSize = preferenceManager.findPreference<EditTextPreference>(READER_FONT_SIZE)
|
val fontSize = preferenceManager.findPreference<EditTextPreference>("reader_font_size")
|
||||||
fontSize?.setOnBindEditTextListener { editText ->
|
fontSize?.setOnBindEditTextListener { editText ->
|
||||||
editText.inputType = InputType.TYPE_CLASS_NUMBER
|
editText.inputType = InputType.TYPE_CLASS_NUMBER
|
||||||
editText.addTextChangedListener {
|
editText.addTextChangedListener { object : TextWatcher {
|
||||||
object : TextWatcher {
|
override fun beforeTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {
|
||||||
override fun beforeTextChanged(
|
// We do nothing
|
||||||
charSequence: CharSequence,
|
}
|
||||||
i: Int,
|
override fun onTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {
|
||||||
i1: Int,
|
// We do nothing
|
||||||
i2: Int,
|
}
|
||||||
) {
|
override fun afterTextChanged(editable: Editable) {
|
||||||
// We do nothing
|
try {
|
||||||
}
|
editText.textSize = editable.toString().toInt().toFloat()
|
||||||
|
} catch (e: NumberFormatException) {
|
||||||
override fun onTextChanged(
|
e.sendSilentlyWithAcraWithName("ArticleViewerPreferenceFragment > afterTextChanged")
|
||||||
charSequence: CharSequence,
|
|
||||||
i: Int,
|
|
||||||
i1: Int,
|
|
||||||
i2: Int,
|
|
||||||
) {
|
|
||||||
// We do nothing
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun afterTextChanged(editable: Editable) {
|
|
||||||
try {
|
|
||||||
editText.textSize = editable.toString().toInt().toFloat()
|
|
||||||
} catch (e: NumberFormatException) {
|
|
||||||
e.sendSilentlyWithAcraWithName("ArticleViewerPreferenceFragment > afterTextChanged")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
} }
|
||||||
editText.filters =
|
editText.filters = arrayOf(
|
||||||
arrayOf(
|
|
||||||
InputFilter { source, _, _, dest, _, _ ->
|
InputFilter { source, _, _, dest, _, _ ->
|
||||||
try {
|
try {
|
||||||
val input = (dest.toString() + source.toString()).toInt()
|
val input = (dest.toString() + source.toString()).toInt()
|
||||||
@ -212,57 +168,57 @@ class SettingsActivity :
|
|||||||
nfe.sendSilentlyWithAcraWithName("ArticleViewerPreferenceFragment > filters")
|
nfe.sendSilentlyWithAcraWithName("ArticleViewerPreferenceFragment > filters")
|
||||||
}
|
}
|
||||||
""
|
""
|
||||||
},
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class OfflinePreferenceFragment : PreferenceFragmentCompat() {
|
class OfflinePreferenceFragment : PreferenceFragmentCompat() {
|
||||||
override fun onCreatePreferences(
|
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||||
savedInstanceState: Bundle?,
|
|
||||||
rootKey: String?,
|
|
||||||
) {
|
|
||||||
setPreferencesFromResource(R.xml.pref_offline, rootKey)
|
setPreferencesFromResource(R.xml.pref_offline, rootKey)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class ThemePreferenceFragment : PreferenceFragmentCompat() {
|
||||||
|
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||||
|
setPreferencesFromResource(R.xml.pref_theme, rootKey)
|
||||||
|
|
||||||
|
preferenceManager.findPreference<Preference>("currentMode")?.onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, newValue ->
|
||||||
|
AppCompatDelegate.setDefaultNightMode(newValue.toString().toInt()) // ListPreference Only takes string-arrays ¯\_(ツ)_/¯
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class LinksPreferenceFragment : PreferenceFragmentCompat() {
|
class LinksPreferenceFragment : PreferenceFragmentCompat() {
|
||||||
private fun openUrl(url: String) {
|
private fun openUrl(uri: Uri?) {
|
||||||
context?.openUrlInBrowser(url)
|
val browserIntent = Intent(Intent.ACTION_VIEW, uri)
|
||||||
|
startActivity(browserIntent)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreatePreferences(
|
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||||
savedInstanceState: Bundle?,
|
|
||||||
rootKey: String?,
|
|
||||||
) {
|
|
||||||
setPreferencesFromResource(R.xml.pref_links, rootKey)
|
setPreferencesFromResource(R.xml.pref_links, rootKey)
|
||||||
|
|
||||||
preferenceManager.findPreference<Preference>("trackerLink")?.onPreferenceClickListener =
|
preferenceManager.findPreference<Preference>("trackerLink")?.onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
||||||
Preference.OnPreferenceClickListener {
|
openUrl(Uri.parse(AppSettingsService.trackerUrl))
|
||||||
openUrl(AppSettingsService.BUG_URL)
|
true
|
||||||
true
|
}
|
||||||
}
|
|
||||||
|
|
||||||
preferenceManager.findPreference<Preference>("sourceLink")?.onPreferenceClickListener =
|
preferenceManager.findPreference<Preference>("sourceLink")?.onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
||||||
Preference.OnPreferenceClickListener {
|
openUrl(Uri.parse(AppSettingsService.sourceUrl))
|
||||||
openUrl(AppSettingsService.SOURCE_URL)
|
false
|
||||||
false
|
}
|
||||||
}
|
|
||||||
|
|
||||||
preferenceManager.findPreference<Preference>("translation")?.onPreferenceClickListener =
|
preferenceManager.findPreference<Preference>("translation")?.onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
||||||
Preference.OnPreferenceClickListener {
|
openUrl(Uri.parse(AppSettingsService.translationUrl))
|
||||||
openUrl(AppSettingsService.TRANSLATION_URL)
|
false
|
||||||
false
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ExperimentalPreferenceFragment : PreferenceFragmentCompat() {
|
class ExperimentalPreferenceFragment : PreferenceFragmentCompat() {
|
||||||
override fun onCreatePreferences(
|
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||||
savedInstanceState: Bundle?,
|
|
||||||
rootKey: String?,
|
|
||||||
) {
|
|
||||||
setPreferencesFromResource(R.xml.pref_experimental, rootKey)
|
setPreferencesFromResource(R.xml.pref_experimental, rootKey)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,20 +0,0 @@
|
|||||||
package bou.amine.apps.readerforselfossv2.android.testing
|
|
||||||
|
|
||||||
import androidx.test.espresso.idling.CountingIdlingResource
|
|
||||||
|
|
||||||
object CountingIdlingResourceSingleton {
|
|
||||||
private const val RESOURCE = "GLOBAL"
|
|
||||||
|
|
||||||
@JvmField
|
|
||||||
val countingIdlingResource = CountingIdlingResource(RESOURCE)
|
|
||||||
|
|
||||||
fun increment() {
|
|
||||||
countingIdlingResource.increment()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun decrement() {
|
|
||||||
if (!countingIdlingResource.isIdleNow) {
|
|
||||||
countingIdlingResource.decrement()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,18 +0,0 @@
|
|||||||
package bou.amine.apps.readerforselfossv2.android.testing
|
|
||||||
|
|
||||||
import android.os.Build
|
|
||||||
|
|
||||||
class TestingHelper {
|
|
||||||
fun isUnitTest(): Boolean {
|
|
||||||
var device = Build.DEVICE
|
|
||||||
var product = Build.PRODUCT
|
|
||||||
if (device == null) {
|
|
||||||
device = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
if (product == null) {
|
|
||||||
product = ""
|
|
||||||
}
|
|
||||||
return device == "robolectric" && product == "robolectric"
|
|
||||||
}
|
|
||||||
}
|
|
@ -2,18 +2,10 @@ package bou.amine.apps.readerforselfossv2.android.utils
|
|||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.util.TypedValue
|
|
||||||
import androidx.annotation.AttrRes
|
|
||||||
import androidx.annotation.ColorInt
|
|
||||||
import androidx.fragment.app.Fragment
|
|
||||||
import bou.amine.apps.readerforselfossv2.android.R
|
import bou.amine.apps.readerforselfossv2.android.R
|
||||||
import bou.amine.apps.readerforselfossv2.android.utils.acra.sendSilentlyWithAcraWithName
|
|
||||||
import bou.amine.apps.readerforselfossv2.utils.toStringUriWithHttp
|
import bou.amine.apps.readerforselfossv2.utils.toStringUriWithHttp
|
||||||
|
|
||||||
fun Context.shareLink(
|
fun Context.shareLink(itemUrl: String, itemTitle: String) {
|
||||||
itemUrl: String,
|
|
||||||
itemTitle: String,
|
|
||||||
) {
|
|
||||||
val sendIntent = Intent()
|
val sendIntent = Intent()
|
||||||
sendIntent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
sendIntent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
||||||
sendIntent.action = Intent.ACTION_SEND
|
sendIntent.action = Intent.ACTION_SEND
|
||||||
@ -21,39 +13,9 @@ fun Context.shareLink(
|
|||||||
sendIntent.putExtra(Intent.EXTRA_SUBJECT, itemTitle)
|
sendIntent.putExtra(Intent.EXTRA_SUBJECT, itemTitle)
|
||||||
sendIntent.type = "text/plain"
|
sendIntent.type = "text/plain"
|
||||||
startActivity(
|
startActivity(
|
||||||
Intent
|
Intent.createChooser(
|
||||||
.createChooser(
|
sendIntent,
|
||||||
sendIntent,
|
getString(R.string.share)
|
||||||
getString(R.string.share),
|
).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||||
).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ColorInt
|
|
||||||
fun Fragment.getColorFromAttr(
|
|
||||||
@AttrRes attrColor: Int,
|
|
||||||
resolveRefs: Boolean = true,
|
|
||||||
): Int {
|
|
||||||
val typedValue = TypedValue()
|
|
||||||
maybeIfContextWithLog { this.requireContext().theme.resolveAttribute(attrColor, typedValue, resolveRefs) }
|
|
||||||
return typedValue.data
|
|
||||||
}
|
|
||||||
|
|
||||||
@Suppress("detekt:SwallowedException")
|
|
||||||
fun Fragment.maybeIfContext(fn: (Context) -> Any): Any? {
|
|
||||||
try {
|
|
||||||
return fn(this.requireContext())
|
|
||||||
} catch (e: Exception) {
|
|
||||||
// Do nothing
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun Fragment.maybeIfContextWithLog(fn: (Context) -> Any): Any? {
|
|
||||||
try {
|
|
||||||
return fn(this.requireContext())
|
|
||||||
} catch (e: Exception) {
|
|
||||||
e.sendSilentlyWithAcraWithName("Fragment context issue...")
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,63 +0,0 @@
|
|||||||
package bou.amine.apps.readerforselfossv2.android.utils
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.graphics.drawable.GradientDrawable
|
|
||||||
import android.util.AttributeSet
|
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.view.View
|
|
||||||
import android.widget.RelativeLayout
|
|
||||||
import android.widget.TextView
|
|
||||||
import bou.amine.apps.readerforselfossv2.android.R
|
|
||||||
import bou.amine.apps.readerforselfossv2.android.model.toTextDrawableString
|
|
||||||
import com.google.android.material.imageview.ShapeableImageView
|
|
||||||
import kotlin.math.abs
|
|
||||||
|
|
||||||
class CircleImageView
|
|
||||||
@JvmOverloads
|
|
||||||
constructor(
|
|
||||||
context: Context,
|
|
||||||
attrs: AttributeSet? = null,
|
|
||||||
defStyleAttr: Int = 0,
|
|
||||||
) : RelativeLayout(context, attrs, defStyleAttr) {
|
|
||||||
val view: View
|
|
||||||
val imageView: ShapeableImageView
|
|
||||||
val textView: TextView
|
|
||||||
|
|
||||||
private val colorScheme =
|
|
||||||
listOf(
|
|
||||||
-0x1a8c8d,
|
|
||||||
-0xf9d6e,
|
|
||||||
-0x459738,
|
|
||||||
-0x6a8a33,
|
|
||||||
-0x867935,
|
|
||||||
-0x9b4a0a,
|
|
||||||
-0xb03c09,
|
|
||||||
-0xb22f1f,
|
|
||||||
-0xb24954,
|
|
||||||
-0x7e387c,
|
|
||||||
-0x512a7f,
|
|
||||||
-0x759b,
|
|
||||||
-0x2b1ea9,
|
|
||||||
-0x2ab1,
|
|
||||||
-0x48b3,
|
|
||||||
-0x5e7781,
|
|
||||||
-0x6f5b52,
|
|
||||||
)
|
|
||||||
|
|
||||||
init {
|
|
||||||
view = LayoutInflater.from(context).inflate(R.layout.circle_image_view, this, true)
|
|
||||||
imageView = view.findViewById(R.id.circleImage)
|
|
||||||
textView = view.findViewById(R.id.circleText)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun setBackgroundAndText(text: String) {
|
|
||||||
val circleDrawable = GradientDrawable()
|
|
||||||
val color = colorFromIdentifier(text)
|
|
||||||
circleDrawable.setColor(color)
|
|
||||||
imageView.setImageDrawable(circleDrawable)
|
|
||||||
|
|
||||||
textView.text = text.toTextDrawableString()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun colorFromIdentifier(key: String): Int = colorScheme[abs(key.hashCode()) % colorScheme.size]
|
|
||||||
}
|
|
@ -1,7 +1,6 @@
|
|||||||
package bou.amine.apps.readerforselfossv2.android.utils
|
package bou.amine.apps.readerforselfossv2.android.utils
|
||||||
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.content.ActivityNotFoundException
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
@ -22,27 +21,31 @@ fun Context.openItemUrl(
|
|||||||
currentItem: Int,
|
currentItem: Int,
|
||||||
linkDecoded: String,
|
linkDecoded: String,
|
||||||
articleViewer: Boolean,
|
articleViewer: Boolean,
|
||||||
app: Activity,
|
app: Activity
|
||||||
) {
|
) {
|
||||||
|
|
||||||
if (!linkDecoded.isUrlValid()) {
|
if (!linkDecoded.isUrlValid()) {
|
||||||
Toast
|
Toast.makeText(
|
||||||
.makeText(
|
this,
|
||||||
this,
|
this.getString(R.string.cant_open_invalid_url),
|
||||||
this.getString(R.string.cant_open_invalid_url),
|
Toast.LENGTH_LONG
|
||||||
Toast.LENGTH_LONG,
|
).show()
|
||||||
).show()
|
|
||||||
} else {
|
} else {
|
||||||
if (articleViewer) {
|
if (articleViewer) {
|
||||||
val intent = Intent(this, ReaderActivity::class.java)
|
val intent = Intent(this, ReaderActivity::class.java)
|
||||||
intent.putExtra("currentItem", currentItem)
|
intent.putExtra("currentItem", currentItem)
|
||||||
app.startActivity(intent)
|
app.startActivity(intent)
|
||||||
} else {
|
} else {
|
||||||
this.openUrlInBrowserAsNewTask(linkDecoded)
|
val intent = Intent(Intent.ACTION_VIEW)
|
||||||
|
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
||||||
|
intent.data = Uri.parse(linkDecoded.toStringUriWithHttp())
|
||||||
|
startActivity(intent)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun String.isUrlValid(): Boolean = this.toHttpUrlOrNull() != null && Patterns.WEB_URL.matcher(this).matches()
|
fun String.isUrlValid(): Boolean =
|
||||||
|
this.toHttpUrlOrNull() != null && Patterns.WEB_URL.matcher(this).matches()
|
||||||
|
|
||||||
fun String.isBaseUrlInvalid(): Boolean {
|
fun String.isBaseUrlInvalid(): Boolean {
|
||||||
val baseUrl = this.toHttpUrlOrNull()
|
val baseUrl = this.toHttpUrlOrNull()
|
||||||
@ -55,37 +58,15 @@ fun String.isBaseUrlInvalid(): Boolean {
|
|||||||
return !(Patterns.WEB_URL.matcher(this).matches() && existsAndEndsWithSlash)
|
return !(Patterns.WEB_URL.matcher(this).matches() && existsAndEndsWithSlash)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Context.openItemUrlInBrowserAsNewTask(i: SelfossModel.Item) {
|
fun Context.openInBrowserAsNewTask(i: SelfossModel.Item) {
|
||||||
this.openUrlInBrowserAsNewTask(i.getLinkDecoded().toStringUriWithHttp())
|
|
||||||
}
|
|
||||||
|
|
||||||
fun Context.openUrlInBrowserAsNewTask(url: String) {
|
|
||||||
val intent = Intent(Intent.ACTION_VIEW)
|
val intent = Intent(Intent.ACTION_VIEW)
|
||||||
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
||||||
intent.data = Uri.parse(url)
|
intent.data = Uri.parse(i.getLinkDecoded().toStringUriWithHttp())
|
||||||
this.mayBeStartActivity(intent)
|
startActivity(intent)
|
||||||
}
|
|
||||||
|
|
||||||
fun Context.openUrlInBrowser(url: String) {
|
|
||||||
val intent = Intent(Intent.ACTION_VIEW)
|
|
||||||
intent.data = Uri.parse(url)
|
|
||||||
this.mayBeStartActivity(intent)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Suppress("detekt:SwallowedException")
|
|
||||||
fun Context.mayBeStartActivity(intent: Intent) {
|
|
||||||
try {
|
|
||||||
this.startActivity(intent)
|
|
||||||
} catch (e: ActivityNotFoundException) {
|
|
||||||
Toast.makeText(this, getString(R.string.no_browser), Toast.LENGTH_SHORT).show()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class LinkOnTouchListener : View.OnTouchListener {
|
class LinkOnTouchListener : View.OnTouchListener {
|
||||||
override fun onTouch(
|
override fun onTouch(v: View?, event: MotionEvent?): Boolean {
|
||||||
v: View?,
|
|
||||||
event: MotionEvent?,
|
|
||||||
): Boolean {
|
|
||||||
var ret = false
|
var ret = false
|
||||||
val widget: TextView = v as TextView
|
val widget: TextView = v as TextView
|
||||||
val text: CharSequence = widget.text
|
val text: CharSequence = widget.text
|
||||||
|
@ -1,26 +0,0 @@
|
|||||||
package bou.amine.apps.readerforselfossv2.android.utils.acra
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.os.DeadSystemException
|
|
||||||
import com.google.auto.service.AutoService
|
|
||||||
import org.acra.builder.ReportBuilder
|
|
||||||
import org.acra.config.CoreConfiguration
|
|
||||||
import org.acra.config.ReportingAdministrator
|
|
||||||
import org.acra.data.CrashReportData
|
|
||||||
|
|
||||||
@AutoService(ReportingAdministrator::class)
|
|
||||||
class AcraReportingAdministrator : ReportingAdministrator {
|
|
||||||
override fun shouldStartCollecting(
|
|
||||||
context: Context,
|
|
||||||
config: CoreConfiguration,
|
|
||||||
reportBuilder: ReportBuilder,
|
|
||||||
): Boolean =
|
|
||||||
reportBuilder.exception !is DeadSystemException &&
|
|
||||||
(reportBuilder.exception != null && reportBuilder.exception!!::class.simpleName != "CannotDeliverBroadcastException")
|
|
||||||
|
|
||||||
override fun shouldSendReport(
|
|
||||||
context: Context,
|
|
||||||
config: CoreConfiguration,
|
|
||||||
crashReportData: CrashReportData,
|
|
||||||
): Boolean = crashReportData.get("BRAND") != "redroid"
|
|
||||||
}
|
|
@ -1,13 +1,6 @@
|
|||||||
package bou.amine.apps.readerforselfossv2.android.utils.bottombar
|
package bou.amine.apps.readerforselfossv2.android.utils.bottombar
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.graphics.drawable.Drawable
|
|
||||||
import androidx.annotation.IdRes
|
|
||||||
import androidx.annotation.StringRes
|
|
||||||
import bou.amine.apps.readerforselfossv2.android.R
|
|
||||||
import com.ashokvarma.bottomnavigation.TextBadgeItem
|
import com.ashokvarma.bottomnavigation.TextBadgeItem
|
||||||
import com.leinardi.android.speeddial.SpeedDialActionItem
|
|
||||||
import com.leinardi.android.speeddial.SpeedDialView
|
|
||||||
|
|
||||||
fun TextBadgeItem.removeBadge(): TextBadgeItem {
|
fun TextBadgeItem.removeBadge(): TextBadgeItem {
|
||||||
this.setText("")
|
this.setText("")
|
||||||
@ -15,26 +8,5 @@ fun TextBadgeItem.removeBadge(): TextBadgeItem {
|
|||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
fun TextBadgeItem.maybeShow(): TextBadgeItem = if (this.isHidden) this.show() else this
|
fun TextBadgeItem.maybeShow(): TextBadgeItem =
|
||||||
|
if (this.isHidden) this.show() else this
|
||||||
@Suppress("detekt:LongParameterList")
|
|
||||||
fun SpeedDialView.addHomeMadeActionItem(
|
|
||||||
@IdRes actionId: Int,
|
|
||||||
actionIcon: Drawable,
|
|
||||||
@StringRes labelId: Int,
|
|
||||||
colorOnSurface: Int,
|
|
||||||
colorSurface: Int,
|
|
||||||
context: Context,
|
|
||||||
) {
|
|
||||||
this.addActionItem(
|
|
||||||
SpeedDialActionItem
|
|
||||||
.Builder(actionId, actionIcon)
|
|
||||||
.setFabBackgroundColor(context.resources.getColor(R.color.colorAccent))
|
|
||||||
.setFabImageTintColor(colorOnSurface)
|
|
||||||
.setLabel(context.getString(labelId))
|
|
||||||
.setLabelClickable(false)
|
|
||||||
.setLabelBackgroundColor(colorOnSurface)
|
|
||||||
.setLabelColor(colorSurface)
|
|
||||||
.create(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
@ -2,135 +2,41 @@ package bou.amine.apps.readerforselfossv2.android.utils.glide
|
|||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
import android.graphics.drawable.Drawable
|
|
||||||
import android.webkit.WebView
|
|
||||||
import android.widget.ImageView
|
import android.widget.ImageView
|
||||||
import bou.amine.apps.readerforselfossv2.android.utils.CircleImageView
|
import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory
|
||||||
import bou.amine.apps.readerforselfossv2.service.AppSettingsService
|
|
||||||
import bou.amine.apps.readerforselfossv2.utils.isEmptyOrNullOrNullString
|
|
||||||
import com.bumptech.glide.Glide
|
import com.bumptech.glide.Glide
|
||||||
import com.bumptech.glide.load.engine.DiskCacheStrategy
|
|
||||||
import com.bumptech.glide.load.model.GlideUrl
|
|
||||||
import com.bumptech.glide.load.model.LazyHeaders
|
|
||||||
import com.bumptech.glide.request.RequestOptions
|
import com.bumptech.glide.request.RequestOptions
|
||||||
import com.bumptech.glide.request.target.ViewTarget
|
import com.bumptech.glide.request.target.BitmapImageViewTarget
|
||||||
import com.google.android.material.chip.Chip
|
|
||||||
import java.io.ByteArrayInputStream
|
import java.io.ByteArrayInputStream
|
||||||
import java.io.ByteArrayOutputStream
|
import java.io.ByteArrayOutputStream
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import kotlin.io.encoding.Base64
|
|
||||||
import kotlin.io.encoding.ExperimentalEncodingApi
|
|
||||||
|
|
||||||
private const val PRELOAD_IMAGE_TIMEOUT = 10000
|
fun Context.bitmapCenterCrop(url: String, iv: ImageView) =
|
||||||
|
Glide.with(this)
|
||||||
|
.asBitmap()
|
||||||
|
.load(url)
|
||||||
|
.apply(RequestOptions.centerCropTransform())
|
||||||
|
.into(iv)
|
||||||
|
|
||||||
@Suppress("detekt:ReturnCount")
|
fun Context.circularBitmapDrawable(url: String, iv: ImageView) =
|
||||||
@OptIn(ExperimentalEncodingApi::class)
|
Glide.with(this)
|
||||||
fun String.toGlideUrl(appSettingsService: AppSettingsService): Any { // GlideUrl Or String
|
.asBitmap()
|
||||||
if (this.isEmptyOrNullOrNullString()) {
|
.load(url)
|
||||||
return ""
|
.apply(RequestOptions.centerCropTransform())
|
||||||
}
|
.into(object : BitmapImageViewTarget(iv) {
|
||||||
if (appSettingsService.getBasicUserName().isNotEmpty()) {
|
override fun setResource(resource: Bitmap?) {
|
||||||
val authString = "${appSettingsService.getBasicUserName()}:${appSettingsService.getBasicPassword()}"
|
val circularBitmapDrawable = RoundedBitmapDrawableFactory.create(
|
||||||
val authBuf = Base64.encode(authString.toByteArray(Charsets.UTF_8))
|
resources,
|
||||||
|
resource
|
||||||
|
)
|
||||||
|
circularBitmapDrawable.isCircular = true
|
||||||
|
iv.setImageDrawable(circularBitmapDrawable)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
return GlideUrl(
|
fun getBitmapInputStream(bitmap:Bitmap,compressFormat: Bitmap.CompressFormat): InputStream {
|
||||||
this,
|
|
||||||
LazyHeaders
|
|
||||||
.Builder()
|
|
||||||
.addHeader("Authorization", "Basic $authBuf")
|
|
||||||
.build(),
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
return GlideUrl(
|
|
||||||
this,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun WebView.getGlideImageForResource(
|
|
||||||
url: String,
|
|
||||||
appSettingsService: AppSettingsService,
|
|
||||||
) = Glide
|
|
||||||
.with(this)
|
|
||||||
.asBitmap()
|
|
||||||
.apply(RequestOptions.diskCacheStrategyOf(DiskCacheStrategy.ALL))
|
|
||||||
.load(url.toGlideUrl(appSettingsService))
|
|
||||||
.submit()
|
|
||||||
.get()
|
|
||||||
|
|
||||||
fun Context.preloadImage(
|
|
||||||
url: String,
|
|
||||||
appSettingsService: AppSettingsService,
|
|
||||||
) = Glide
|
|
||||||
.with(this)
|
|
||||||
.asBitmap()
|
|
||||||
.apply(RequestOptions.diskCacheStrategyOf(DiskCacheStrategy.ALL).timeout(PRELOAD_IMAGE_TIMEOUT))
|
|
||||||
.load(url.toGlideUrl(appSettingsService))
|
|
||||||
.submit()
|
|
||||||
|
|
||||||
fun Context.imageIntoViewTarget(
|
|
||||||
url: String,
|
|
||||||
target: ViewTarget<Chip?, Drawable?>,
|
|
||||||
appSettingsService: AppSettingsService,
|
|
||||||
) = Glide
|
|
||||||
.with(this)
|
|
||||||
.load(url.toGlideUrl(appSettingsService))
|
|
||||||
.into(target)
|
|
||||||
|
|
||||||
fun Context.bitmapWithCache(
|
|
||||||
url: String,
|
|
||||||
iv: ImageView,
|
|
||||||
appSettingsService: AppSettingsService,
|
|
||||||
) = Glide
|
|
||||||
.with(this)
|
|
||||||
.asBitmap()
|
|
||||||
.apply(RequestOptions.diskCacheStrategyOf(DiskCacheStrategy.ALL))
|
|
||||||
.load(url.toGlideUrl(appSettingsService))
|
|
||||||
.into(iv)
|
|
||||||
|
|
||||||
fun Context.bitmapCenterCrop(
|
|
||||||
url: String,
|
|
||||||
iv: ImageView,
|
|
||||||
appSettingsService: AppSettingsService,
|
|
||||||
) = Glide
|
|
||||||
.with(this)
|
|
||||||
.asBitmap()
|
|
||||||
.load(url.toGlideUrl(appSettingsService))
|
|
||||||
.apply(RequestOptions.centerCropTransform())
|
|
||||||
.into(iv)
|
|
||||||
|
|
||||||
fun Context.bitmapFitCenter(
|
|
||||||
url: String,
|
|
||||||
iv: ImageView,
|
|
||||||
appSettingsService: AppSettingsService,
|
|
||||||
) = Glide
|
|
||||||
.with(this)
|
|
||||||
.asBitmap()
|
|
||||||
.load(url.toGlideUrl(appSettingsService))
|
|
||||||
.apply(RequestOptions.fitCenterTransform())
|
|
||||||
.into(iv)
|
|
||||||
|
|
||||||
fun Context.circularDrawable(
|
|
||||||
url: String,
|
|
||||||
view: CircleImageView,
|
|
||||||
appSettingsService: AppSettingsService,
|
|
||||||
) {
|
|
||||||
view.textView.text = ""
|
|
||||||
|
|
||||||
Glide
|
|
||||||
.with(this)
|
|
||||||
.load(url.toGlideUrl(appSettingsService))
|
|
||||||
.into(view.imageView)
|
|
||||||
}
|
|
||||||
|
|
||||||
private const val BITMAP_INPUT_STREAM_COMPRESSION_QUALITY = 80
|
|
||||||
|
|
||||||
fun getBitmapInputStream(
|
|
||||||
bitmap: Bitmap,
|
|
||||||
compressFormat: Bitmap.CompressFormat,
|
|
||||||
): InputStream {
|
|
||||||
val byteArrayOutputStream = ByteArrayOutputStream()
|
val byteArrayOutputStream = ByteArrayOutputStream()
|
||||||
bitmap.compress(compressFormat, BITMAP_INPUT_STREAM_COMPRESSION_QUALITY, byteArrayOutputStream)
|
bitmap.compress(compressFormat, 80, byteArrayOutputStream)
|
||||||
val bitmapData: ByteArray = byteArrayOutputStream.toByteArray()
|
val bitmapData: ByteArray = byteArrayOutputStream.toByteArray()
|
||||||
return ByteArrayInputStream(bitmapData)
|
return ByteArrayInputStream(bitmapData)
|
||||||
}
|
}
|
@ -3,6 +3,7 @@ package bou.amine.apps.readerforselfossv2.android.utils.network
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.net.ConnectivityManager
|
import android.net.ConnectivityManager
|
||||||
import android.net.NetworkCapabilities
|
import android.net.NetworkCapabilities
|
||||||
|
import android.os.Build
|
||||||
import com.google.android.material.snackbar.Snackbar
|
import com.google.android.material.snackbar.Snackbar
|
||||||
|
|
||||||
lateinit var s: Snackbar
|
lateinit var s: Snackbar
|
||||||
@ -10,13 +11,19 @@ lateinit var s: Snackbar
|
|||||||
fun isNetworkAccessible(context: Context): Boolean {
|
fun isNetworkAccessible(context: Context): Boolean {
|
||||||
val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
|
val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
|
||||||
|
|
||||||
val networkCapabilities = connectivityManager.getNetworkCapabilities(connectivityManager.activeNetwork) ?: return false
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||||
|
val network = connectivityManager.activeNetwork ?: return false
|
||||||
|
val networkCapabilities = connectivityManager.getNetworkCapabilities(network) ?: return false
|
||||||
|
|
||||||
return when {
|
return when {
|
||||||
networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_BLUETOOTH) -> true
|
networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_BLUETOOTH) -> true
|
||||||
networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) -> true
|
networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) -> true
|
||||||
networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET) -> true
|
networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET) -> true
|
||||||
networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) -> true
|
networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) -> true
|
||||||
else -> false
|
else -> false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
val network = connectivityManager.activeNetworkInfo ?: return false
|
||||||
|
return network.isConnectedOrConnecting
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -7,9 +7,7 @@ import kotlinx.coroutines.flow.MutableSharedFlow
|
|||||||
import kotlinx.coroutines.flow.asSharedFlow
|
import kotlinx.coroutines.flow.asSharedFlow
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
class AppViewModel(
|
class AppViewModel(private val repository: Repository) : ViewModel() {
|
||||||
private val repository: Repository,
|
|
||||||
) : ViewModel() {
|
|
||||||
private val _networkAvailableProvider = MutableSharedFlow<Boolean>()
|
private val _networkAvailableProvider = MutableSharedFlow<Boolean>()
|
||||||
val networkAvailableProvider = _networkAvailableProvider.asSharedFlow()
|
val networkAvailableProvider = _networkAvailableProvider.asSharedFlow()
|
||||||
private var wasConnected = true
|
private var wasConnected = true
|
||||||
@ -21,7 +19,7 @@ class AppViewModel(
|
|||||||
if (isConnected && !wasConnected && repository.connectionMonitored) {
|
if (isConnected && !wasConnected && repository.connectionMonitored) {
|
||||||
_networkAvailableProvider.emit(true)
|
_networkAvailableProvider.emit(true)
|
||||||
wasConnected = true
|
wasConnected = true
|
||||||
} else if (!isConnected && wasConnected && repository.connectionMonitored) {
|
} else if (!isConnected && wasConnected && repository.connectionMonitored){
|
||||||
_networkAvailableProvider.emit(false)
|
_networkAvailableProvider.emit(false)
|
||||||
wasConnected = false
|
wasConnected = false
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<androidx.drawerlayout.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<androidx.drawerlayout.widget.DrawerLayout
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:id="@+id/drawerContainer"
|
android:id="@+id/drawerContainer"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
|
tools:context="bou.amine.apps.readerforselfossv2.android.HomeActivity"
|
||||||
android:fitsSystemWindows="true"
|
android:fitsSystemWindows="true"
|
||||||
tools:context="bou.amine.apps.readerforselfossv2.android.HomeActivity">
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
|
||||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||||
android:id="@+id/coordLayout"
|
android:id="@+id/coordLayout"
|
||||||
@ -27,14 +28,12 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content">
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
<androidx.appcompat.widget.Toolbar
|
<androidx.appcompat.widget.Toolbar app:popupTheme="?attr/toolbarPopupTheme" android:theme="@style/ToolBarStyle"
|
||||||
android:id="@+id/toolBar"
|
android:id="@+id/toolBar"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="?attr/actionBarSize"
|
android:layout_height="?attr/actionBarSize"
|
||||||
android:theme="@style/ToolBarStyle"
|
|
||||||
app:popupTheme="?attr/toolbarPopupTheme"
|
|
||||||
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
</com.google.android.material.appbar.AppBarLayout>
|
</com.google.android.material.appbar.AppBarLayout>
|
||||||
|
|
||||||
@ -46,19 +45,19 @@
|
|||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:background="?android:attr/windowBackground"
|
android:orientation="vertical"
|
||||||
android:orientation="vertical">
|
android:background="?android:attr/windowBackground">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/emptyText"
|
android:id="@+id/emptyText"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:background="@android:color/transparent"
|
|
||||||
android:gravity="center_horizontal"
|
android:gravity="center_horizontal"
|
||||||
android:paddingTop="100dp"
|
android:paddingTop="100dp"
|
||||||
android:text="@string/nothing_here"
|
android:text="@string/nothing_here"
|
||||||
android:textAlignment="center"
|
android:textAlignment="center"
|
||||||
android:textAppearance="@style/TextAppearance.AppCompat.Headline"
|
android:textAppearance="@style/TextAppearance.AppCompat.Headline"
|
||||||
|
android:background="@android:color/transparent"
|
||||||
android:visibility="gone" />
|
android:visibility="gone" />
|
||||||
|
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
@ -70,7 +69,7 @@
|
|||||||
android:paddingBottom="60dp"
|
android:paddingBottom="60dp"
|
||||||
android:scrollbars="vertical"
|
android:scrollbars="vertical"
|
||||||
app:layout_behavior="@string/appbar_scrolling_view_behavior"
|
app:layout_behavior="@string/appbar_scrolling_view_behavior"
|
||||||
tools:listitem="@layout/list_item" />
|
tools:listitem="@layout/list_item"/>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
||||||
@ -78,7 +77,6 @@
|
|||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
|
|
||||||
<com.ashokvarma.bottomnavigation.BottomNavigationBar
|
<com.ashokvarma.bottomnavigation.BottomNavigationBar
|
||||||
android:id="@+id/bottomBar"
|
android:id="@+id/bottomBar"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
@ -1,40 +1,33 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<androidx.constraintlayout.motion.widget.MotionLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
android:id="@+id/container"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent">
|
||||||
app:layoutDescription="@xml/image_close_scene">
|
|
||||||
|
|
||||||
<com.google.android.material.appbar.AppBarLayout
|
<com.google.android.material.appbar.AppBarLayout
|
||||||
android:id="@+id/appBarLayout"
|
android:id="@+id/appBarLayout"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content">
|
android:layout_height="wrap_content"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent">
|
||||||
|
|
||||||
<androidx.appcompat.widget.Toolbar android:theme="@style/ToolBarStyle"
|
<androidx.appcompat.widget.Toolbar
|
||||||
android:id="@+id/toolBar"
|
android:id="@+id/toolBar"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="?attr/actionBarSize"
|
android:layout_height="?attr/actionBarSize"
|
||||||
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
</com.google.android.material.appbar.AppBarLayout>
|
</com.google.android.material.appbar.AppBarLayout>
|
||||||
|
|
||||||
<androidx.core.widget.NestedScrollView
|
<androidx.viewpager2.widget.ViewPager2
|
||||||
android:id="@+id/scrollView"
|
android:id="@+id/pager"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="0dp"
|
||||||
android:fillViewport="true"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/appBarLayout">
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/appBarLayout" />
|
||||||
|
|
||||||
<androidx.viewpager2.widget.ViewPager2
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
android:id="@+id/pager"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent" />
|
|
||||||
</androidx.core.widget.NestedScrollView>
|
|
||||||
|
|
||||||
|
|
||||||
</androidx.constraintlayout.motion.widget.MotionLayout>
|
|
||||||
|
@ -1,31 +1,31 @@
|
|||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
android:gravity="center_horizontal"
|
android:gravity="center_horizontal"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
tools:context="bou.amine.apps.readerforselfossv2.android.LoginActivity">
|
tools:context="bou.amine.apps.readerforselfossv2.android.LoginActivity">
|
||||||
|
|
||||||
<com.google.android.material.appbar.AppBarLayout
|
<com.google.android.material.appbar.AppBarLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content">
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
<androidx.appcompat.widget.Toolbar
|
<androidx.appcompat.widget.Toolbar app:popupTheme="?attr/toolbarPopupTheme" android:theme="@style/ToolBarStyle"
|
||||||
android:id="@+id/toolbar"
|
android:id="@+id/toolbar"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="?attr/actionBarSize"
|
android:layout_height="?attr/actionBarSize"
|
||||||
android:theme="@style/ToolBarStyle"
|
|
||||||
app:popupTheme="?attr/toolbarPopupTheme" />
|
/>
|
||||||
|
|
||||||
</com.google.android.material.appbar.AppBarLayout>
|
</com.google.android.material.appbar.AppBarLayout>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:gravity="center"
|
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:padding="@dimen/activity_horizontal_margin">
|
android:paddingBottom="@dimen/activity_vertical_margin"
|
||||||
|
android:paddingLeft="@dimen/activity_horizontal_margin"
|
||||||
|
android:paddingRight="@dimen/activity_horizontal_margin"
|
||||||
|
android:paddingTop="@dimen/activity_vertical_margin">
|
||||||
<!-- Login progress -->
|
<!-- Login progress -->
|
||||||
<ProgressBar
|
<ProgressBar
|
||||||
android:id="@+id/loginProgress"
|
android:id="@+id/loginProgress"
|
||||||
@ -33,72 +33,67 @@
|
|||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginBottom="8dp"
|
android:layout_marginBottom="8dp"
|
||||||
android:visibility="gone" />
|
android:visibility="gone"/>
|
||||||
|
|
||||||
<LinearLayout
|
<ScrollView
|
||||||
android:id="@+id/loginForm"
|
android:id="@+id/loginForm"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent">
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<EditText
|
<LinearLayout
|
||||||
android:id="@+id/urlView"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:hint="@string/prompt_url"
|
android:orientation="vertical">
|
||||||
android:imeOptions="actionUnspecified"
|
|
||||||
android:importantForAutofill="no"
|
|
||||||
android:inputType="textUri"
|
|
||||||
android:maxLines="1"
|
|
||||||
android:minHeight="48dp" />
|
|
||||||
|
|
||||||
<com.google.android.material.switchmaterial.SwitchMaterial
|
<EditText
|
||||||
android:id="@+id/selfSigned"
|
android:id="@+id/urlView"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="@string/disable_ssl"
|
android:hint="@string/prompt_url"
|
||||||
android:textAlignment="viewStart" />
|
android:imeOptions="actionUnspecified"
|
||||||
|
android:importantForAutofill="no"
|
||||||
|
android:inputType="textUri"
|
||||||
|
android:maxLines="1" />
|
||||||
|
|
||||||
<com.google.android.material.switchmaterial.SwitchMaterial
|
<com.google.android.material.switchmaterial.SwitchMaterial
|
||||||
android:id="@+id/withLogin"
|
android:text="@string/withLoginSwitch"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="0dp"
|
||||||
android:text="@string/withLoginSwitch"
|
android:id="@+id/withLogin"
|
||||||
android:textAlignment="viewStart" />
|
android:layout_weight="1"/>
|
||||||
|
|
||||||
<EditText
|
<EditText
|
||||||
android:id="@+id/loginView"
|
android:id="@+id/loginView"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:autofillHints="username"
|
android:autofillHints="username"
|
||||||
android:hint="@string/prompt_login"
|
android:hint="@string/prompt_login"
|
||||||
android:inputType="text"
|
android:inputType="text"
|
||||||
android:maxLines="1"
|
android:maxLines="1"
|
||||||
android:minHeight="48dp"
|
android:visibility="gone" />
|
||||||
android:visibility="gone" />
|
|
||||||
|
|
||||||
<EditText
|
<EditText
|
||||||
android:id="@+id/passwordView"
|
android:id="@+id/passwordView"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:autofillHints="password"
|
android:autofillHints="password"
|
||||||
android:hint="@string/prompt_password"
|
android:hint="@string/prompt_password"
|
||||||
android:inputType="textPassword"
|
android:inputType="textPassword"
|
||||||
android:maxLines="1"
|
android:maxLines="1"
|
||||||
android:minHeight="48dp"
|
android:visibility="gone" />
|
||||||
android:visibility="gone" />
|
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
android:id="@+id/signInButton"
|
android:id="@+id/signInButton"
|
||||||
style="?android:textAppearanceSmall"
|
style="?android:textAppearanceSmall"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="16dp"
|
android:layout_marginTop="16dp"
|
||||||
android:layout_marginBottom="16dp"
|
android:layout_marginBottom="16dp"
|
||||||
android:text="@string/action_sign_in"
|
android:text="@string/action_sign_in"
|
||||||
android:textStyle="bold" />
|
android:textStyle="bold" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
</ScrollView>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
@ -24,8 +24,7 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:scrollbars="vertical"
|
android:scrollbars="vertical"
|
||||||
app:layout_behavior="@string/appbar_scrolling_view_behavior"
|
app:layout_behavior="@string/appbar_scrolling_view_behavior">
|
||||||
tools:listitem="@layout/source_list_item">
|
|
||||||
</androidx.recyclerview.widget.RecyclerView>
|
</androidx.recyclerview.widget.RecyclerView>
|
||||||
|
|
||||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||||
|
@ -17,83 +17,100 @@
|
|||||||
<androidx.appcompat.widget.Toolbar app:popupTheme="?attr/toolbarPopupTheme" android:theme="@style/ToolBarStyle"
|
<androidx.appcompat.widget.Toolbar app:popupTheme="?attr/toolbarPopupTheme" android:theme="@style/ToolBarStyle"
|
||||||
android:id="@+id/toolbar"
|
android:id="@+id/toolbar"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="?attr/actionBarSize" />
|
android:layout_height="?attr/actionBarSize"
|
||||||
|
|
||||||
|
/>
|
||||||
|
|
||||||
</com.google.android.material.appbar.AppBarLayout>
|
</com.google.android.material.appbar.AppBarLayout>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
android:id="@+id/formContainer"
|
android:paddingBottom="@dimen/activity_vertical_margin"
|
||||||
android:layout_width="match_parent"
|
android:paddingLeft="@dimen/activity_horizontal_margin"
|
||||||
|
android:paddingRight="@dimen/activity_horizontal_margin"
|
||||||
|
android:paddingTop="@dimen/activity_vertical_margin"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:padding="16dp"
|
android:layout_width="match_parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
android:id="@+id/formContainer"
|
||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
app:layout_constraintHorizontal_bias="1.0"
|
app:layout_constraintHorizontal_bias="1.0"
|
||||||
app:layout_constraintLeft_toLeftOf="parent"
|
app:layout_constraintVertical_bias="0.0">
|
||||||
app:layout_constraintTop_toTopOf="parent">
|
|
||||||
|
|
||||||
|
|
||||||
<EditText
|
<EditText
|
||||||
android:id="@+id/nameInput"
|
android:id="@+id/nameInput"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:minHeight="48dp"
|
|
||||||
android:layout_marginTop="16dp"
|
android:layout_marginTop="16dp"
|
||||||
android:autofillHints="false"
|
android:autofillHints="false"
|
||||||
|
android:ems="10"
|
||||||
android:hint="@string/add_source_hint_name"
|
android:hint="@string/add_source_hint_name"
|
||||||
android:inputType="text"
|
android:inputType="text"
|
||||||
android:textColorHint="?android:textColorPrimary"
|
android:textColorHint="?android:textColorPrimary"
|
||||||
app:layout_constraintLeft_toLeftOf="parent"
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
<EditText
|
<EditText
|
||||||
android:id="@+id/sourceUri"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:minHeight="48dp"
|
|
||||||
android:layout_marginTop="16dp"
|
|
||||||
android:autofillHints="false"
|
|
||||||
android:hint="@string/add_source_hint_url"
|
|
||||||
android:inputType="textUri"
|
android:inputType="textUri"
|
||||||
|
android:ems="10"
|
||||||
|
android:id="@+id/sourceUri"
|
||||||
|
android:hint="@string/add_source_hint_url"
|
||||||
android:textColorHint="?android:textColorPrimary"
|
android:textColorHint="?android:textColorPrimary"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/nameInput"
|
||||||
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
app:layout_constraintLeft_toLeftOf="parent"
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/nameInput" />
|
android:autofillHints="false" />
|
||||||
|
|
||||||
<EditText
|
<EditText
|
||||||
android:id="@+id/tags"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:minHeight="48dp"
|
android:ems="10"
|
||||||
android:layout_marginTop="16dp"
|
android:id="@+id/tags"
|
||||||
android:autofillHints="false"
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
android:hint="@string/add_source_hint_tags"
|
|
||||||
android:inputType="text"
|
|
||||||
android:textColorHint="?android:textColorPrimary"
|
|
||||||
app:layout_constraintLeft_toLeftOf="parent"
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/sourceUri" />
|
android:layout_marginTop="16dp"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/sourceUri"
|
||||||
|
android:hint="@string/add_source_hint_tags"
|
||||||
|
android:textColorHint="?android:textColorPrimary"
|
||||||
|
android:inputType="text"
|
||||||
|
android:autofillHints="false" />
|
||||||
|
|
||||||
<Spinner
|
<Spinner
|
||||||
android:id="@+id/spoutsSpinner"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:id="@+id/spoutsSpinner"
|
||||||
android:minHeight="48dp"
|
|
||||||
android:layout_marginTop="16dp"
|
android:layout_marginTop="16dp"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/tags"
|
||||||
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
app:layout_constraintLeft_toLeftOf="parent"
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/tags" />
|
android:layout_height="40dp"/>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
android:id="@+id/saveBtn"
|
android:text="@string/add_source_save"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="16dp"
|
android:id="@+id/saveBtn"
|
||||||
android:elevation="5dp"
|
android:elevation="5dp"
|
||||||
android:text="@string/add_source_save"
|
|
||||||
android:textColor="?android:textColorPrimary"
|
android:textColor="?android:textColorPrimary"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
android:layout_marginEnd="16dp"
|
||||||
app:layout_constraintLeft_toLeftOf="parent"
|
|
||||||
app:layout_constraintRight_toRightOf="parent"
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/spoutsSpinner" />
|
android:layout_marginRight="16dp"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
|
android:layout_marginLeft="16dp"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/spoutsSpinner"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
android:layout_marginBottom="16dp"
|
||||||
|
app:layout_constraintVertical_bias="0.0"/>
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
@ -102,6 +119,8 @@
|
|||||||
style="?android:attr/progressBarStyleLarge"
|
style="?android:attr/progressBarStyleLarge"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:visibility="visible"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintLeft_toLeftOf="parent"
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
app:layout_constraintRight_toRightOf="parent"
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
@ -1,14 +1,18 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
|
<androidx.cardview.widget.CardView
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:card_view="http://schemas.android.com/apk/res-auto"
|
xmlns:card_view="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:id="@+id/card"
|
android:id="@+id/card"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_margin="8dp"
|
android:layout_marginLeft="8dp"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
android:layout_marginRight="8dp"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
android:layout_marginTop="8dp"
|
||||||
|
app:layout_constraintHorizontal_bias="0.62"
|
||||||
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
card_view:cardElevation="2dp"
|
card_view:cardElevation="2dp"
|
||||||
card_view:cardUseCompatPadding="true"
|
card_view:cardUseCompatPadding="true"
|
||||||
@ -24,8 +28,8 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:adjustViewBounds="true"
|
android:adjustViewBounds="true"
|
||||||
android:cropToPadding="true"
|
android:cropToPadding="true"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
app:srcCompat="@drawable/background_splash"
|
app:srcCompat="@drawable/background_splash"
|
||||||
card_view:layout_constraintBottom_toTopOf="@+id/constraintLayout" />
|
card_view:layout_constraintBottom_toTopOf="@+id/constraintLayout" />
|
||||||
@ -35,17 +39,18 @@
|
|||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/itemImage">
|
app:layout_constraintTop_toBottomOf="@+id/itemImage">
|
||||||
|
|
||||||
<bou.amine.apps.readerforselfossv2.android.utils.CircleImageView
|
<ImageView
|
||||||
android:id="@+id/sourceImage"
|
android:id="@+id/sourceImage"
|
||||||
android:layout_width="40dp"
|
android:layout_width="40dp"
|
||||||
android:layout_height="40dp"
|
android:layout_height="40dp"
|
||||||
|
android:layout_marginLeft="8dp"
|
||||||
android:layout_marginStart="8dp"
|
android:layout_marginStart="8dp"
|
||||||
android:layout_marginTop="8dp"
|
android:layout_marginTop="8dp"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
app:srcCompat="@drawable/background_splash" />
|
app:srcCompat="@drawable/background_splash" />
|
||||||
|
|
||||||
@ -53,63 +58,70 @@
|
|||||||
android:id="@+id/title"
|
android:id="@+id/title"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_margin="8dp"
|
|
||||||
android:layout_marginStart="8dp"
|
|
||||||
android:layout_marginTop="8dp"
|
|
||||||
android:layout_marginEnd="8dp"
|
android:layout_marginEnd="8dp"
|
||||||
|
android:layout_marginLeft="8dp"
|
||||||
|
android:layout_marginRight="8dp"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
android:gravity="start"
|
||||||
android:textAlignment="viewStart"
|
android:textAlignment="viewStart"
|
||||||
android:textColor="?android:textColorPrimary"
|
|
||||||
android:textStyle="bold"
|
android:textStyle="bold"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
android:textColor="?android:textColorPrimary"
|
||||||
app:layout_constraintStart_toEndOf="@+id/sourceImage"
|
app:layout_constraintHorizontal_bias="0.0"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintLeft_toRightOf="@+id/sourceImage"
|
||||||
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="@+id/sourceImage"
|
||||||
tools:text="Titre" />
|
tools:text="Titre" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/sourceTitleAndDate"
|
android:id="@+id/sourceTitleAndDate"
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="8dp"
|
|
||||||
android:layout_marginTop="8dp"
|
|
||||||
android:layout_marginEnd="8dp"
|
|
||||||
android:textAlignment="viewStart"
|
|
||||||
android:textColor="?android:textColorPrimary"
|
|
||||||
android:textSize="14sp"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="@+id/title"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/title"
|
|
||||||
tools:text="Google Actualité Il y a 5h" />
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="8dp"
|
android:layout_marginTop="8dp"
|
||||||
android:layout_marginEnd="8dp"
|
android:gravity="start"
|
||||||
|
android:textAlignment="viewStart"
|
||||||
|
android:textSize="14sp"
|
||||||
|
android:textColor="?android:textColorPrimary"
|
||||||
|
app:layout_constraintLeft_toLeftOf="@+id/title"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/title"
|
||||||
|
tools:text="Google Actualité Il y a 5h" />
|
||||||
|
|
||||||
|
<RelativeLayout
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/sourceTitleAndDate">
|
app:layout_constraintTop_toBottomOf="@+id/sourceTitleAndDate">
|
||||||
|
|
||||||
<ImageButton
|
<ImageButton
|
||||||
android:id="@+id/browserBtn"
|
android:id="@+id/favButton"
|
||||||
android:layout_width="35dp"
|
android:layout_width="35dp"
|
||||||
android:layout_height="35dp"
|
android:layout_height="35dp"
|
||||||
|
android:layout_alignParentEnd="true"
|
||||||
|
android:layout_alignParentRight="true"
|
||||||
|
android:layout_centerVertical="true"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:layout_marginRight="8dp"
|
||||||
android:adjustViewBounds="true"
|
android:adjustViewBounds="true"
|
||||||
android:background="@android:color/transparent"
|
android:background="@android:color/transparent"
|
||||||
android:contentDescription="@string/reader_action_open"
|
|
||||||
android:elevation="5dp"
|
android:elevation="5dp"
|
||||||
android:padding="4dp"
|
android:padding="4dp"
|
||||||
android:scaleType="centerCrop"
|
android:scaleType="centerCrop"
|
||||||
app:srcCompat="@drawable/ic_open_in_browser_black_24dp"
|
app:srcCompat="@drawable/ic_menu_heart_60dp"
|
||||||
app:tint="?android:attr/textColorPrimary" />
|
app:tint="@color/ic_menu_heart_color" />
|
||||||
|
|
||||||
<ImageButton
|
<ImageButton
|
||||||
android:id="@+id/shareBtn"
|
android:id="@+id/shareBtn"
|
||||||
android:layout_width="35dp"
|
android:layout_width="35dp"
|
||||||
android:layout_height="35dp"
|
android:layout_height="35dp"
|
||||||
android:layout_marginStart="16dp"
|
android:layout_centerVertical="true"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:layout_marginRight="16dp"
|
||||||
|
android:layout_toLeftOf="@+id/favButton"
|
||||||
|
android:layout_toStartOf="@+id/favButton"
|
||||||
android:adjustViewBounds="true"
|
android:adjustViewBounds="true"
|
||||||
android:background="@android:color/transparent"
|
android:background="@android:color/transparent"
|
||||||
android:contentDescription="@string/share"
|
|
||||||
android:elevation="5dp"
|
android:elevation="5dp"
|
||||||
android:padding="4dp"
|
android:padding="4dp"
|
||||||
android:scaleType="centerCrop"
|
android:scaleType="centerCrop"
|
||||||
@ -117,21 +129,23 @@
|
|||||||
app:tint="?android:attr/textColorPrimary" />
|
app:tint="?android:attr/textColorPrimary" />
|
||||||
|
|
||||||
<ImageButton
|
<ImageButton
|
||||||
android:id="@+id/favButton"
|
android:id="@+id/browserBtn"
|
||||||
android:layout_width="35dp"
|
android:layout_width="35dp"
|
||||||
android:layout_height="35dp"
|
android:layout_height="35dp"
|
||||||
android:layout_marginStart="16dp"
|
android:layout_centerVertical="true"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:layout_marginRight="16dp"
|
||||||
|
android:layout_toLeftOf="@+id/shareBtn"
|
||||||
|
android:layout_toStartOf="@+id/shareBtn"
|
||||||
android:adjustViewBounds="true"
|
android:adjustViewBounds="true"
|
||||||
android:background="@android:color/transparent"
|
android:background="@android:color/transparent"
|
||||||
android:contentDescription="@string/add_to_favs_reader"
|
|
||||||
android:elevation="5dp"
|
android:elevation="5dp"
|
||||||
android:padding="4dp"
|
android:padding="4dp"
|
||||||
android:scaleType="centerCrop"
|
android:scaleType="centerCrop"
|
||||||
app:srcCompat="@drawable/ic_menu_heart_60dp"
|
app:srcCompat="@drawable/ic_open_in_browser_black_24dp"
|
||||||
app:tint="@color/ic_menu_heart_color" />
|
app:tint="?android:attr/textColorPrimary" />
|
||||||
|
|
||||||
|
</RelativeLayout>
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
@ -1,26 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content">
|
|
||||||
|
|
||||||
<com.google.android.material.imageview.ShapeableImageView
|
|
||||||
android:id="@+id/circleImage"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:scaleType="centerCrop"
|
|
||||||
app:shapeAppearanceOverlay="@style/circleImageView"
|
|
||||||
app:srcCompat="@drawable/background_splash" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/circleText"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:ellipsize="none"
|
|
||||||
android:gravity="center"
|
|
||||||
android:singleLine="true"
|
|
||||||
android:textColor="@color/white"
|
|
||||||
android:textIsSelectable="false"
|
|
||||||
android:textSize="20sp"
|
|
||||||
android:typeface="normal" />
|
|
||||||
</RelativeLayout>
|
|
@ -1,5 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
@ -16,81 +17,73 @@
|
|||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
tools:visibility="gone" />
|
tools:visibility="gone" />
|
||||||
|
|
||||||
<androidx.core.widget.NestedScrollView
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:id="@+id/filterView"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:fillViewport="true">
|
android:visibility="gone"
|
||||||
|
tools:visibility="visible">
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||||
android:id="@+id/filterView"
|
android:id="@+id/floatingActionButton2"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:clickable="true"
|
||||||
|
app:backgroundTint="@color/colorAccent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:rippleColor="@color/colorAccentDark"
|
||||||
|
app:srcCompat="@drawable/ic_menu_search_white_24dp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/filterTagsTitle"
|
||||||
|
style="@style/MaterialAlertDialog.MaterialComponents.Title.Text"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="24dp"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:text="@string/filter_item_tags"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
|
||||||
|
<com.google.android.material.chip.ChipGroup
|
||||||
|
android:id="@+id/tagsGroup"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:visibility="gone"
|
android:layout_marginStart="16dp"
|
||||||
tools:visibility="visible">
|
android:layout_marginTop="24dp"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/filterTagsTitle"
|
||||||
|
app:singleSelection="true">
|
||||||
|
|
||||||
<TextView
|
</com.google.android.material.chip.ChipGroup>
|
||||||
android:id="@+id/filterTagsTitle"
|
|
||||||
style="@style/MaterialAlertDialog.MaterialComponents.Title.Text"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="24dp"
|
|
||||||
android:layout_marginTop="16dp"
|
|
||||||
android:text="@string/filter_item_tags"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/filterSourcesTitle"
|
android:id="@+id/filterSourcesTitle"
|
||||||
style="@style/MaterialAlertDialog.MaterialComponents.Title.Text"
|
style="@style/MaterialAlertDialog.MaterialComponents.Title.Text"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="24dp"
|
android:layout_marginStart="24dp"
|
||||||
android:layout_marginTop="24dp"
|
android:layout_marginTop="24dp"
|
||||||
android:text="@string/filter_item_sources"
|
android:text="@string/filter_item_sources"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/tagsGroup" />
|
app:layout_constraintTop_toBottomOf="@+id/tagsGroup" />
|
||||||
|
|
||||||
|
|
||||||
<com.google.android.material.chip.ChipGroup
|
<com.google.android.material.chip.ChipGroup
|
||||||
android:id="@+id/tagsGroup"
|
android:id="@+id/sourcesGroup"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="16dp"
|
android:layout_marginStart="16dp"
|
||||||
android:layout_marginTop="24dp"
|
android:layout_marginTop="24dp"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/filterTagsTitle"
|
app:layout_constraintTop_toBottomOf="@+id/filterSourcesTitle">
|
||||||
app:singleSelection="true">
|
|
||||||
|
|
||||||
</com.google.android.material.chip.ChipGroup>
|
</com.google.android.material.chip.ChipGroup>
|
||||||
|
|
||||||
<com.google.android.material.chip.ChipGroup
|
|
||||||
android:id="@+id/sourcesGroup"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="16dp"
|
|
||||||
android:layout_marginTop="24dp"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/filterSourcesTitle">
|
|
||||||
|
|
||||||
</com.google.android.material.chip.ChipGroup>
|
|
||||||
|
|
||||||
|
|
||||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
|
||||||
android:id="@+id/floatingActionButton2"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="8dp"
|
|
||||||
android:layout_marginEnd="16dp"
|
|
||||||
android:clickable="true"
|
|
||||||
android:contentDescription="@string/menu_home_search"
|
|
||||||
android:focusable="true"
|
|
||||||
app:backgroundTint="@color/colorAccent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
app:rippleColor="@color/colorAccentDark"
|
|
||||||
app:srcCompat="@drawable/ic_menu_search_white_24dp" />
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
</androidx.core.widget.NestedScrollView>
|
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
@ -1,4 +1,5 @@
|
|||||||
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
@ -21,22 +22,10 @@
|
|||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="200dp"
|
android:layout_height="200dp"
|
||||||
android:scaleType="centerCrop"
|
android:scaleType="centerCrop"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
/>
|
||||||
<TextView
|
|
||||||
android:id="@+id/titleView"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginLeft="16dp"
|
|
||||||
android:layout_marginTop="8dp"
|
|
||||||
android:layout_marginRight="16dp"
|
|
||||||
android:textAppearance="@style/TextAppearance.AppCompat.Headline"
|
|
||||||
android:textStyle="bold"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/imageView" />
|
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/source"
|
android:id="@+id/source"
|
||||||
@ -47,23 +36,40 @@
|
|||||||
android:layout_marginRight="16dp"
|
android:layout_marginRight="16dp"
|
||||||
android:textColor="?android:textColorSecondary"
|
android:textColor="?android:textColorSecondary"
|
||||||
android:textSize="12sp"
|
android:textSize="12sp"
|
||||||
|
app:layout_constraintHorizontal_bias="0.0"
|
||||||
app:layout_constraintLeft_toLeftOf="parent"
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
app:layout_constraintRight_toRightOf="parent"
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/titleView" />
|
app:layout_constraintTop_toBottomOf="@+id/titleView" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/titleView"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginLeft="16dp"
|
||||||
|
android:layout_marginRight="16dp"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:textAppearance="@style/TextAppearance.AppCompat.Headline"
|
||||||
|
android:textStyle="bold"
|
||||||
|
app:layout_constraintHorizontal_bias="0.0"
|
||||||
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/imageView" />
|
||||||
|
|
||||||
|
|
||||||
<WebView
|
<WebView
|
||||||
android:id="@+id/webcontent"
|
android:id="@+id/webcontent"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginLeft="16dp"
|
|
||||||
android:layout_marginTop="24dp"
|
|
||||||
android:layout_marginRight="16dp"
|
|
||||||
android:background="?attr/webviewBackground"
|
|
||||||
android:paddingBottom="48dp"
|
|
||||||
android:textColorLink="?attr/colorAccent"
|
android:textColorLink="?attr/colorAccent"
|
||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
android:layout_marginLeft="16dp"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
android:layout_marginRight="16dp"
|
||||||
|
android:layout_marginTop="24dp"
|
||||||
|
android:paddingBottom="48dp"
|
||||||
|
android:background="?attr/webviewBackground"
|
||||||
|
app:layout_constraintHorizontal_bias="0.0"
|
||||||
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/source"
|
app:layout_constraintTop_toBottomOf="@+id/source"
|
||||||
tools:visibility="visible" />
|
tools:visibility="visible" />
|
||||||
|
|
||||||
@ -71,23 +77,46 @@
|
|||||||
|
|
||||||
</androidx.core.widget.NestedScrollView>
|
</androidx.core.widget.NestedScrollView>
|
||||||
|
|
||||||
<com.leinardi.android.speeddial.SpeedDialView
|
<FrameLayout
|
||||||
android:id="@+id/speedDial"
|
android:layout_width="match_parent"
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="bottom|end"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_behavior="@string/speeddial_scrolling_view_snackbar_behavior"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:sdMainFabClosedSrc="@drawable/ic_add_white_24dp" />
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
|
android:layout_gravity="end|bottom|right">
|
||||||
|
|
||||||
|
<com.github.rubensousa.floatingtoolbar.FloatingToolbar
|
||||||
|
android:id="@+id/floatingToolbar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="?attr/actionBarSize"
|
||||||
|
android:layout_gravity="bottom"
|
||||||
|
app:floatingMenu="@menu/reader_toolbar" />
|
||||||
|
|
||||||
|
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||||
|
android:id="@+id/fab"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="end|bottom|right"
|
||||||
|
android:layout_marginBottom="16dp"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:layout_marginRight="16dp"
|
||||||
|
android:paddingBottom="@dimen/activity_vertical_margin"
|
||||||
|
android:paddingTop="@dimen/activity_vertical_margin"
|
||||||
|
android:src="@drawable/ic_add_white_24dp"
|
||||||
|
app:backgroundTint="?attr/colorAccent"
|
||||||
|
app:fabSize="mini"
|
||||||
|
app:rippleColor="?attr/colorAccentDark" />
|
||||||
|
</FrameLayout>
|
||||||
|
|
||||||
<FrameLayout
|
<FrameLayout
|
||||||
android:id="@+id/progressBar"
|
android:id="@+id/progressBar"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:alpha="0.8"
|
android:visibility="gone"
|
||||||
android:animateLayoutChanges="true"
|
android:animateLayoutChanges="true"
|
||||||
|
android:alpha="0.8"
|
||||||
android:background="@color/black"
|
android:background="@color/black"
|
||||||
android:clickable="false"
|
android:clickable="false">
|
||||||
android:visibility="gone">
|
|
||||||
|
|
||||||
<ProgressBar
|
<ProgressBar
|
||||||
style="?android:attr/progressBarStyleLarge"
|
style="?android:attr/progressBarStyleLarge"
|
||||||
@ -97,5 +126,4 @@
|
|||||||
android:progressTint="?attr/colorAccent" />
|
android:progressTint="?attr/colorAccent" />
|
||||||
</FrameLayout>
|
</FrameLayout>
|
||||||
|
|
||||||
|
|
||||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
@ -1,16 +1,16 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
<com.github.chrisbanes.photoview.PhotoView
|
<com.github.chrisbanes.photoview.PhotoView
|
||||||
android:id="@+id/photoView"
|
android:id="@+id/photoView"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:layout_centerVertical="true"
|
android:layout_centerVertical="true"
|
||||||
android:adjustViewBounds="true"
|
android:adjustViewBounds="true"
|
||||||
android:background="@drawable/checkerboard"
|
android:background="@drawable/checkerboard"
|
||||||
app:srcCompat="@android:drawable/screen_background_dark" />
|
app:srcCompat="@android:drawable/screen_background_dark" />
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</RelativeLayout>
|
@ -3,18 +3,17 @@
|
|||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content">
|
android:layout_height="88dp">
|
||||||
|
|
||||||
<bou.amine.apps.readerforselfossv2.android.utils.CircleImageView
|
<ImageView
|
||||||
android:id="@+id/itemImage"
|
android:id="@+id/itemImage"
|
||||||
android:layout_width="46dp"
|
android:layout_width="46dp"
|
||||||
android:layout_height="46dp"
|
android:layout_height="46dp"
|
||||||
android:layout_marginStart="8dp"
|
android:layout_marginStart="8dp"
|
||||||
android:layout_marginTop="8dp"
|
android:layout_marginTop="21dp"
|
||||||
android:layout_marginBottom="8dp"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
android:layout_marginLeft="8dp" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/title"
|
android:id="@+id/title"
|
||||||
@ -22,35 +21,42 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="8dp"
|
android:layout_marginStart="8dp"
|
||||||
android:layout_marginTop="8dp"
|
android:layout_marginTop="8dp"
|
||||||
android:layout_marginEnd="8dp"
|
android:layout_marginEnd="16dp"
|
||||||
android:ellipsize="end"
|
android:ellipsize="end"
|
||||||
android:fontFamily="sans-serif"
|
android:fontFamily="sans-serif"
|
||||||
|
android:gravity="start"
|
||||||
android:maxLines="3"
|
android:maxLines="3"
|
||||||
android:textAlignment="viewStart"
|
android:textAlignment="viewStart"
|
||||||
android:textColor="?android:textColorPrimary"
|
android:textAllCaps="false"
|
||||||
android:textSize="16sp"
|
android:textSize="16sp"
|
||||||
android:textStyle="bold"
|
android:textStyle="bold"
|
||||||
|
android:textColor="?android:textColorPrimary"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintHorizontal_bias="0.0"
|
||||||
app:layout_constraintStart_toEndOf="@+id/itemImage"
|
app:layout_constraintStart_toEndOf="@+id/itemImage"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
tools:text="Titre" />
|
tools:text="Titre"
|
||||||
|
android:layout_marginLeft="8dp"
|
||||||
|
android:layout_marginRight="16dp" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/sourceTitleAndDate"
|
android:id="@+id/sourceTitleAndDate"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="8dp"
|
android:layout_marginStart="8dp"
|
||||||
android:layout_marginTop="16dp"
|
android:layout_marginTop="66dp"
|
||||||
android:layout_marginEnd="8dp"
|
android:layout_marginEnd="16dp"
|
||||||
android:layout_marginBottom="8dp"
|
|
||||||
android:gravity="start"
|
android:gravity="start"
|
||||||
|
android:maxLines="1"
|
||||||
android:textAlignment="viewStart"
|
android:textAlignment="viewStart"
|
||||||
android:textColor="?android:textColorPrimary"
|
|
||||||
android:textSize="14sp"
|
android:textSize="14sp"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
android:textColor="?android:textColorPrimary"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintHorizontal_bias="0.0"
|
||||||
app:layout_constraintStart_toEndOf="@+id/itemImage"
|
app:layout_constraintStart_toEndOf="@+id/itemImage"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/title"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
tools:text="Google Actualité Il y a 5h" />
|
tools:text="Google Actualité Il y a 5h"
|
||||||
|
android:layout_marginLeft="8dp"
|
||||||
|
android:layout_marginRight="16dp" />
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
@ -12,9 +12,9 @@
|
|||||||
style="@style/Widget.AppCompat.Button.Borderless"
|
style="@style/Widget.AppCompat.Button.Borderless"
|
||||||
android:layout_width="48dp"
|
android:layout_width="48dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="8dp"
|
|
||||||
android:layout_marginEnd="8dp"
|
|
||||||
android:layout_marginBottom="8dp"
|
android:layout_marginBottom="8dp"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
android:background="@drawable/ic_remove_circle_outline_black_24dp"
|
android:background="@drawable/ic_remove_circle_outline_black_24dp"
|
||||||
android:backgroundTint="?android:textColorSecondary"
|
android:backgroundTint="?android:textColorSecondary"
|
||||||
android:contentDescription="@string/remove_source"
|
android:contentDescription="@string/remove_source"
|
||||||
@ -25,52 +25,52 @@
|
|||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
app:layout_constraintVertical_bias="0.0" />
|
app:layout_constraintVertical_bias="0.0" />
|
||||||
|
|
||||||
<bou.amine.apps.readerforselfossv2.android.utils.CircleImageView
|
<ImageView
|
||||||
android:id="@+id/itemImage"
|
android:id="@+id/itemImage"
|
||||||
android:layout_width="36dp"
|
android:layout_width="36dp"
|
||||||
android:layout_height="36dp"
|
android:layout_height="36dp"
|
||||||
android:layout_marginStart="8dp"
|
|
||||||
android:layout_marginTop="8dp"
|
|
||||||
android:layout_marginBottom="8dp"
|
android:layout_marginBottom="8dp"
|
||||||
|
android:layout_marginLeft="8dp"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
android:importantForAccessibility="no"
|
android:importantForAccessibility="no"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
app:layout_constraintVertical_bias="0.0" />
|
app:layout_constraintVertical_bias="0.0" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/sourceTitle"
|
android:id="@+id/sourceTitle"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="17dp"
|
||||||
android:layout_marginStart="8dp"
|
android:layout_marginStart="8dp"
|
||||||
android:ellipsize="end"
|
android:ellipsize="end"
|
||||||
|
android:gravity="start"
|
||||||
android:maxLines="1"
|
android:maxLines="1"
|
||||||
android:textAlignment="viewStart"
|
android:textAlignment="textStart"
|
||||||
android:textColor="?android:textColorPrimary"
|
android:textColor="?android:textColorPrimary"
|
||||||
android:textSize="20sp"
|
android:textSize="13sp"
|
||||||
android:textStyle="bold"
|
|
||||||
app:layout_constraintBottom_toTopOf="@+id/errorText"
|
app:layout_constraintBottom_toTopOf="@+id/errorText"
|
||||||
app:layout_constraintEnd_toStartOf="@+id/deleteBtn"
|
app:layout_constraintEnd_toStartOf="@+id/deleteBtn"
|
||||||
app:layout_constraintStart_toEndOf="@+id/itemImage"
|
app:layout_constraintStart_toEndOf="@+id/itemImage"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
tools:text="Source title" />
|
tools:text="source title" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/errorText"
|
android:id="@+id/errorText"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="10sp"
|
|
||||||
android:layout_marginTop="8dp"
|
|
||||||
android:layout_marginBottom="8dp"
|
android:layout_marginBottom="8dp"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
android:textAppearance="@style/TextAppearance.AppCompat.Small"
|
android:textAppearance="@style/TextAppearance.AppCompat.Small"
|
||||||
android:textColor="@color/red"
|
android:textColor="@color/red"
|
||||||
android:textStyle="italic"
|
|
||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toStartOf="@+id/deleteBtn"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/itemImage"
|
app:layout_constraintTop_toBottomOf="@+id/itemImage"
|
||||||
tools:text="Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."
|
tools:text="Test"
|
||||||
tools:visibility="visible" />
|
tools:visibility="visible" />
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
@ -36,12 +36,6 @@
|
|||||||
android:orderInCategory="101"
|
android:orderInCategory="101"
|
||||||
android:title="@string/menu_home_refresh" />
|
android:title="@string/menu_home_refresh" />
|
||||||
|
|
||||||
<item
|
|
||||||
android:id="@+id/issue_tracker"
|
|
||||||
app:showAsAction="never"
|
|
||||||
android:orderInCategory="103"
|
|
||||||
android:title="@string/issue_tracker_link" />
|
|
||||||
|
|
||||||
<item android:id="@+id/action_disconnect"
|
<item android:id="@+id/action_disconnect"
|
||||||
android:title="@string/action_disconnect"
|
android:title="@string/action_disconnect"
|
||||||
android:orderInCategory="104"
|
android:orderInCategory="104"
|
||||||
|
@ -3,13 +3,6 @@
|
|||||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<item
|
|
||||||
android:id="@+id/issue_tracker"
|
|
||||||
app:showAsAction="never"
|
|
||||||
android:orderInCategory="101"
|
|
||||||
android:title="@string/issue_tracker_link" />
|
|
||||||
|
|
||||||
<item android:id="@+id/about"
|
<item android:id="@+id/about"
|
||||||
android:title="@string/action_about"
|
android:title="@string/action_about"
|
||||||
android:orderInCategory="102"
|
android:orderInCategory="102"
|
||||||
|
@ -1,6 +1,13 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/unread_action"
|
||||||
|
android:icon="@drawable/ic_baseline_white_eye_24dp"
|
||||||
|
android:title="@string/unmark"
|
||||||
|
app:showAsAction="ifRoom" />
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/open_action"
|
android:id="@+id/open_action"
|
||||||
android:icon="@drawable/ic_open_in_browser_white_24dp"
|
android:icon="@drawable/ic_open_in_browser_white_24dp"
|
@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||||
<string name="app_name">"Lector per a Selfoss"</string>
|
<string name="app_name">"Lector per a Selfoss"</string>
|
||||||
<string name="title_activity_login">"Inicia la sessió"</string>
|
<string name="title_activity_login">"Inicia la sessió"</string>
|
||||||
<string name="prompt_password">"Contrasenya"</string>
|
<string name="prompt_password">"Contrasenya"</string>
|
||||||
@ -7,7 +7,6 @@
|
|||||||
<string name="error_invalid_password">"La contrasenya és massa curta"</string>
|
<string name="error_invalid_password">"La contrasenya és massa curta"</string>
|
||||||
<string name="error_field_required">"Camp necessari"</string>
|
<string name="error_field_required">"Camp necessari"</string>
|
||||||
<string name="prompt_url">"URL"</string>
|
<string name="prompt_url">"URL"</string>
|
||||||
<string name="disable_ssl">"Disable SSL"</string>
|
|
||||||
<string name="withLoginSwitch">"Autenticació (si és necessària)"</string>
|
<string name="withLoginSwitch">"Autenticació (si és necessària)"</string>
|
||||||
<string name="login_url_problem">"Pot ser que falti una \"/\" al final de l'url."</string>
|
<string name="login_url_problem">"Pot ser que falti una \"/\" al final de l'url."</string>
|
||||||
<string name="prompt_login">"Nom d'usuari"</string>
|
<string name="prompt_login">"Nom d'usuari"</string>
|
||||||
@ -23,6 +22,13 @@
|
|||||||
<string name="wrong_infos">"Torneu a comprovar la informació."</string>
|
<string name="wrong_infos">"Torneu a comprovar la informació."</string>
|
||||||
<string name="all_posts_not_read">"No s'han llegit totes les publicacions"</string>
|
<string name="all_posts_not_read">"No s'han llegit totes les publicacions"</string>
|
||||||
<string name="all_posts_read">"S'han llegit totes les publicacions"</string>
|
<string name="all_posts_read">"S'han llegit totes les publicacions"</string>
|
||||||
|
<string name="nothing_here">"No hi ha res"</string>
|
||||||
|
<string name="tab_new">"Nou"</string>
|
||||||
|
<string name="tab_read">"Tot"</string>
|
||||||
|
<string name="tab_favs">"Preferits"</string>
|
||||||
|
<string name="action_about">"Quant a"</string>
|
||||||
|
<string name="marked_as_read">"Element llegit"</string>
|
||||||
|
<string name="marked_as_unread">"Item unread"</string>
|
||||||
<string name="undo_string">"Desfés"</string>
|
<string name="undo_string">"Desfés"</string>
|
||||||
<string name="addStringNoUrl">"Inicieu la sessió per afegir fonts."</string>
|
<string name="addStringNoUrl">"Inicieu la sessió per afegir fonts."</string>
|
||||||
<string name="cant_get_sources">"No es pot obtenir la llista de fonts."</string>
|
<string name="cant_get_sources">"No es pot obtenir la llista de fonts."</string>
|
||||||
@ -84,7 +90,7 @@
|
|||||||
<string name="pref_switch_items_caching">Guarda els elements per utilitzar-los sense connexió</string>
|
<string name="pref_switch_items_caching">Guarda els elements per utilitzar-los sense connexió</string>
|
||||||
<string name="pref_switch_update_sources">Check for new sources and tags</string>
|
<string name="pref_switch_update_sources">Check for new sources and tags</string>
|
||||||
<string name="pref_switch_update_sources_summary">Disable this if your server is receiving excessive amounts of database queries.</string>
|
<string name="pref_switch_update_sources_summary">Disable this if your server is receiving excessive amounts of database queries.</string>
|
||||||
<string name="network_connectivity_lost">"Sense connexió!"</string>
|
<string name="network_connectivity_lost">"Network connection lost"</string>
|
||||||
<string name="network_connectivity_retrieved">"Network connection is now available"</string>
|
<string name="network_connectivity_retrieved">"Network connection is now available"</string>
|
||||||
<string name="pref_switch_periodic_refresh">Sincronitza els articles</string>
|
<string name="pref_switch_periodic_refresh">Sincronitza els articles</string>
|
||||||
<string name="pref_switch_periodic_refresh_off">Els articles no se sincronitzaran en segon pla</string>
|
<string name="pref_switch_periodic_refresh_off">Els articles no se sincronitzaran en segon pla</string>
|
||||||
@ -106,6 +112,9 @@
|
|||||||
<string name="reader_text_align_left">Align left</string>
|
<string name="reader_text_align_left">Align left</string>
|
||||||
<string name="reader_text_align_justify">Justify</string>
|
<string name="reader_text_align_justify">Justify</string>
|
||||||
<string name="settings_reader_font">Reader font</string>
|
<string name="settings_reader_font">Reader font</string>
|
||||||
|
<string name="reader_static_bar_title">Static bottom bar in the article viewer</string>
|
||||||
|
<string name="reader_static_bar_on">The bottom bar will always be displayed</string>
|
||||||
|
<string name="reader_static_bar_off">The bottom bar can be shown through the floating button</string>
|
||||||
<string name="remove_source">Remove source</string>
|
<string name="remove_source">Remove source</string>
|
||||||
<string name="pref_theme_title">Light/Dark mode</string>
|
<string name="pref_theme_title">Light/Dark mode</string>
|
||||||
<string name="mode_dark">Dark mode</string>
|
<string name="mode_dark">Dark mode</string>
|
||||||
@ -114,19 +123,9 @@
|
|||||||
<string name="gdpr_dialog_title">The app does not share any personal data about you.</string>
|
<string name="gdpr_dialog_title">The app does not share any personal data about you.</string>
|
||||||
<string name="gdpr_dialog_message"><![CDATA[Crash reports sending is now enabled. It can be disabled from the settings page. Keep in mind that crash reports are essential for the app development.]]></string>
|
<string name="gdpr_dialog_message"><![CDATA[Crash reports sending is now enabled. It can be disabled from the settings page. Keep in mind that crash reports are essential for the app development.]]></string>
|
||||||
<string name="crash_toast_text">A crash occured. Sending the details to the developper.</string>
|
<string name="crash_toast_text">A crash occured. Sending the details to the developper.</string>
|
||||||
<string name="pref_switch_disable_acra">"Disable automatic bug reporting."</string>
|
<string name="pref_switch_disable_acra">"Disable automatic bug reporting. "</string>
|
||||||
<string name="menu_home_filter">Filters</string>
|
<string name="menu_home_filter">Filters</string>
|
||||||
<string name="application_selfoss_only">This app only works with a Selfoss instance, and no other RSS feed.</string>
|
<string name="application_selfoss_only">This app only works with a Selfoss instance, and no other RSS feed.</string>
|
||||||
<string name="menu_home_sources">Sources</string>
|
<string name="menu_home_sources">Sources</string>
|
||||||
<string name="update_source">Update source</string>
|
<string name="update_source">Update source</string>
|
||||||
<string name="confirm_disconnect_title">Disconnect ?</string>
|
|
||||||
<string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string>
|
|
||||||
<string name="no_browser">Can\'t open a link without a browser installed</string>
|
|
||||||
<string name="nothing_here">"No hi ha res"</string>
|
|
||||||
<string name="tab_new">"Nou"</string>
|
|
||||||
<string name="tab_read">"Tot"</string>
|
|
||||||
<string name="tab_favs">"Preferits"</string>
|
|
||||||
<string name="action_about">"Quant a"</string>
|
|
||||||
<string name="marked_as_read">"Element llegit"</string>
|
|
||||||
<string name="marked_as_unread">"Item unread"</string>
|
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -1,13 +1,12 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||||
<string name="app_name">"Reader für Selfoss"</string>
|
<string name="app_name">"Reader für selfoss"</string>
|
||||||
<string name="title_activity_login">"Anmelden"</string>
|
<string name="title_activity_login">"Anmelden"</string>
|
||||||
<string name="prompt_password">"Passwort"</string>
|
<string name="prompt_password">"Passwort"</string>
|
||||||
<string name="action_sign_in">"Fortfahren"</string>
|
<string name="action_sign_in">"Fortfahren"</string>
|
||||||
<string name="error_invalid_password">"Passwort ist nicht lang genug"</string>
|
<string name="error_invalid_password">"Passwort ist nicht lang genug"</string>
|
||||||
<string name="error_field_required">"Pflichtfeld"</string>
|
<string name="error_field_required">"Pflichtfeld"</string>
|
||||||
<string name="prompt_url">"URL"</string>
|
<string name="prompt_url">"URL"</string>
|
||||||
<string name="disable_ssl">"Disable SSL"</string>
|
|
||||||
<string name="withLoginSwitch">"Anmeldung erforderlich?"</string>
|
<string name="withLoginSwitch">"Anmeldung erforderlich?"</string>
|
||||||
<string name="login_url_problem">"Ups. Du musst eventuell ein \"/\" am Ende der URL anhängen."</string>
|
<string name="login_url_problem">"Ups. Du musst eventuell ein \"/\" am Ende der URL anhängen."</string>
|
||||||
<string name="prompt_login">"Benutzername"</string>
|
<string name="prompt_login">"Benutzername"</string>
|
||||||
@ -23,15 +22,22 @@
|
|||||||
<string name="wrong_infos">"Überprüfe deine Angaben noch einmal."</string>
|
<string name="wrong_infos">"Überprüfe deine Angaben noch einmal."</string>
|
||||||
<string name="all_posts_not_read">"Nicht alle Beiträge wurden gelesen"</string>
|
<string name="all_posts_not_read">"Nicht alle Beiträge wurden gelesen"</string>
|
||||||
<string name="all_posts_read">"Alle Beiträge wurden gelesen"</string>
|
<string name="all_posts_read">"Alle Beiträge wurden gelesen"</string>
|
||||||
|
<string name="nothing_here">"Keine Einträge vorhanden"</string>
|
||||||
|
<string name="tab_new">"Neu"</string>
|
||||||
|
<string name="tab_read">"Alle"</string>
|
||||||
|
<string name="tab_favs">"Favoriten"</string>
|
||||||
|
<string name="action_about">"Über"</string>
|
||||||
|
<string name="marked_as_read">"Artikel gelesen"</string>
|
||||||
|
<string name="marked_as_unread">"Item unread"</string>
|
||||||
<string name="undo_string">"Rückgängig"</string>
|
<string name="undo_string">"Rückgängig"</string>
|
||||||
<string name="addStringNoUrl">"Melde dich an um Quellen hinzuzufügen."</string>
|
<string name="addStringNoUrl">"Melde dich an um Quellen hinzuzufügen."</string>
|
||||||
<string name="cant_get_sources">"Quellen können nicht abgerufen werden."</string>
|
<string name="cant_get_sources">"Quellen können nicht abgerufen werden."</string>
|
||||||
<string name="cant_create_source">"Quelle kann nicht gespeichert werden."</string>
|
<string name="cant_create_source">"Quelle kann nicht gespeichert werden."</string>
|
||||||
<string name="cant_get_spouts_no_network">"Fehler beim Laden der Spouts-Liste aufgrund von Netzwerkproblemen."</string>
|
<string name="cant_get_spouts_no_network">"Can't get spouts list because of a network issue."</string>
|
||||||
<string name="cant_get_spouts">"Fehler beim Laden der Spouts-Liste, möglicherweise aufgrund eines API-Fehlers."</string>
|
<string name="cant_get_spouts">"Can't get spouts list. There may ben an api issue."</string>
|
||||||
<string name="form_not_complete">"Das Formular ist nicht vollständig"</string>
|
<string name="form_not_complete">"Das Formular ist nicht vollständig"</string>
|
||||||
<string name="pref_header_links">"Links"</string>
|
<string name="pref_header_links">"Links"</string>
|
||||||
<string name="issue_tracker_link">"Ticketsystem"</string>
|
<string name="issue_tracker_link">"Issue Tracker"</string>
|
||||||
<string name="issue_tracker_summary">"Melde einen Bug oder rege ein neues Feature an"</string>
|
<string name="issue_tracker_summary">"Melde einen Bug oder rege ein neues Feature an"</string>
|
||||||
<string name="warning_wrong_url">"WARNUNG"</string>
|
<string name="warning_wrong_url">"WARNUNG"</string>
|
||||||
<string name="pref_switch_card_view_title">"Kachelansicht"</string>
|
<string name="pref_switch_card_view_title">"Kachelansicht"</string>
|
||||||
@ -58,75 +64,68 @@
|
|||||||
<string name="filter_item_tags">Tags</string>
|
<string name="filter_item_tags">Tags</string>
|
||||||
<string name="filter_item_sources">Quellen</string>
|
<string name="filter_item_sources">Quellen</string>
|
||||||
<string name="menu_home_search">Suche</string>
|
<string name="menu_home_search">Suche</string>
|
||||||
<string name="can_delete_source">Quelle konnte nicht gelöscht werden…</string>
|
<string name="can_delete_source">Can\'t delete the source…</string>
|
||||||
<string name="base_url_error">Beim Versuch deine Selfoss-Instanz zu erreichen ist ein Fehler aufgetreten. Solltet dieser Fehler bestehen bleiben, trete bitte mit mir in Kontakt.</string>
|
<string name="base_url_error">Beim Versuch deine Selfoss-Instanz zu erreichen ist ein Fehler aufgetreten. Solltet dieser Fehler bestehen bleiben, trete bitte mit mir in Kontakt.</string>
|
||||||
<string name="pref_header_theme">Designs</string>
|
<string name="pref_header_theme">Designs</string>
|
||||||
<string name="pref_selfoss_category">selfoss API</string>
|
<string name="pref_selfoss_category">selfoss API</string>
|
||||||
<string name="pref_api_items_number_title">Anzahl der zu ladenden Artikel</string>
|
<string name="pref_api_items_number_title">Loaded items number</string>
|
||||||
<string name="pref_general_infinite_loading_title">Weitere Artikel beim Navigieren laden</string>
|
<string name="pref_general_infinite_loading_title">Load more articles on scroll</string>
|
||||||
<string name="translation">Übersetzung</string>
|
<string name="translation">Übersetzung</string>
|
||||||
<string name="cant_open_invalid_url">Der Artikel-Link ist ungültig. Ich such nach einer Lösung dieses Problems, damit die App nicht abstürzt.</string>
|
<string name="cant_open_invalid_url">The item url is invalid. I\'m looking into solving this issue so the app won\'t crash.</string>
|
||||||
<string name="items_number_should_be_number">Die Anzahl der Artikel sollte eine Ganzzahl sein.</string>
|
<string name="items_number_should_be_number">The items number should be an integer.</string>
|
||||||
<string name="reader_action_open">Im Browser öffnen</string>
|
<string name="reader_action_open">Im Browser öffnen</string>
|
||||||
<string name="reader_action_share">Teilen</string>
|
<string name="reader_action_share">Teilen</string>
|
||||||
<string name="pref_switch_actions_pager_scroll_on">Artikel als gelesen markieren, wenn zwischen den Artikeln gewischt wird.</string>
|
<string name="pref_switch_actions_pager_scroll_on">Mark articles as read when swiping between articles.</string>
|
||||||
<string name="add_to_favs_reader">Zu Favoriten hinzufügen</string>
|
<string name="add_to_favs_reader">Zu Favoriten hinzufügen</string>
|
||||||
<string name="pref_content_reader_font_size">Schriftgröße im Lesemodus</string>
|
<string name="pref_content_reader_font_size">Article reader content font size</string>
|
||||||
<string name="pref_header_viewer">Lesemodus</string>
|
<string name="pref_header_viewer">Article viewer</string>
|
||||||
<string name="refresh_dialog_message">Dies wird die Selfoss-Instanz aktualisieren.</string>
|
<string name="refresh_dialog_message">This will refresh your Selfoss instance.</string>
|
||||||
<string name="markall_dialog_message">Dies wird alle Elemente als gelesen markieren.</string>
|
<string name="markall_dialog_message">Dies wird alle Elemente als gelesen markieren.</string>
|
||||||
<string name="pref_switch_actions_pager_scroll">Beim Wischen als gelesen markieren</string>
|
<string name="pref_switch_actions_pager_scroll">Beim Wischen als gelesen markieren</string>
|
||||||
<string name="pref_switch_actions_pager_scroll_off">Artikel nicht als gelesen markieren, wenn zwischen den Artikeln gewischt wird.</string>
|
<string name="pref_switch_actions_pager_scroll_off">Don\'t mark articles as read when swiping.</string>
|
||||||
<string name="unmark">Eintrag als ungelesen markieren</string>
|
<string name="unmark">Eintrag als ungelesen markieren</string>
|
||||||
<string name="pref_header_offline">Offline-Modus und Cache</string>
|
<string name="pref_header_offline">Offline and cache</string>
|
||||||
<string name="pref_switch_items_caching_off">Artikel werden nicht lokal zwischengespeichert wodurch die App nicht offline nutzbar ist.</string>
|
<string name="pref_switch_items_caching_off">Articles won\'t be saved to the device memory, and the app won\'t be usable offline.</string>
|
||||||
<string name="pref_switch_items_caching_on">Artikel werden lokal zwischengespeichert wodurch die App offline nutzbar ist.</string>
|
<string name="pref_switch_items_caching_on">Articles will be saved to the device memory and will be used for offline use.</string>
|
||||||
<string name="pref_switch_items_caching">Artikel lokal zwischenspeichern</string>
|
<string name="pref_switch_items_caching">Save items for offline use</string>
|
||||||
<string name="pref_switch_update_sources">Nach neuen Quellen und Tags suchen</string>
|
<string name="pref_switch_update_sources">Check for new sources and tags</string>
|
||||||
<string name="pref_switch_update_sources_summary">Diese Funktion sollte deaktiviert werden, wenn der Server übermäßig viele Datenbankanfragen erhält.</string>
|
<string name="pref_switch_update_sources_summary">Disable this if your server is receiving excessive amounts of database queries.</string>
|
||||||
<string name="network_connectivity_lost">"Die Netzwerkverbindung wurde unterbrochen"</string>
|
<string name="network_connectivity_lost">"Die Netzwerkverbindung wurde unterbrochen"</string>
|
||||||
<string name="network_connectivity_retrieved">"Netzwerkverbindung ist jetzt verfügbar"</string>
|
<string name="network_connectivity_retrieved">"Netzwerkverbindung ist jetzt verfügbar"</string>
|
||||||
<string name="pref_switch_periodic_refresh">Synchronisiere Artikel</string>
|
<string name="pref_switch_periodic_refresh">Synchronisiere Artikel</string>
|
||||||
<string name="pref_switch_periodic_refresh_off">Artikel werden nicht im Hintergrund synchronisiert</string>
|
<string name="pref_switch_periodic_refresh_off">Artikel werden nicht im Hintergrund synchronisiert</string>
|
||||||
<string name="pref_switch_periodic_refresh_on">Die Artikel werden regelmäßig synchronisiert</string>
|
<string name="pref_switch_periodic_refresh_on">Die Artikel werden regelmäßig synchronisiert</string>
|
||||||
<string name="pref_periodic_refresh_minutes_title"><![CDATA[Aktualisierungsintervall (>= 15 Minuten)]]></string>
|
<string name="pref_periodic_refresh_minutes_title"><![CDATA[Sync interval ( >= 15 minutes)]]></string>
|
||||||
<string name="pref_switch_refresh_when_charging">Nur aktualisieren, wenn das Telefon aufgeladen wird</string>
|
<string name="pref_switch_refresh_when_charging">Nur aktualisieren, wenn das Telefon aufgeladen wird</string>
|
||||||
<string name="loading_notification_title">Lädt…</string>
|
<string name="loading_notification_title">Lädt…</string>
|
||||||
<string name="loading_notification_text">Selfoss synchronisiert Ihre Artikel</string>
|
<string name="loading_notification_text">Selfoss synchronisiert Ihre Artikel</string>
|
||||||
<string name="notification_channel_sync">Synchronisationsbenachrichtigung</string>
|
<string name="notification_channel_sync">Sync notification</string>
|
||||||
<string name="new_items_channel_sync">Benachrichtigung bei neuen Artikeln</string>
|
<string name="new_items_channel_sync">New items notification</string>
|
||||||
<string name="new_items_notification_title">Neue Artikel!</string>
|
<string name="new_items_notification_title">New items !</string>
|
||||||
<string name="new_items_notification_text">%1$d neue Artikel geladen.</string>
|
<string name="new_items_notification_text">%1$d new items loaded.</string>
|
||||||
<string name="pref_switch_notify_new_items">Benachrichtigung bei neuen Artikeln</string>
|
<string name="pref_switch_notify_new_items">Notify on new items synced.</string>
|
||||||
<string name="shortcut_offline">Offline</string>
|
<string name="shortcut_offline">Offline</string>
|
||||||
<string name="pref_api_timeout">API-Zeitüberschreitung</string>
|
<string name="pref_api_timeout">API-Zeitüberschreitung</string>
|
||||||
<string name="pref_header_experimental">Experimentell</string>
|
<string name="pref_header_experimental">Experimentell</string>
|
||||||
<string name="webview_dialog_issue_message">Webview ist nicht verfügbar. Deaktiviere den Lesemodus, um zukünftige Abstürze zu vermeiden. Lade von nun an die Nachrichten in deinen Browser.</string>
|
<string name="webview_dialog_issue_message">Webview not available. Disabling the article viewer to avoid any future crashes. Will load articles inside of your browser from now on.</string>
|
||||||
<string name="webview_dialog_issue_title">Webview-Probleme</string>
|
<string name="webview_dialog_issue_title">Webview issue</string>
|
||||||
<string name="reader_text_align_left">Linksbündig</string>
|
<string name="reader_text_align_left">Align left</string>
|
||||||
<string name="reader_text_align_justify">Blocksatz</string>
|
<string name="reader_text_align_justify">Justify</string>
|
||||||
<string name="settings_reader_font">Schriftgröße im Lesemodus</string>
|
<string name="settings_reader_font">Reader font</string>
|
||||||
<string name="remove_source">Quelle entfernen</string>
|
<string name="reader_static_bar_title">Static bottom bar in the article viewer</string>
|
||||||
<string name="pref_theme_title">Heller/Dunkler Modus</string>
|
<string name="reader_static_bar_on">The bottom bar will always be displayed</string>
|
||||||
<string name="mode_dark">Dunkler Modus</string>
|
<string name="reader_static_bar_off">The bottom bar can be shown through the floating button</string>
|
||||||
<string name="mode_system">Systemeinstellungen übernehmen</string>
|
<string name="remove_source">Remove source</string>
|
||||||
<string name="mode_light">Heller Modus</string>
|
<string name="pref_theme_title">Light/Dark mode</string>
|
||||||
<string name="gdpr_dialog_title">Diese App teilt keine persönlichen Daten.</string>
|
<string name="mode_dark">Dark mode</string>
|
||||||
<string name="gdpr_dialog_message"><![CDATA[Das Senden von Absturzberichten ist jetzt aktiviert. Die Funktion kann auf der Einstellungsseite deaktiviert werden, beachte aber bitte, dass Absturzberichte für die Anwendungsentwicklung von entscheidender Bedeutung sind.]]></string>
|
<string name="mode_system">Follow the system setting</string>
|
||||||
<string name="crash_toast_text">Die App ist abgestürzt. Details werden an den Entwickler gesendet.</string>
|
<string name="mode_light">Light mode</string>
|
||||||
<string name="pref_switch_disable_acra">"Automatische Fehlerberichterstattung deaktivieren."</string>
|
<string name="gdpr_dialog_title">The app does not share any personal data about you.</string>
|
||||||
<string name="menu_home_filter">Filter</string>
|
<string name="gdpr_dialog_message"><![CDATA[Crash reports sending is now enabled. It can be disabled from the settings page. Keep in mind that crash reports are essential for the app development.]]></string>
|
||||||
<string name="application_selfoss_only">Diese App funktioniert nur mit einer Selfoss-Instanz, nicht mit einzelnen RSS-Feeds.</string>
|
<string name="crash_toast_text">A crash occured. Sending the details to the developper.</string>
|
||||||
<string name="menu_home_sources">Quellen</string>
|
<string name="pref_switch_disable_acra">"Disable automatic bug reporting. "</string>
|
||||||
<string name="update_source">Quelle aktualisieren</string>
|
<string name="menu_home_filter">Filters</string>
|
||||||
<string name="confirm_disconnect_title">Verbindung trennen?</string>
|
<string name="application_selfoss_only">This app only works with a Selfoss instance, and no other RSS feed.</string>
|
||||||
<string name="confirm_disconnect_description">Die Verbindung zur Selfoss-Instanz wird getrennt.</string>
|
<string name="menu_home_sources">Sources</string>
|
||||||
<string name="no_browser">Can\'t open a link without a browser installed</string>
|
<string name="update_source">Update source</string>
|
||||||
<string name="nothing_here">"Keine Einträge vorhanden"</string>
|
|
||||||
<string name="tab_new">"Neu"</string>
|
|
||||||
<string name="tab_read">"Alle"</string>
|
|
||||||
<string name="tab_favs">"Favoriten"</string>
|
|
||||||
<string name="action_about">"Über"</string>
|
|
||||||
<string name="marked_as_read">"Artikel gelesen"</string>
|
|
||||||
<string name="marked_as_unread">"Item unread"</string>
|
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||||
<string name="app_name">"Lector para Selfoss"</string>
|
<string name="app_name">"Lector para Selfoss"</string>
|
||||||
<string name="title_activity_login">"Iniciar sesión"</string>
|
<string name="title_activity_login">"Iniciar sesión"</string>
|
||||||
<string name="prompt_password">"Contraseña"</string>
|
<string name="prompt_password">"Contraseña"</string>
|
||||||
@ -7,7 +7,6 @@
|
|||||||
<string name="error_invalid_password">"La contraseña no es suficientemente larga"</string>
|
<string name="error_invalid_password">"La contraseña no es suficientemente larga"</string>
|
||||||
<string name="error_field_required">"Campo requerido"</string>
|
<string name="error_field_required">"Campo requerido"</string>
|
||||||
<string name="prompt_url">"Url"</string>
|
<string name="prompt_url">"Url"</string>
|
||||||
<string name="disable_ssl">"Disable SSL"</string>
|
|
||||||
<string name="withLoginSwitch">"Inicio de sesión requerido ?"</string>
|
<string name="withLoginSwitch">"Inicio de sesión requerido ?"</string>
|
||||||
<string name="login_url_problem">"Oops. Puede que necesite añadir un \"/\" al final de la url."</string>
|
<string name="login_url_problem">"Oops. Puede que necesite añadir un \"/\" al final de la url."</string>
|
||||||
<string name="prompt_login">"Nombre de usuario"</string>
|
<string name="prompt_login">"Nombre de usuario"</string>
|
||||||
@ -23,12 +22,19 @@
|
|||||||
<string name="wrong_infos">"Revise sus datos de nuevo."</string>
|
<string name="wrong_infos">"Revise sus datos de nuevo."</string>
|
||||||
<string name="all_posts_not_read">"No todas las publicaciones fueron leídas"</string>
|
<string name="all_posts_not_read">"No todas las publicaciones fueron leídas"</string>
|
||||||
<string name="all_posts_read">"Todas las publicaciones fueron leídas"</string>
|
<string name="all_posts_read">"Todas las publicaciones fueron leídas"</string>
|
||||||
|
<string name="nothing_here">"Nada aquí"</string>
|
||||||
|
<string name="tab_new">"Nuevo"</string>
|
||||||
|
<string name="tab_read">"Todo"</string>
|
||||||
|
<string name="tab_favs">"Favoritos"</string>
|
||||||
|
<string name="action_about">"Acerca de"</string>
|
||||||
|
<string name="marked_as_read">"Artículo leído"</string>
|
||||||
|
<string name="marked_as_unread">"Artículo no leído"</string>
|
||||||
<string name="undo_string">"Deshacer"</string>
|
<string name="undo_string">"Deshacer"</string>
|
||||||
<string name="addStringNoUrl">"Iniciar sesión para añadir fuentes."</string>
|
<string name="addStringNoUrl">"Iniciar sesión para añadir fuentes."</string>
|
||||||
<string name="cant_get_sources">"No se puede obtener la lista de fuentes."</string>
|
<string name="cant_get_sources">"No se puede obtener la lista de fuentes."</string>
|
||||||
<string name="cant_create_source">"No se puede crear la fuente."</string>
|
<string name="cant_create_source">"No se puede crear la fuente."</string>
|
||||||
<string name="cant_get_spouts_no_network">"Can't get spouts list because of a network issue."</string>
|
<string name="cant_get_spouts_no_network">"Can't get spouts list because of a network issue."</string>
|
||||||
<string name="cant_get_spouts">"No se puede obtener la lista de fuentes."</string>
|
<string name="cant_get_spouts">"Can't get spouts list. There may ben an api issue."</string>
|
||||||
<string name="form_not_complete">"El formulario no está completo"</string>
|
<string name="form_not_complete">"El formulario no está completo"</string>
|
||||||
<string name="pref_header_links">"Enlaces"</string>
|
<string name="pref_header_links">"Enlaces"</string>
|
||||||
<string name="issue_tracker_link">"Rastreador de Incidencias"</string>
|
<string name="issue_tracker_link">"Rastreador de Incidencias"</string>
|
||||||
@ -84,7 +90,7 @@
|
|||||||
<string name="pref_switch_items_caching">Guardar elementos para uso sin conexión</string>
|
<string name="pref_switch_items_caching">Guardar elementos para uso sin conexión</string>
|
||||||
<string name="pref_switch_update_sources">Check for new sources and tags</string>
|
<string name="pref_switch_update_sources">Check for new sources and tags</string>
|
||||||
<string name="pref_switch_update_sources_summary">Disable this if your server is receiving excessive amounts of database queries.</string>
|
<string name="pref_switch_update_sources_summary">Disable this if your server is receiving excessive amounts of database queries.</string>
|
||||||
<string name="network_connectivity_lost">"Sin conexión!"</string>
|
<string name="network_connectivity_lost">"Network connection lost"</string>
|
||||||
<string name="network_connectivity_retrieved">"Network connection is now available"</string>
|
<string name="network_connectivity_retrieved">"Network connection is now available"</string>
|
||||||
<string name="pref_switch_periodic_refresh">Sincronizar artículos</string>
|
<string name="pref_switch_periodic_refresh">Sincronizar artículos</string>
|
||||||
<string name="pref_switch_periodic_refresh_off">Los artículos no se sincronizarán en segundo plano</string>
|
<string name="pref_switch_periodic_refresh_off">Los artículos no se sincronizarán en segundo plano</string>
|
||||||
@ -106,6 +112,9 @@
|
|||||||
<string name="reader_text_align_left">Alinear a la izquierda</string>
|
<string name="reader_text_align_left">Alinear a la izquierda</string>
|
||||||
<string name="reader_text_align_justify">Justificado</string>
|
<string name="reader_text_align_justify">Justificado</string>
|
||||||
<string name="settings_reader_font">Modo lectura</string>
|
<string name="settings_reader_font">Modo lectura</string>
|
||||||
|
<string name="reader_static_bar_title">Static bottom bar in the article viewer</string>
|
||||||
|
<string name="reader_static_bar_on">The bottom bar will always be displayed</string>
|
||||||
|
<string name="reader_static_bar_off">The bottom bar can be shown through the floating button</string>
|
||||||
<string name="remove_source">Remove source</string>
|
<string name="remove_source">Remove source</string>
|
||||||
<string name="pref_theme_title">Light/Dark mode</string>
|
<string name="pref_theme_title">Light/Dark mode</string>
|
||||||
<string name="mode_dark">Dark mode</string>
|
<string name="mode_dark">Dark mode</string>
|
||||||
@ -114,19 +123,9 @@
|
|||||||
<string name="gdpr_dialog_title">The app does not share any personal data about you.</string>
|
<string name="gdpr_dialog_title">The app does not share any personal data about you.</string>
|
||||||
<string name="gdpr_dialog_message"><![CDATA[Crash reports sending is now enabled. It can be disabled from the settings page. Keep in mind that crash reports are essential for the app development.]]></string>
|
<string name="gdpr_dialog_message"><![CDATA[Crash reports sending is now enabled. It can be disabled from the settings page. Keep in mind that crash reports are essential for the app development.]]></string>
|
||||||
<string name="crash_toast_text">A crash occured. Sending the details to the developper.</string>
|
<string name="crash_toast_text">A crash occured. Sending the details to the developper.</string>
|
||||||
<string name="pref_switch_disable_acra">"Disable automatic bug reporting."</string>
|
<string name="pref_switch_disable_acra">"Disable automatic bug reporting. "</string>
|
||||||
<string name="menu_home_filter">Filters</string>
|
<string name="menu_home_filter">Filters</string>
|
||||||
<string name="application_selfoss_only">This app only works with a Selfoss instance, and no other RSS feed.</string>
|
<string name="application_selfoss_only">This app only works with a Selfoss instance, and no other RSS feed.</string>
|
||||||
<string name="menu_home_sources">Sources</string>
|
<string name="menu_home_sources">Sources</string>
|
||||||
<string name="update_source">Update source</string>
|
<string name="update_source">Update source</string>
|
||||||
<string name="confirm_disconnect_title">Disconnect ?</string>
|
|
||||||
<string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string>
|
|
||||||
<string name="no_browser">Can\'t open a link without a browser installed</string>
|
|
||||||
<string name="nothing_here">"Nada aquí"</string>
|
|
||||||
<string name="tab_new">"Nuevo"</string>
|
|
||||||
<string name="tab_read">"Todo"</string>
|
|
||||||
<string name="tab_favs">"Favoritos"</string>
|
|
||||||
<string name="action_about">"Acerca de"</string>
|
|
||||||
<string name="marked_as_read">"Artículo leído"</string>
|
|
||||||
<string name="marked_as_unread">"Artículo no leído"</string>
|
|
||||||
</resources>
|
</resources>
|
||||||
|
131
androidApp/src/main/res/values-fa-rIR/strings.xml
Normal file
131
androidApp/src/main/res/values-fa-rIR/strings.xml
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
<string name="app_name">"Reader for Selfoss"</string>
|
||||||
|
<string name="title_activity_login">"Log in"</string>
|
||||||
|
<string name="prompt_password">"Password"</string>
|
||||||
|
<string name="action_sign_in">"Go"</string>
|
||||||
|
<string name="error_invalid_password">"Password not long enough"</string>
|
||||||
|
<string name="error_field_required">"Field required"</string>
|
||||||
|
<string name="prompt_url">"Url"</string>
|
||||||
|
<string name="withLoginSwitch">"Login required ?"</string>
|
||||||
|
<string name="login_url_problem">"Oops. You may need to add a \"/\" at the end of the url."</string>
|
||||||
|
<string name="prompt_login">"Username"</string>
|
||||||
|
<string name="readAll">"Read all"</string>
|
||||||
|
<string name="action_disconnect">"Disconnect"</string>
|
||||||
|
<string name="title_activity_settings">"Settings"</string>
|
||||||
|
<string name="pref_header_general">"General"</string>
|
||||||
|
<string name="add_source_hint_tags">"Tag1, Tag2, Tag3"</string>
|
||||||
|
<string name="add_source_hint_url">"Link"</string>
|
||||||
|
<string name="add_source_hint_name">"Name"</string>
|
||||||
|
<string name="add_source">"Add a source"</string>
|
||||||
|
<string name="add_source_save">"Save"</string>
|
||||||
|
<string name="wrong_infos">"Check your details again."</string>
|
||||||
|
<string name="all_posts_not_read">"All posts weren't read"</string>
|
||||||
|
<string name="all_posts_read">"All posts were read"</string>
|
||||||
|
<string name="nothing_here">"Nothing here"</string>
|
||||||
|
<string name="tab_new">"New"</string>
|
||||||
|
<string name="tab_read">"All"</string>
|
||||||
|
<string name="tab_favs">"Favorites"</string>
|
||||||
|
<string name="action_about">"About"</string>
|
||||||
|
<string name="marked_as_read">"Item read"</string>
|
||||||
|
<string name="marked_as_unread">"Item unread"</string>
|
||||||
|
<string name="undo_string">"Undo"</string>
|
||||||
|
<string name="addStringNoUrl">"Log in to add sources."</string>
|
||||||
|
<string name="cant_get_sources">"Can't get sources list."</string>
|
||||||
|
<string name="cant_create_source">"Can't create source."</string>
|
||||||
|
<string name="cant_get_spouts_no_network">"Can't get spouts list because of a network issue."</string>
|
||||||
|
<string name="cant_get_spouts">"Can't get spouts list. There may ben an api issue."</string>
|
||||||
|
<string name="form_not_complete">"The form is not complete"</string>
|
||||||
|
<string name="pref_header_links">"Links"</string>
|
||||||
|
<string name="issue_tracker_link">"Issue Tracker"</string>
|
||||||
|
<string name="issue_tracker_summary">"Report a bug or ask for a new feature"</string>
|
||||||
|
<string name="warning_wrong_url">"WARNING"</string>
|
||||||
|
<string name="pref_switch_card_view_title">"Card View"</string>
|
||||||
|
<string name="share">"Share"</string>
|
||||||
|
<string name="switch_unread_count">"Display the unread count as a badge for the bottom bar."</string>
|
||||||
|
<string name="switch_unread_count_title">"Display unread count"</string>
|
||||||
|
<string name="display_all_counts_title">"Display count for favorite and read"</string>
|
||||||
|
<string name="text_wrong_url">"You seem to be trying to use an invalid URL. Make sure it is correct, and if the problem persists, contact me (via the store contact link). Please note that the app needs you to be using Selfoss. You can't access RSS feeds without it."</string>
|
||||||
|
<string name="pref_article_viewer_title">"Open links inside the app"</string>
|
||||||
|
<string name="pref_article_viewer_on">"Articles will open inside the app"</string>
|
||||||
|
<string name="pref_article_viewer_off">"Articles will open with your default browser"</string>
|
||||||
|
<string name="pref_general_category_links">"Link handling"</string>
|
||||||
|
<string name="pref_general_category_displaying">"Displaying"</string>
|
||||||
|
<string name="pref_switch_card_view_on">"The articles will be displayed as cards"</string>
|
||||||
|
<string name="pref_switch_card_view_off">"The articles will be displayed as a list"</string>
|
||||||
|
<string name="menu_home_refresh">"Update remote"</string>
|
||||||
|
<string name="refresh_success_response">"The remote is updated, you can now reload the articles list"</string>
|
||||||
|
<string name="refresh_failer_message">"The update didn't work, try again later, or check your selfoss logs."</string>
|
||||||
|
<string name="refresh_in_progress">"Refresh in progress"</string>
|
||||||
|
<string name="card_height_title">Full height cards</string>
|
||||||
|
<string name="card_height_on">Cards height will adjust to its content</string>
|
||||||
|
<string name="card_height_off">Card height will be fixed</string>
|
||||||
|
<string name="source_code">Source code</string>
|
||||||
|
<string name="filter_item_tags">Tags</string>
|
||||||
|
<string name="filter_item_sources">Sources</string>
|
||||||
|
<string name="menu_home_search">Search</string>
|
||||||
|
<string name="can_delete_source">Can\'t delete the source…</string>
|
||||||
|
<string name="base_url_error">There was an issue when trying to communicate with your Selfoss Instance. If the issue persists, please get in touch with me.</string>
|
||||||
|
<string name="pref_header_theme">Themes</string>
|
||||||
|
<string name="pref_selfoss_category">Selfoss Api</string>
|
||||||
|
<string name="pref_api_items_number_title">Loaded items number</string>
|
||||||
|
<string name="pref_general_infinite_loading_title">Load more articles on scroll</string>
|
||||||
|
<string name="translation">Translation</string>
|
||||||
|
<string name="cant_open_invalid_url">The item url is invalid. I\'m looking into solving this issue so the app won\'t crash.</string>
|
||||||
|
<string name="items_number_should_be_number">The items number should be an integer.</string>
|
||||||
|
<string name="reader_action_open">Open in browser</string>
|
||||||
|
<string name="reader_action_share">Share</string>
|
||||||
|
<string name="pref_switch_actions_pager_scroll_on">Mark articles as read when swiping between articles.</string>
|
||||||
|
<string name="add_to_favs_reader">Add to favorites</string>
|
||||||
|
<string name="pref_content_reader_font_size">Article reader content font size</string>
|
||||||
|
<string name="pref_header_viewer">Article viewer</string>
|
||||||
|
<string name="refresh_dialog_message">This will refresh your Selfoss instance.</string>
|
||||||
|
<string name="markall_dialog_message">This will mark all the items as read.</string>
|
||||||
|
<string name="pref_switch_actions_pager_scroll">Mark as read on swipe</string>
|
||||||
|
<string name="pref_switch_actions_pager_scroll_off">Don\'t mark articles as read when swiping.</string>
|
||||||
|
<string name="unmark">Mark item as unread</string>
|
||||||
|
<string name="pref_header_offline">Offline and cache</string>
|
||||||
|
<string name="pref_switch_items_caching_off">Articles won\'t be saved to the device memory, and the app won\'t be usable offline.</string>
|
||||||
|
<string name="pref_switch_items_caching_on">Articles will be saved to the device memory and will be used for offline use.</string>
|
||||||
|
<string name="pref_switch_items_caching">Save items for offline use</string>
|
||||||
|
<string name="pref_switch_update_sources">Check for new sources and tags</string>
|
||||||
|
<string name="pref_switch_update_sources_summary">Disable this if your server is receiving excessive amounts of database queries.</string>
|
||||||
|
<string name="network_connectivity_lost">"Network connection lost"</string>
|
||||||
|
<string name="network_connectivity_retrieved">"Network connection is now available"</string>
|
||||||
|
<string name="pref_switch_periodic_refresh">Sync articles</string>
|
||||||
|
<string name="pref_switch_periodic_refresh_off">Articles will not be synced in the background</string>
|
||||||
|
<string name="pref_switch_periodic_refresh_on">Articles will periodically be synced</string>
|
||||||
|
<string name="pref_periodic_refresh_minutes_title"><![CDATA[Sync interval ( >= 15 minutes)]]></string>
|
||||||
|
<string name="pref_switch_refresh_when_charging">Only refresh when phone is charging</string>
|
||||||
|
<string name="loading_notification_title">Loading …</string>
|
||||||
|
<string name="loading_notification_text">Selfoss is syncing your articles</string>
|
||||||
|
<string name="notification_channel_sync">Sync notification</string>
|
||||||
|
<string name="new_items_channel_sync">New items notification</string>
|
||||||
|
<string name="new_items_notification_title">New items !</string>
|
||||||
|
<string name="new_items_notification_text">%1$d new items loaded.</string>
|
||||||
|
<string name="pref_switch_notify_new_items">Notify on new items synced.</string>
|
||||||
|
<string name="shortcut_offline">Offline</string>
|
||||||
|
<string name="pref_api_timeout">Api Timeout</string>
|
||||||
|
<string name="pref_header_experimental">Experimental</string>
|
||||||
|
<string name="webview_dialog_issue_message">Webview not available. Disabling the article viewer to avoid any future crashes. Will load articles inside of your browser from now on.</string>
|
||||||
|
<string name="webview_dialog_issue_title">Webview issue</string>
|
||||||
|
<string name="reader_text_align_left">Align left</string>
|
||||||
|
<string name="reader_text_align_justify">Justify</string>
|
||||||
|
<string name="settings_reader_font">Reader font</string>
|
||||||
|
<string name="reader_static_bar_title">Static bottom bar in the article viewer</string>
|
||||||
|
<string name="reader_static_bar_on">The bottom bar will always be displayed</string>
|
||||||
|
<string name="reader_static_bar_off">The bottom bar can be shown through the floating button</string>
|
||||||
|
<string name="remove_source">Remove source</string>
|
||||||
|
<string name="pref_theme_title">Light/Dark mode</string>
|
||||||
|
<string name="mode_dark">Dark mode</string>
|
||||||
|
<string name="mode_system">Follow the system setting</string>
|
||||||
|
<string name="mode_light">Light mode</string>
|
||||||
|
<string name="gdpr_dialog_title">The app does not share any personal data about you.</string>
|
||||||
|
<string name="gdpr_dialog_message"><![CDATA[Crash reports sending is now enabled. It can be disabled from the settings page. Keep in mind that crash reports are essential for the app development.]]></string>
|
||||||
|
<string name="crash_toast_text">A crash occured. Sending the details to the developper.</string>
|
||||||
|
<string name="pref_switch_disable_acra">"Disable automatic bug reporting. "</string>
|
||||||
|
<string name="menu_home_filter">Filters</string>
|
||||||
|
<string name="application_selfoss_only">This app only works with a Selfoss instance, and no other RSS feed.</string>
|
||||||
|
<string name="menu_home_sources">Sources</string>
|
||||||
|
<string name="update_source">Update source</string>
|
||||||
|
</resources>
|
@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||||
<string name="app_name">"Reader for Selfoss"</string>
|
<string name="app_name">"Reader for Selfoss"</string>
|
||||||
<string name="title_activity_login">"Login"</string>
|
<string name="title_activity_login">"Login"</string>
|
||||||
<string name="prompt_password">"Mot de passe"</string>
|
<string name="prompt_password">"Mot de passe"</string>
|
||||||
@ -7,7 +7,6 @@
|
|||||||
<string name="error_invalid_password">"Mot de passe trop court"</string>
|
<string name="error_invalid_password">"Mot de passe trop court"</string>
|
||||||
<string name="error_field_required">"Champ requis"</string>
|
<string name="error_field_required">"Champ requis"</string>
|
||||||
<string name="prompt_url">"Url Selfoss"</string>
|
<string name="prompt_url">"Url Selfoss"</string>
|
||||||
<string name="disable_ssl">"Désactiver la vérification SSL"</string>
|
|
||||||
<string name="withLoginSwitch">"Avec login ?"</string>
|
<string name="withLoginSwitch">"Avec login ?"</string>
|
||||||
<string name="login_url_problem">"Petit souci. Il manque peut-être un / à la fin ?"</string>
|
<string name="login_url_problem">"Petit souci. Il manque peut-être un / à la fin ?"</string>
|
||||||
<string name="prompt_login">"Utilisateur"</string>
|
<string name="prompt_login">"Utilisateur"</string>
|
||||||
@ -23,9 +22,16 @@
|
|||||||
<string name="wrong_infos">"Vérifiez vos informations."</string>
|
<string name="wrong_infos">"Vérifiez vos informations."</string>
|
||||||
<string name="all_posts_not_read">"Tous les posts n'ont pas été lus"</string>
|
<string name="all_posts_not_read">"Tous les posts n'ont pas été lus"</string>
|
||||||
<string name="all_posts_read">"Tous les posts sont lus"</string>
|
<string name="all_posts_read">"Tous les posts sont lus"</string>
|
||||||
|
<string name="nothing_here">"Il n'y a rien ici !"</string>
|
||||||
|
<string name="tab_new">"Non lus"</string>
|
||||||
|
<string name="tab_read">"Tous"</string>
|
||||||
|
<string name="tab_favs">"Favoris"</string>
|
||||||
|
<string name="action_about">"À propos"</string>
|
||||||
|
<string name="marked_as_read">"Marqué comme lu"</string>
|
||||||
|
<string name="marked_as_unread">"Marqué comme non lu"</string>
|
||||||
<string name="undo_string">"Annuler"</string>
|
<string name="undo_string">"Annuler"</string>
|
||||||
<string name="addStringNoUrl">"Identifiez-vous pour ajouter une source."</string>
|
<string name="addStringNoUrl">"Identifiez-vous pour ajouter une source."</string>
|
||||||
<string name="cant_get_sources">"Impossible de récupérer la liste des sources."</string>
|
<string name="cant_get_sources">"Impossible de récupérer la liste des sources"</string>
|
||||||
<string name="cant_create_source">"Impossible de créer la source."</string>
|
<string name="cant_create_source">"Impossible de créer la source."</string>
|
||||||
<string name="cant_get_spouts_no_network">"Impossible d'obtenir la liste des spouts en raison d'un problème de réseau."</string>
|
<string name="cant_get_spouts_no_network">"Impossible d'obtenir la liste des spouts en raison d'un problème de réseau."</string>
|
||||||
<string name="cant_get_spouts">"Impossible d'obtenir la liste des spouts. Cela pourrait venir de l'api."</string>
|
<string name="cant_get_spouts">"Impossible d'obtenir la liste des spouts. Cela pourrait venir de l'api."</string>
|
||||||
@ -36,13 +42,13 @@
|
|||||||
<string name="warning_wrong_url">"ATTENTION"</string>
|
<string name="warning_wrong_url">"ATTENTION"</string>
|
||||||
<string name="pref_switch_card_view_title">"Vue en carte"</string>
|
<string name="pref_switch_card_view_title">"Vue en carte"</string>
|
||||||
<string name="share">"Partager"</string>
|
<string name="share">"Partager"</string>
|
||||||
<string name="switch_unread_count">"Afficher le nombre d'articles non lus sur la barre en bas de l'écran."</string>
|
<string name="switch_unread_count">"Afficher le nombre d'articles non lus sur la barre en bas de l'écran"</string>
|
||||||
<string name="switch_unread_count_title">"Afficher le nombre de non lus"</string>
|
<string name="switch_unread_count_title">"Afficher le nombre de non lus"</string>
|
||||||
<string name="display_all_counts_title">"Afficher le nombre de favoris et d'articles lus"</string>
|
<string name="display_all_counts_title">"Afficher le nombre de favoris et d'articles lus"</string>
|
||||||
<string name="text_wrong_url">"Vous semblez essayer de vous connecter avec une URL invalide. Assurez-vous que c'est la bonne, et si le problème persiste, contactez-moi via le lien du play store. Notez aussi que l'application ne peut fonctionner sans l'application web Selfoss. Vous ne pouvez pas utiliser l'application pour accéder directement aux flux RSS."</string>
|
<string name="text_wrong_url">"Vous semblez essayer de vous connecter avec une URL invalide. Assurez-vous que c'est la bonne, et si le problème persiste, contactez-moi via le lien du play store. Notez aussi que l'application ne peut fonctionner sans l'application web Selfoss. Vous ne pouvez pas utiliser l'application pour accéder directement aux flux RSS."</string>
|
||||||
<string name="pref_article_viewer_title">"Ouvrir les liens dans l'application"</string>
|
<string name="pref_article_viewer_title">"Ouvrir les liens dans l'application"</string>
|
||||||
<string name="pref_article_viewer_on">"Les articles s'ouvriront dans l'application"</string>
|
<string name="pref_article_viewer_on">"Les articles s'ouvriront dans l'application"</string>
|
||||||
<string name="pref_article_viewer_off">"Les articles s'ouvriront dans votre navigateur par défaut"</string>
|
<string name="pref_article_viewer_off">"Les articles s'ouvriront dans votre naviguateur par défaut"</string>
|
||||||
<string name="pref_general_category_links">"Gestion des liens"</string>
|
<string name="pref_general_category_links">"Gestion des liens"</string>
|
||||||
<string name="pref_general_category_displaying">"Affichage"</string>
|
<string name="pref_general_category_displaying">"Affichage"</string>
|
||||||
<string name="pref_switch_card_view_on">"Les articles seront affichés en forme de carte"</string>
|
<string name="pref_switch_card_view_on">"Les articles seront affichés en forme de carte"</string>
|
||||||
@ -59,7 +65,7 @@
|
|||||||
<string name="filter_item_sources">Sources</string>
|
<string name="filter_item_sources">Sources</string>
|
||||||
<string name="menu_home_search">Rechercher</string>
|
<string name="menu_home_search">Rechercher</string>
|
||||||
<string name="can_delete_source">Impossible de supprimer la source…</string>
|
<string name="can_delete_source">Impossible de supprimer la source…</string>
|
||||||
<string name="base_url_error">Il y a eu un souci lors de la communication avec votre instance Selfoss. Si le problème persiste, contactez-moi pour trouver une solution.</string>
|
<string name="base_url_error">Il y a eu un souci lors de la communication avec votre instance Selfoss. Si le problèmes persiste, contactez-moi pour trouver une solution.</string>
|
||||||
<string name="pref_header_theme">Thèmes</string>
|
<string name="pref_header_theme">Thèmes</string>
|
||||||
<string name="pref_selfoss_category">Api Selfoss</string>
|
<string name="pref_selfoss_category">Api Selfoss</string>
|
||||||
<string name="pref_api_items_number_title">Nombre d\'articles chargés</string>
|
<string name="pref_api_items_number_title">Nombre d\'articles chargés</string>
|
||||||
@ -75,7 +81,7 @@
|
|||||||
<string name="pref_header_viewer">Lecteur d\'articles</string>
|
<string name="pref_header_viewer">Lecteur d\'articles</string>
|
||||||
<string name="refresh_dialog_message">En validant, votre instance Selfoss sera mise à jour.</string>
|
<string name="refresh_dialog_message">En validant, votre instance Selfoss sera mise à jour.</string>
|
||||||
<string name="markall_dialog_message">Marquer tous les éléments comme lus ?</string>
|
<string name="markall_dialog_message">Marquer tous les éléments comme lus ?</string>
|
||||||
<string name="pref_switch_actions_pager_scroll">Marquer comme lu à la navigation</string>
|
<string name="pref_switch_actions_pager_scroll">Marquer comme lu à la navigation.</string>
|
||||||
<string name="pref_switch_actions_pager_scroll_off">Ne pas marquer les articles comme lus à la navigation.</string>
|
<string name="pref_switch_actions_pager_scroll_off">Ne pas marquer les articles comme lus à la navigation.</string>
|
||||||
<string name="unmark">Marquer l\'article comme non lu</string>
|
<string name="unmark">Marquer l\'article comme non lu</string>
|
||||||
<string name="pref_header_offline">Hors ligne et cache</string>
|
<string name="pref_header_offline">Hors ligne et cache</string>
|
||||||
@ -87,9 +93,9 @@
|
|||||||
<string name="network_connectivity_lost">"Connexion au réseau perdue"</string>
|
<string name="network_connectivity_lost">"Connexion au réseau perdue"</string>
|
||||||
<string name="network_connectivity_retrieved">"Connexion réseau de nouveau disponible"</string>
|
<string name="network_connectivity_retrieved">"Connexion réseau de nouveau disponible"</string>
|
||||||
<string name="pref_switch_periodic_refresh">Synchroniser les articles</string>
|
<string name="pref_switch_periodic_refresh">Synchroniser les articles</string>
|
||||||
<string name="pref_switch_periodic_refresh_off">Les articles ne seront pas synchronisés en arrière-plan</string>
|
<string name="pref_switch_periodic_refresh_off">Les articles ne seront pas synchronisés en arrière plan</string>
|
||||||
<string name="pref_switch_periodic_refresh_on">Articles seront périodiquement synchronisés</string>
|
<string name="pref_switch_periodic_refresh_on">Articles seront périodiquement synchronisées</string>
|
||||||
<string name="pref_periodic_refresh_minutes_title"><![CDATA[Intervalle de synchronisation ( >= 15 minutes)]]></string>
|
<string name="pref_periodic_refresh_minutes_title"><![CDATA[Interval de synchronisation ( >= 15 minutes)]]></string>
|
||||||
<string name="pref_switch_refresh_when_charging">Synchroniser uniquement lorsque le téléphone est en charge</string>
|
<string name="pref_switch_refresh_when_charging">Synchroniser uniquement lorsque le téléphone est en charge</string>
|
||||||
<string name="loading_notification_title">Chargement …</string>
|
<string name="loading_notification_title">Chargement …</string>
|
||||||
<string name="loading_notification_text">Selfoss synchronise vos articles</string>
|
<string name="loading_notification_text">Selfoss synchronise vos articles</string>
|
||||||
@ -101,32 +107,25 @@
|
|||||||
<string name="shortcut_offline">Hors ligne</string>
|
<string name="shortcut_offline">Hors ligne</string>
|
||||||
<string name="pref_api_timeout">Timeout de l\'api</string>
|
<string name="pref_api_timeout">Timeout de l\'api</string>
|
||||||
<string name="pref_header_experimental">Expérimental</string>
|
<string name="pref_header_experimental">Expérimental</string>
|
||||||
<string name="webview_dialog_issue_message">Pas de Webview disponible. Désactivation du lecteur d\'article pour éviter les futures erreurs. Les articles seront lus via votre navigateur à l\'avenir.</string>
|
<string name="webview_dialog_issue_message">Pas de Webview disponible. Désactivation du lecteur d\'article pour éviter les futures erreurs. Les articles seront lu via votre navigateur à l\'avenir.</string>
|
||||||
<string name="webview_dialog_issue_title">Problème de Webview</string>
|
<string name="webview_dialog_issue_title">Problème de Webview</string>
|
||||||
<string name="reader_text_align_left">Aligner à gauche</string>
|
<string name="reader_text_align_left">Aligner à gauche</string>
|
||||||
<string name="reader_text_align_justify">Justifier le texte</string>
|
<string name="reader_text_align_justify">Justifier le texte</string>
|
||||||
<string name="settings_reader_font">Police du lecteur d\'articles</string>
|
<string name="settings_reader_font">Police du lecteur d\'articles</string>
|
||||||
|
<string name="reader_static_bar_title">Barre statique pour le visionneur d\'articles</string>
|
||||||
|
<string name="reader_static_bar_on">La barre sera affichée</string>
|
||||||
|
<string name="reader_static_bar_off">La barre sera affichée grâce au bouton</string>
|
||||||
<string name="remove_source">Supprimer la source</string>
|
<string name="remove_source">Supprimer la source</string>
|
||||||
<string name="pref_theme_title">Thème Clair/Sombre</string>
|
<string name="pref_theme_title">Thème Clair/Sombre</string>
|
||||||
<string name="mode_dark">Thème sombre</string>
|
<string name="mode_dark">Thème sombre</string>
|
||||||
<string name="mode_system">Utiliser les paramètres système</string>
|
<string name="mode_system">Utiliser les paramètres système</string>
|
||||||
<string name="mode_light">Thème clair</string>
|
<string name="mode_light">Thème clair</string>
|
||||||
<string name="gdpr_dialog_title">L\'application ne partage aucune information personnelle.</string>
|
<string name="gdpr_dialog_title">The app does not share any personal data about you.</string>
|
||||||
<string name="gdpr_dialog_message"><![CDATA[Le rapport de plantage est activés par défaut. Il peut être désactivé depuis les paramètres de l\'application. Notez que les rapports de plantage sont essentiels pour le développement de l\'application.]]></string>
|
<string name="gdpr_dialog_message"><![CDATA[Crash reports sending is now enabled. It can be disabled from the settings page. Keep in mind that crash reports are essential for the app development.]]></string>
|
||||||
<string name="crash_toast_text">Un bug s\'est produit. Le développeur en sera informé.</string>
|
<string name="crash_toast_text">A crash occured. Sending the details to the developper.</string>
|
||||||
<string name="pref_switch_disable_acra">"Désactiver les rapports de plantage."</string>
|
<string name="pref_switch_disable_acra">"Disable automatic bug reporting. "</string>
|
||||||
<string name="menu_home_filter">Filtres</string>
|
<string name="menu_home_filter">Filters</string>
|
||||||
<string name="application_selfoss_only">Cette application ne fonctionne qu\'avec l\'api de Selfoss, et aucun autre.</string>
|
<string name="application_selfoss_only">This app only works with a Selfoss instance, and no other RSS feed.</string>
|
||||||
<string name="menu_home_sources">Sources</string>
|
<string name="menu_home_sources">Sources</string>
|
||||||
<string name="update_source">Mise à jour des sources</string>
|
<string name="update_source">Update source</string>
|
||||||
<string name="confirm_disconnect_title">Se déconnecter ?</string>
|
|
||||||
<string name="confirm_disconnect_description">Vous allez être déconnecté de votre instance Selfoss.</string>
|
|
||||||
<string name="no_browser">Can\'t open a link without a browser installed</string>
|
|
||||||
<string name="nothing_here">"Il n'y a rien ici !"</string>
|
|
||||||
<string name="tab_new">"Non lus"</string>
|
|
||||||
<string name="tab_read">"Tous"</string>
|
|
||||||
<string name="tab_favs">"Favoris"</string>
|
|
||||||
<string name="action_about">"À propos"</string>
|
|
||||||
<string name="marked_as_read">"Marqué comme lu"</string>
|
|
||||||
<string name="marked_as_unread">"Marqué comme non lu"</string>
|
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||||
<string name="app_name">"Lector para selfoss"</string>
|
<string name="app_name">"Lector para selfoss"</string>
|
||||||
<string name="title_activity_login">"Conectar"</string>
|
<string name="title_activity_login">"Conectar"</string>
|
||||||
<string name="prompt_password">"Contrasinal"</string>
|
<string name="prompt_password">"Contrasinal"</string>
|
||||||
@ -7,7 +7,6 @@
|
|||||||
<string name="error_invalid_password">"O contrasinal non é suficientemente longo"</string>
|
<string name="error_invalid_password">"O contrasinal non é suficientemente longo"</string>
|
||||||
<string name="error_field_required">"Campo requirido"</string>
|
<string name="error_field_required">"Campo requirido"</string>
|
||||||
<string name="prompt_url">"URL"</string>
|
<string name="prompt_url">"URL"</string>
|
||||||
<string name="disable_ssl">"Disable SSL"</string>
|
|
||||||
<string name="withLoginSwitch">"É preciso iniciar sesión?"</string>
|
<string name="withLoginSwitch">"É preciso iniciar sesión?"</string>
|
||||||
<string name="login_url_problem">"Ups! Pode que precises engadir un \"/\" o final da URL."</string>
|
<string name="login_url_problem">"Ups! Pode que precises engadir un \"/\" o final da URL."</string>
|
||||||
<string name="prompt_login">"Nome de usuario"</string>
|
<string name="prompt_login">"Nome de usuario"</string>
|
||||||
@ -23,12 +22,19 @@
|
|||||||
<string name="wrong_infos">"Comprobar os teus detalles de novo."</string>
|
<string name="wrong_infos">"Comprobar os teus detalles de novo."</string>
|
||||||
<string name="all_posts_not_read">"Non se leron todas as publicacións"</string>
|
<string name="all_posts_not_read">"Non se leron todas as publicacións"</string>
|
||||||
<string name="all_posts_read">"Leronse todas as publicacións"</string>
|
<string name="all_posts_read">"Leronse todas as publicacións"</string>
|
||||||
|
<string name="nothing_here">"Non hai nada aquí"</string>
|
||||||
|
<string name="tab_new">"Novo"</string>
|
||||||
|
<string name="tab_read">"Todos"</string>
|
||||||
|
<string name="tab_favs">"Favoritos"</string>
|
||||||
|
<string name="action_about">"Acerca de"</string>
|
||||||
|
<string name="marked_as_read">"Elemento lido"</string>
|
||||||
|
<string name="marked_as_unread">"Elemento non lido"</string>
|
||||||
<string name="undo_string">"Desfacer"</string>
|
<string name="undo_string">"Desfacer"</string>
|
||||||
<string name="addStringNoUrl">"Accede pra engadir fontes."</string>
|
<string name="addStringNoUrl">"Accede pra engadir fontes."</string>
|
||||||
<string name="cant_get_sources">"Non se pode obter a lista de fontes."</string>
|
<string name="cant_get_sources">"Non se pode obter a lista de fontes."</string>
|
||||||
<string name="cant_create_source">"Non se pode crear unha fonte."</string>
|
<string name="cant_create_source">"Non se pode crear unha fonte."</string>
|
||||||
<string name="cant_get_spouts_no_network">"Non se pode obter a lista de spouts por mor dun erro de rede."</string>
|
<string name="cant_get_spouts_no_network">"Can't get spouts list because of a network issue."</string>
|
||||||
<string name="cant_get_spouts">"Non se pode obter a lista de spoits. Pode que haxa algún problema coa api."</string>
|
<string name="cant_get_spouts">"Can't get spouts list. There may ben an api issue."</string>
|
||||||
<string name="form_not_complete">"O formulario non está completo"</string>
|
<string name="form_not_complete">"O formulario non está completo"</string>
|
||||||
<string name="pref_header_links">"Ligazóns"</string>
|
<string name="pref_header_links">"Ligazóns"</string>
|
||||||
<string name="issue_tracker_link">"Rastrexador de Incidencias"</string>
|
<string name="issue_tracker_link">"Rastrexador de Incidencias"</string>
|
||||||
@ -106,27 +112,20 @@
|
|||||||
<string name="reader_text_align_left">Aliñar á esquerda</string>
|
<string name="reader_text_align_left">Aliñar á esquerda</string>
|
||||||
<string name="reader_text_align_justify">Xustificado</string>
|
<string name="reader_text_align_justify">Xustificado</string>
|
||||||
<string name="settings_reader_font">Modo lector</string>
|
<string name="settings_reader_font">Modo lector</string>
|
||||||
|
<string name="reader_static_bar_title">Barra inferior estática na vista de artigos</string>
|
||||||
|
<string name="reader_static_bar_on">A barra inferior mostrarase sempre</string>
|
||||||
|
<string name="reader_static_bar_off">A barra inferior pode mostrarse a través do botón flotante</string>
|
||||||
<string name="remove_source">Eliminar fonte</string>
|
<string name="remove_source">Eliminar fonte</string>
|
||||||
<string name="pref_theme_title">Modo Claro/Escuro</string>
|
<string name="pref_theme_title">Light/Dark mode</string>
|
||||||
<string name="mode_dark">Modo escuro</string>
|
<string name="mode_dark">Dark mode</string>
|
||||||
<string name="mode_system">Seguir axustes do sistema</string>
|
<string name="mode_system">Follow the system setting</string>
|
||||||
<string name="mode_light">Modo claro</string>
|
<string name="mode_light">Light mode</string>
|
||||||
<string name="gdpr_dialog_title">A aplicación non comparte ningún dato persoal seu.</string>
|
<string name="gdpr_dialog_title">The app does not share any personal data about you.</string>
|
||||||
<string name="gdpr_dialog_message"><![CDATA[O envío de informes de erros está habilitado. Pode deshabilitarse dende a páxina de axustes. Ten en conta que os informes de erros son esenciais para o desenvolvemento da aplicación.]]></string>
|
<string name="gdpr_dialog_message"><![CDATA[Crash reports sending is now enabled. It can be disabled from the settings page. Keep in mind that crash reports are essential for the app development.]]></string>
|
||||||
<string name="crash_toast_text">Ocurriu un erro. Enviando os detalles o desenvolvedor.</string>
|
<string name="crash_toast_text">A crash occured. Sending the details to the developper.</string>
|
||||||
<string name="pref_switch_disable_acra">"Deshabilitar o reporte automático de erros."</string>
|
<string name="pref_switch_disable_acra">"Disable automatic bug reporting. "</string>
|
||||||
<string name="menu_home_filter">Filtros</string>
|
<string name="menu_home_filter">Filters</string>
|
||||||
<string name="application_selfoss_only">Esta aplicación só funciona cunha instancia de Selfoss, e con ningún outro filtro RSS.</string>
|
<string name="application_selfoss_only">This app only works with a Selfoss instance, and no other RSS feed.</string>
|
||||||
<string name="menu_home_sources">Sources</string>
|
<string name="menu_home_sources">Sources</string>
|
||||||
<string name="update_source">Update source</string>
|
<string name="update_source">Update source</string>
|
||||||
<string name="confirm_disconnect_title">Disconnect ?</string>
|
|
||||||
<string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string>
|
|
||||||
<string name="no_browser">Can\'t open a link without a browser installed</string>
|
|
||||||
<string name="nothing_here">"Non hai nada aquí"</string>
|
|
||||||
<string name="tab_new">"Novo"</string>
|
|
||||||
<string name="tab_read">"Todos"</string>
|
|
||||||
<string name="tab_favs">"Favoritos"</string>
|
|
||||||
<string name="action_about">"Acerca de"</string>
|
|
||||||
<string name="marked_as_read">"Elemento lido"</string>
|
|
||||||
<string name="marked_as_unread">"Elemento non lido"</string>
|
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||||
<string name="app_name">"Reader for Selfoss"</string>
|
<string name="app_name">"Reader for Selfoss"</string>
|
||||||
<string name="title_activity_login">"Masuk"</string>
|
<string name="title_activity_login">"Masuk"</string>
|
||||||
<string name="prompt_password">"Kata sandi"</string>
|
<string name="prompt_password">"Kata sandi"</string>
|
||||||
@ -7,7 +7,6 @@
|
|||||||
<string name="error_invalid_password">"Kata sandinya tidak cukup panjang"</string>
|
<string name="error_invalid_password">"Kata sandinya tidak cukup panjang"</string>
|
||||||
<string name="error_field_required">"Kolom wajib diisi"</string>
|
<string name="error_field_required">"Kolom wajib diisi"</string>
|
||||||
<string name="prompt_url">"URL"</string>
|
<string name="prompt_url">"URL"</string>
|
||||||
<string name="disable_ssl">"Disable SSL"</string>
|
|
||||||
<string name="withLoginSwitch">"Harus masuk?"</string>
|
<string name="withLoginSwitch">"Harus masuk?"</string>
|
||||||
<string name="login_url_problem">"Ups. Anda mungkin harus menambahkan \"/\" di akhir url."</string>
|
<string name="login_url_problem">"Ups. Anda mungkin harus menambahkan \"/\" di akhir url."</string>
|
||||||
<string name="prompt_login">"Nama pengguna"</string>
|
<string name="prompt_login">"Nama pengguna"</string>
|
||||||
@ -23,12 +22,19 @@
|
|||||||
<string name="wrong_infos">"Periksa kembali detail Anda."</string>
|
<string name="wrong_infos">"Periksa kembali detail Anda."</string>
|
||||||
<string name="all_posts_not_read">"Semua pos belum dibaca"</string>
|
<string name="all_posts_not_read">"Semua pos belum dibaca"</string>
|
||||||
<string name="all_posts_read">"Semua pos sudah dibaca"</string>
|
<string name="all_posts_read">"Semua pos sudah dibaca"</string>
|
||||||
|
<string name="nothing_here">"Tidak ada di sini"</string>
|
||||||
|
<string name="tab_new">"Baru"</string>
|
||||||
|
<string name="tab_read">"Semua"</string>
|
||||||
|
<string name="tab_favs">"Favorit"</string>
|
||||||
|
<string name="action_about">"Tentang"</string>
|
||||||
|
<string name="marked_as_read">"Membaca item"</string>
|
||||||
|
<string name="marked_as_unread">"Item unread"</string>
|
||||||
<string name="undo_string">"Urung"</string>
|
<string name="undo_string">"Urung"</string>
|
||||||
<string name="addStringNoUrl">"Masuk untuk menambah sumber."</string>
|
<string name="addStringNoUrl">"Masuk untuk menambah sumber."</string>
|
||||||
<string name="cant_get_sources">"Tidak bisa mendapatkan daftar sumber."</string>
|
<string name="cant_get_sources">"Tidak bisa mendapatkan daftar sumber."</string>
|
||||||
<string name="cant_create_source">"Tidak dapat membuat sumber."</string>
|
<string name="cant_create_source">"Tidak dapat membuat sumber."</string>
|
||||||
<string name="cant_get_spouts_no_network">"Can't get spouts list because of a network issue."</string>
|
<string name="cant_get_spouts_no_network">"Can't get spouts list because of a network issue."</string>
|
||||||
<string name="cant_get_spouts">"Tidak bisa masuk ke daftar Spouts."</string>
|
<string name="cant_get_spouts">"Can't get spouts list. There may ben an api issue."</string>
|
||||||
<string name="form_not_complete">"Formulirnya belum selesai"</string>
|
<string name="form_not_complete">"Formulirnya belum selesai"</string>
|
||||||
<string name="pref_header_links">"Tautan"</string>
|
<string name="pref_header_links">"Tautan"</string>
|
||||||
<string name="issue_tracker_link">"Pelacak Masalah"</string>
|
<string name="issue_tracker_link">"Pelacak Masalah"</string>
|
||||||
@ -84,7 +90,7 @@
|
|||||||
<string name="pref_switch_items_caching">Save items for offline use</string>
|
<string name="pref_switch_items_caching">Save items for offline use</string>
|
||||||
<string name="pref_switch_update_sources">Check for new sources and tags</string>
|
<string name="pref_switch_update_sources">Check for new sources and tags</string>
|
||||||
<string name="pref_switch_update_sources_summary">Disable this if your server is receiving excessive amounts of database queries.</string>
|
<string name="pref_switch_update_sources_summary">Disable this if your server is receiving excessive amounts of database queries.</string>
|
||||||
<string name="network_connectivity_lost">"Koneksi jaringan hilang"</string>
|
<string name="network_connectivity_lost">"Network connection lost"</string>
|
||||||
<string name="network_connectivity_retrieved">"Network connection is now available"</string>
|
<string name="network_connectivity_retrieved">"Network connection is now available"</string>
|
||||||
<string name="pref_switch_periodic_refresh">Sync articles</string>
|
<string name="pref_switch_periodic_refresh">Sync articles</string>
|
||||||
<string name="pref_switch_periodic_refresh_off">Articles will not be synced in the background</string>
|
<string name="pref_switch_periodic_refresh_off">Articles will not be synced in the background</string>
|
||||||
@ -106,6 +112,9 @@
|
|||||||
<string name="reader_text_align_left">Align left</string>
|
<string name="reader_text_align_left">Align left</string>
|
||||||
<string name="reader_text_align_justify">Justify</string>
|
<string name="reader_text_align_justify">Justify</string>
|
||||||
<string name="settings_reader_font">Reader font</string>
|
<string name="settings_reader_font">Reader font</string>
|
||||||
|
<string name="reader_static_bar_title">Static bottom bar in the article viewer</string>
|
||||||
|
<string name="reader_static_bar_on">The bottom bar will always be displayed</string>
|
||||||
|
<string name="reader_static_bar_off">The bottom bar can be shown through the floating button</string>
|
||||||
<string name="remove_source">Remove source</string>
|
<string name="remove_source">Remove source</string>
|
||||||
<string name="pref_theme_title">Light/Dark mode</string>
|
<string name="pref_theme_title">Light/Dark mode</string>
|
||||||
<string name="mode_dark">Dark mode</string>
|
<string name="mode_dark">Dark mode</string>
|
||||||
@ -114,19 +123,9 @@
|
|||||||
<string name="gdpr_dialog_title">The app does not share any personal data about you.</string>
|
<string name="gdpr_dialog_title">The app does not share any personal data about you.</string>
|
||||||
<string name="gdpr_dialog_message"><![CDATA[Crash reports sending is now enabled. It can be disabled from the settings page. Keep in mind that crash reports are essential for the app development.]]></string>
|
<string name="gdpr_dialog_message"><![CDATA[Crash reports sending is now enabled. It can be disabled from the settings page. Keep in mind that crash reports are essential for the app development.]]></string>
|
||||||
<string name="crash_toast_text">A crash occured. Sending the details to the developper.</string>
|
<string name="crash_toast_text">A crash occured. Sending the details to the developper.</string>
|
||||||
<string name="pref_switch_disable_acra">"Disable automatic bug reporting."</string>
|
<string name="pref_switch_disable_acra">"Disable automatic bug reporting. "</string>
|
||||||
<string name="menu_home_filter">Filters</string>
|
<string name="menu_home_filter">Filters</string>
|
||||||
<string name="application_selfoss_only">This app only works with a Selfoss instance, and no other RSS feed.</string>
|
<string name="application_selfoss_only">This app only works with a Selfoss instance, and no other RSS feed.</string>
|
||||||
<string name="menu_home_sources">Sources</string>
|
<string name="menu_home_sources">Sources</string>
|
||||||
<string name="update_source">Update source</string>
|
<string name="update_source">Update source</string>
|
||||||
<string name="confirm_disconnect_title">Disconnect ?</string>
|
|
||||||
<string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string>
|
|
||||||
<string name="no_browser">Can\'t open a link without a browser installed</string>
|
|
||||||
<string name="nothing_here">"Tidak ada di sini"</string>
|
|
||||||
<string name="tab_new">"Baru"</string>
|
|
||||||
<string name="tab_read">"Semua"</string>
|
|
||||||
<string name="tab_favs">"Favorit"</string>
|
|
||||||
<string name="action_about">"Tentang"</string>
|
|
||||||
<string name="marked_as_read">"Membaca item"</string>
|
|
||||||
<string name="marked_as_unread">"Item unread"</string>
|
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||||
<string name="app_name">"Lettore RSS per Selfoss"</string>
|
<string name="app_name">"Lettore RSS per Selfoss"</string>
|
||||||
<string name="title_activity_login">"Accedi"</string>
|
<string name="title_activity_login">"Accedi"</string>
|
||||||
<string name="prompt_password">"Password"</string>
|
<string name="prompt_password">"Password"</string>
|
||||||
@ -7,7 +7,6 @@
|
|||||||
<string name="error_invalid_password">"La password non è sufficientemente lunga"</string>
|
<string name="error_invalid_password">"La password non è sufficientemente lunga"</string>
|
||||||
<string name="error_field_required">"Campo obbligatorio"</string>
|
<string name="error_field_required">"Campo obbligatorio"</string>
|
||||||
<string name="prompt_url">"URL"</string>
|
<string name="prompt_url">"URL"</string>
|
||||||
<string name="disable_ssl">"Disable SSL"</string>
|
|
||||||
<string name="withLoginSwitch">"È richiesto l'accesso?"</string>
|
<string name="withLoginSwitch">"È richiesto l'accesso?"</string>
|
||||||
<string name="login_url_problem">"Oops. Potrebbe essere necessario aggiungere un \"/\" alla fine dell'url."</string>
|
<string name="login_url_problem">"Oops. Potrebbe essere necessario aggiungere un \"/\" alla fine dell'url."</string>
|
||||||
<string name="prompt_login">"Nome utente"</string>
|
<string name="prompt_login">"Nome utente"</string>
|
||||||
@ -23,6 +22,13 @@
|
|||||||
<string name="wrong_infos">"Controlla nuovamente i dati."</string>
|
<string name="wrong_infos">"Controlla nuovamente i dati."</string>
|
||||||
<string name="all_posts_not_read">"All posts weren't read"</string>
|
<string name="all_posts_not_read">"All posts weren't read"</string>
|
||||||
<string name="all_posts_read">"Tutti i messaggi sono stati letti"</string>
|
<string name="all_posts_read">"Tutti i messaggi sono stati letti"</string>
|
||||||
|
<string name="nothing_here">"Non c'è niente qui"</string>
|
||||||
|
<string name="tab_new">"Nuovi"</string>
|
||||||
|
<string name="tab_read">"Tutti"</string>
|
||||||
|
<string name="tab_favs">"Preferiti"</string>
|
||||||
|
<string name="action_about">"Informazioni"</string>
|
||||||
|
<string name="marked_as_read">"Articolo letto"</string>
|
||||||
|
<string name="marked_as_unread">"Item unread"</string>
|
||||||
<string name="undo_string">"Annulla"</string>
|
<string name="undo_string">"Annulla"</string>
|
||||||
<string name="addStringNoUrl">"Autenticati per aggiungere fonti."</string>
|
<string name="addStringNoUrl">"Autenticati per aggiungere fonti."</string>
|
||||||
<string name="cant_get_sources">"Can't get sources list."</string>
|
<string name="cant_get_sources">"Can't get sources list."</string>
|
||||||
@ -106,6 +112,9 @@
|
|||||||
<string name="reader_text_align_left">Align left</string>
|
<string name="reader_text_align_left">Align left</string>
|
||||||
<string name="reader_text_align_justify">Justify</string>
|
<string name="reader_text_align_justify">Justify</string>
|
||||||
<string name="settings_reader_font">Reader font</string>
|
<string name="settings_reader_font">Reader font</string>
|
||||||
|
<string name="reader_static_bar_title">Static bottom bar in the article viewer</string>
|
||||||
|
<string name="reader_static_bar_on">The bottom bar will always be displayed</string>
|
||||||
|
<string name="reader_static_bar_off">The bottom bar can be shown through the floating button</string>
|
||||||
<string name="remove_source">Remove source</string>
|
<string name="remove_source">Remove source</string>
|
||||||
<string name="pref_theme_title">Light/Dark mode</string>
|
<string name="pref_theme_title">Light/Dark mode</string>
|
||||||
<string name="mode_dark">Dark mode</string>
|
<string name="mode_dark">Dark mode</string>
|
||||||
@ -114,19 +123,9 @@
|
|||||||
<string name="gdpr_dialog_title">The app does not share any personal data about you.</string>
|
<string name="gdpr_dialog_title">The app does not share any personal data about you.</string>
|
||||||
<string name="gdpr_dialog_message"><![CDATA[Crash reports sending is now enabled. It can be disabled from the settings page. Keep in mind that crash reports are essential for the app development.]]></string>
|
<string name="gdpr_dialog_message"><![CDATA[Crash reports sending is now enabled. It can be disabled from the settings page. Keep in mind that crash reports are essential for the app development.]]></string>
|
||||||
<string name="crash_toast_text">A crash occured. Sending the details to the developper.</string>
|
<string name="crash_toast_text">A crash occured. Sending the details to the developper.</string>
|
||||||
<string name="pref_switch_disable_acra">"Disable automatic bug reporting."</string>
|
<string name="pref_switch_disable_acra">"Disable automatic bug reporting. "</string>
|
||||||
<string name="menu_home_filter">Filters</string>
|
<string name="menu_home_filter">Filters</string>
|
||||||
<string name="application_selfoss_only">This app only works with a Selfoss instance, and no other RSS feed.</string>
|
<string name="application_selfoss_only">This app only works with a Selfoss instance, and no other RSS feed.</string>
|
||||||
<string name="menu_home_sources">Sources</string>
|
<string name="menu_home_sources">Sources</string>
|
||||||
<string name="update_source">Update source</string>
|
<string name="update_source">Update source</string>
|
||||||
<string name="confirm_disconnect_title">Disconnect ?</string>
|
|
||||||
<string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string>
|
|
||||||
<string name="no_browser">Can\'t open a link without a browser installed</string>
|
|
||||||
<string name="nothing_here">"Non c'è niente qui"</string>
|
|
||||||
<string name="tab_new">"Nuovi"</string>
|
|
||||||
<string name="tab_read">"Tutti"</string>
|
|
||||||
<string name="tab_favs">"Preferiti"</string>
|
|
||||||
<string name="action_about">"Informazioni"</string>
|
|
||||||
<string name="marked_as_read">"Articolo letto"</string>
|
|
||||||
<string name="marked_as_unread">"Item unread"</string>
|
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||||
<string name="app_name">"Reader for Selfoss"</string>
|
<string name="app_name">"Reader for Selfoss"</string>
|
||||||
<string name="title_activity_login">"로그인"</string>
|
<string name="title_activity_login">"로그인"</string>
|
||||||
<string name="prompt_password">"비밀번호"</string>
|
<string name="prompt_password">"비밀번호"</string>
|
||||||
@ -7,7 +7,6 @@
|
|||||||
<string name="error_invalid_password">"패스워드가 짧습니다."</string>
|
<string name="error_invalid_password">"패스워드가 짧습니다."</string>
|
||||||
<string name="error_field_required">"필수 항목"</string>
|
<string name="error_field_required">"필수 항목"</string>
|
||||||
<string name="prompt_url">"Url"</string>
|
<string name="prompt_url">"Url"</string>
|
||||||
<string name="disable_ssl">"Disable SSL"</string>
|
|
||||||
<string name="withLoginSwitch">"로그인이 필요합니까?"</string>
|
<string name="withLoginSwitch">"로그인이 필요합니까?"</string>
|
||||||
<string name="login_url_problem">"죄송합니다. Url의 끝에 \"/\"를 추가할 필요가 있습니다."</string>
|
<string name="login_url_problem">"죄송합니다. Url의 끝에 \"/\"를 추가할 필요가 있습니다."</string>
|
||||||
<string name="prompt_login">"사용자 이름"</string>
|
<string name="prompt_login">"사용자 이름"</string>
|
||||||
@ -23,12 +22,19 @@
|
|||||||
<string name="wrong_infos">"세부 정보를 다시 확인하세요."</string>
|
<string name="wrong_infos">"세부 정보를 다시 확인하세요."</string>
|
||||||
<string name="all_posts_not_read">"모든 게시물을 읽지 않았습니다."</string>
|
<string name="all_posts_not_read">"모든 게시물을 읽지 않았습니다."</string>
|
||||||
<string name="all_posts_read">"모든 게시물을 읽었습니다."</string>
|
<string name="all_posts_read">"모든 게시물을 읽었습니다."</string>
|
||||||
|
<string name="nothing_here">"비어있음"</string>
|
||||||
|
<string name="tab_new">"새로운"</string>
|
||||||
|
<string name="tab_read">"전체"</string>
|
||||||
|
<string name="tab_favs">"즐겨찾기"</string>
|
||||||
|
<string name="action_about">"정보"</string>
|
||||||
|
<string name="marked_as_read">"항목 읽기"</string>
|
||||||
|
<string name="marked_as_unread">"Item unread"</string>
|
||||||
<string name="undo_string">"실행 취소"</string>
|
<string name="undo_string">"실행 취소"</string>
|
||||||
<string name="addStringNoUrl">"로그인 소스를 추가 해야 합니다."</string>
|
<string name="addStringNoUrl">"로그인 소스를 추가 해야 합니다."</string>
|
||||||
<string name="cant_get_sources">"소스 리스트를 얻을 수 없습니다."</string>
|
<string name="cant_get_sources">"소스 리스트를 얻을 수 없습니다."</string>
|
||||||
<string name="cant_create_source">"소스를 만들 수 없습니다."</string>
|
<string name="cant_create_source">"소스를 만들 수 없습니다."</string>
|
||||||
<string name="cant_get_spouts_no_network">"Can't get spouts list because of a network issue."</string>
|
<string name="cant_get_spouts_no_network">"Can't get spouts list because of a network issue."</string>
|
||||||
<string name="cant_get_spouts">"Spouts 목록을 가져올 수 없습니다."</string>
|
<string name="cant_get_spouts">"Can't get spouts list. There may ben an api issue."</string>
|
||||||
<string name="form_not_complete">"양식이 완료되지 않았습니다."</string>
|
<string name="form_not_complete">"양식이 완료되지 않았습니다."</string>
|
||||||
<string name="pref_header_links">"링크"</string>
|
<string name="pref_header_links">"링크"</string>
|
||||||
<string name="issue_tracker_link">"이슈 트래커"</string>
|
<string name="issue_tracker_link">"이슈 트래커"</string>
|
||||||
@ -106,6 +112,9 @@
|
|||||||
<string name="reader_text_align_left">Align left</string>
|
<string name="reader_text_align_left">Align left</string>
|
||||||
<string name="reader_text_align_justify">Justify</string>
|
<string name="reader_text_align_justify">Justify</string>
|
||||||
<string name="settings_reader_font">Reader font</string>
|
<string name="settings_reader_font">Reader font</string>
|
||||||
|
<string name="reader_static_bar_title">Static bottom bar in the article viewer</string>
|
||||||
|
<string name="reader_static_bar_on">The bottom bar will always be displayed</string>
|
||||||
|
<string name="reader_static_bar_off">The bottom bar can be shown through the floating button</string>
|
||||||
<string name="remove_source">Remove source</string>
|
<string name="remove_source">Remove source</string>
|
||||||
<string name="pref_theme_title">Light/Dark mode</string>
|
<string name="pref_theme_title">Light/Dark mode</string>
|
||||||
<string name="mode_dark">Dark mode</string>
|
<string name="mode_dark">Dark mode</string>
|
||||||
@ -114,19 +123,9 @@
|
|||||||
<string name="gdpr_dialog_title">The app does not share any personal data about you.</string>
|
<string name="gdpr_dialog_title">The app does not share any personal data about you.</string>
|
||||||
<string name="gdpr_dialog_message"><![CDATA[Crash reports sending is now enabled. It can be disabled from the settings page. Keep in mind that crash reports are essential for the app development.]]></string>
|
<string name="gdpr_dialog_message"><![CDATA[Crash reports sending is now enabled. It can be disabled from the settings page. Keep in mind that crash reports are essential for the app development.]]></string>
|
||||||
<string name="crash_toast_text">A crash occured. Sending the details to the developper.</string>
|
<string name="crash_toast_text">A crash occured. Sending the details to the developper.</string>
|
||||||
<string name="pref_switch_disable_acra">"Disable automatic bug reporting."</string>
|
<string name="pref_switch_disable_acra">"Disable automatic bug reporting. "</string>
|
||||||
<string name="menu_home_filter">Filters</string>
|
<string name="menu_home_filter">Filters</string>
|
||||||
<string name="application_selfoss_only">This app only works with a Selfoss instance, and no other RSS feed.</string>
|
<string name="application_selfoss_only">This app only works with a Selfoss instance, and no other RSS feed.</string>
|
||||||
<string name="menu_home_sources">Sources</string>
|
<string name="menu_home_sources">Sources</string>
|
||||||
<string name="update_source">Update source</string>
|
<string name="update_source">Update source</string>
|
||||||
<string name="confirm_disconnect_title">Disconnect ?</string>
|
|
||||||
<string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string>
|
|
||||||
<string name="no_browser">Can\'t open a link without a browser installed</string>
|
|
||||||
<string name="nothing_here">"비어있음"</string>
|
|
||||||
<string name="tab_new">"새로운"</string>
|
|
||||||
<string name="tab_read">"전체"</string>
|
|
||||||
<string name="tab_favs">"즐겨찾기"</string>
|
|
||||||
<string name="action_about">"정보"</string>
|
|
||||||
<string name="marked_as_read">"항목 읽기"</string>
|
|
||||||
<string name="marked_as_unread">"Item unread"</string>
|
|
||||||
</resources>
|
</resources>
|
||||||
|
11
androidApp/src/main/res/values-night/strings.xml
Normal file
11
androidApp/src/main/res/values-night/strings.xml
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<string name="gdpr_dialog_title">The app does not share any personal data about you.</string>
|
||||||
|
<string name="gdpr_dialog_message"><![CDATA[Crash reports sending is now enabled. It can be disabled from the settings page. Keep in mind that crash reports are essential for the app development.]]></string>
|
||||||
|
<string name="crash_toast_text">A crash occured. Sending the details to the developper.</string>
|
||||||
|
<string name="pref_switch_disable_acra">"Disable automatic bug reporting. "</string>
|
||||||
|
<string name="menu_home_filter">Filters</string>
|
||||||
|
<string name="application_selfoss_only">This app only works with a Selfoss instance, and no other RSS feed.</string>
|
||||||
|
<string name="menu_home_sources">Sources</string>
|
||||||
|
<string name="update_source">Update source</string>
|
||||||
|
</resources>
|
@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||||
<string name="app_name">"Selfoss Reader"</string>
|
<string name="app_name">"Selfoss Reader"</string>
|
||||||
<string name="title_activity_login">"Inloggen"</string>
|
<string name="title_activity_login">"Inloggen"</string>
|
||||||
<string name="prompt_password">"Wachtwoord"</string>
|
<string name="prompt_password">"Wachtwoord"</string>
|
||||||
@ -7,7 +7,6 @@
|
|||||||
<string name="error_invalid_password">"Wachtwoord niet lang genoeg"</string>
|
<string name="error_invalid_password">"Wachtwoord niet lang genoeg"</string>
|
||||||
<string name="error_field_required">"Dit veld is verplicht"</string>
|
<string name="error_field_required">"Dit veld is verplicht"</string>
|
||||||
<string name="prompt_url">"Selfoss server"</string>
|
<string name="prompt_url">"Selfoss server"</string>
|
||||||
<string name="disable_ssl">"Disable SSL"</string>
|
|
||||||
<string name="withLoginSwitch">"Authenticatie vereist?"</string>
|
<string name="withLoginSwitch">"Authenticatie vereist?"</string>
|
||||||
<string name="login_url_problem">"Oeps, ben je soms de \"/\" vergeten aan het eind?"</string>
|
<string name="login_url_problem">"Oeps, ben je soms de \"/\" vergeten aan het eind?"</string>
|
||||||
<string name="prompt_login">"Gebruikersnaam"</string>
|
<string name="prompt_login">"Gebruikersnaam"</string>
|
||||||
@ -23,12 +22,19 @@
|
|||||||
<string name="wrong_infos">"Controleer de gegevens nogmaals."</string>
|
<string name="wrong_infos">"Controleer de gegevens nogmaals."</string>
|
||||||
<string name="all_posts_not_read">"Fout bij markeren als gelezen"</string>
|
<string name="all_posts_not_read">"Fout bij markeren als gelezen"</string>
|
||||||
<string name="all_posts_read">"Alle artikelen gemarkeerd als gelezen"</string>
|
<string name="all_posts_read">"Alle artikelen gemarkeerd als gelezen"</string>
|
||||||
|
<string name="nothing_here">"Niets gevonden"</string>
|
||||||
|
<string name="tab_new">"Nieuw"</string>
|
||||||
|
<string name="tab_read">"Alle"</string>
|
||||||
|
<string name="tab_favs">"Favorieten"</string>
|
||||||
|
<string name="action_about">"Over"</string>
|
||||||
|
<string name="marked_as_read">"Artikel gelezen"</string>
|
||||||
|
<string name="marked_as_unread">"Item unread"</string>
|
||||||
<string name="undo_string">"Ongedaan maken"</string>
|
<string name="undo_string">"Ongedaan maken"</string>
|
||||||
<string name="addStringNoUrl">"Login om bronnen toe te voegen"</string>
|
<string name="addStringNoUrl">"Login om bronnen toe te voegen"</string>
|
||||||
<string name="cant_get_sources">"Kan de lijst met bronnen niet ophalen"</string>
|
<string name="cant_get_sources">"Kan de lijst met bronnen niet ophalen"</string>
|
||||||
<string name="cant_create_source">"Kan bron niet creëeren"</string>
|
<string name="cant_create_source">"Kan bron niet creëeren"</string>
|
||||||
<string name="cant_get_spouts_no_network">"Can't get spouts list because of a network issue."</string>
|
<string name="cant_get_spouts_no_network">"Can't get spouts list because of a network issue."</string>
|
||||||
<string name="cant_get_spouts">"Ophalen spouts mislukt"</string>
|
<string name="cant_get_spouts">"Can't get spouts list. There may ben an api issue."</string>
|
||||||
<string name="form_not_complete">"Formulier is niet volledig ingevuld"</string>
|
<string name="form_not_complete">"Formulier is niet volledig ingevuld"</string>
|
||||||
<string name="pref_header_links">"Links"</string>
|
<string name="pref_header_links">"Links"</string>
|
||||||
<string name="issue_tracker_link">"Bug tracker"</string>
|
<string name="issue_tracker_link">"Bug tracker"</string>
|
||||||
@ -86,7 +92,7 @@
|
|||||||
<string name="pref_switch_update_sources_summary">Disable this if your server is receiving excessive amounts of database queries.</string>
|
<string name="pref_switch_update_sources_summary">Disable this if your server is receiving excessive amounts of database queries.</string>
|
||||||
<string name="network_connectivity_lost">"Network connection lost"</string>
|
<string name="network_connectivity_lost">"Network connection lost"</string>
|
||||||
<string name="network_connectivity_retrieved">"Network connection is now available"</string>
|
<string name="network_connectivity_retrieved">"Network connection is now available"</string>
|
||||||
<string name="pref_switch_periodic_refresh">Artikel synchronisieren</string>
|
<string name="pref_switch_periodic_refresh">Sync articles</string>
|
||||||
<string name="pref_switch_periodic_refresh_off">Articles will not be synced in the background</string>
|
<string name="pref_switch_periodic_refresh_off">Articles will not be synced in the background</string>
|
||||||
<string name="pref_switch_periodic_refresh_on">Articles will periodically be synced</string>
|
<string name="pref_switch_periodic_refresh_on">Articles will periodically be synced</string>
|
||||||
<string name="pref_periodic_refresh_minutes_title"><![CDATA[Sync interval ( >= 15 minutes)]]></string>
|
<string name="pref_periodic_refresh_minutes_title"><![CDATA[Sync interval ( >= 15 minutes)]]></string>
|
||||||
@ -106,6 +112,9 @@
|
|||||||
<string name="reader_text_align_left">Align left</string>
|
<string name="reader_text_align_left">Align left</string>
|
||||||
<string name="reader_text_align_justify">Justify</string>
|
<string name="reader_text_align_justify">Justify</string>
|
||||||
<string name="settings_reader_font">Reader font</string>
|
<string name="settings_reader_font">Reader font</string>
|
||||||
|
<string name="reader_static_bar_title">Static bottom bar in the article viewer</string>
|
||||||
|
<string name="reader_static_bar_on">The bottom bar will always be displayed</string>
|
||||||
|
<string name="reader_static_bar_off">The bottom bar can be shown through the floating button</string>
|
||||||
<string name="remove_source">Remove source</string>
|
<string name="remove_source">Remove source</string>
|
||||||
<string name="pref_theme_title">Light/Dark mode</string>
|
<string name="pref_theme_title">Light/Dark mode</string>
|
||||||
<string name="mode_dark">Dark mode</string>
|
<string name="mode_dark">Dark mode</string>
|
||||||
@ -114,19 +123,9 @@
|
|||||||
<string name="gdpr_dialog_title">The app does not share any personal data about you.</string>
|
<string name="gdpr_dialog_title">The app does not share any personal data about you.</string>
|
||||||
<string name="gdpr_dialog_message"><![CDATA[Crash reports sending is now enabled. It can be disabled from the settings page. Keep in mind that crash reports are essential for the app development.]]></string>
|
<string name="gdpr_dialog_message"><![CDATA[Crash reports sending is now enabled. It can be disabled from the settings page. Keep in mind that crash reports are essential for the app development.]]></string>
|
||||||
<string name="crash_toast_text">A crash occured. Sending the details to the developper.</string>
|
<string name="crash_toast_text">A crash occured. Sending the details to the developper.</string>
|
||||||
<string name="pref_switch_disable_acra">"Disable automatic bug reporting."</string>
|
<string name="pref_switch_disable_acra">"Disable automatic bug reporting. "</string>
|
||||||
<string name="menu_home_filter">Filters</string>
|
<string name="menu_home_filter">Filters</string>
|
||||||
<string name="application_selfoss_only">This app only works with a Selfoss instance, and no other RSS feed.</string>
|
<string name="application_selfoss_only">This app only works with a Selfoss instance, and no other RSS feed.</string>
|
||||||
<string name="menu_home_sources">Sources</string>
|
<string name="menu_home_sources">Sources</string>
|
||||||
<string name="update_source">Update source</string>
|
<string name="update_source">Update source</string>
|
||||||
<string name="confirm_disconnect_title">Disconnect ?</string>
|
|
||||||
<string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string>
|
|
||||||
<string name="no_browser">Can\'t open a link without a browser installed</string>
|
|
||||||
<string name="nothing_here">"Niets gevonden"</string>
|
|
||||||
<string name="tab_new">"Nieuw"</string>
|
|
||||||
<string name="tab_read">"Alle"</string>
|
|
||||||
<string name="tab_favs">"Favorieten"</string>
|
|
||||||
<string name="action_about">"Over"</string>
|
|
||||||
<string name="marked_as_read">"Artikel gelezen"</string>
|
|
||||||
<string name="marked_as_unread">"Item unread"</string>
|
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||||
<string name="app_name">"Reader for Selfoss"</string>
|
<string name="app_name">"Reader for Selfoss"</string>
|
||||||
<string name="title_activity_login">"Entrar"</string>
|
<string name="title_activity_login">"Entrar"</string>
|
||||||
<string name="prompt_password">"Senha"</string>
|
<string name="prompt_password">"Senha"</string>
|
||||||
@ -7,7 +7,6 @@
|
|||||||
<string name="error_invalid_password">"Senha muito pequena"</string>
|
<string name="error_invalid_password">"Senha muito pequena"</string>
|
||||||
<string name="error_field_required">"Campo obrigatório"</string>
|
<string name="error_field_required">"Campo obrigatório"</string>
|
||||||
<string name="prompt_url">"Url"</string>
|
<string name="prompt_url">"Url"</string>
|
||||||
<string name="disable_ssl">"Disable SSL"</string>
|
|
||||||
<string name="withLoginSwitch">"É necessário o login ?"</string>
|
<string name="withLoginSwitch">"É necessário o login ?"</string>
|
||||||
<string name="login_url_problem">"Oops. Talvez você precise adicionar uma \"/\" no final da url."</string>
|
<string name="login_url_problem">"Oops. Talvez você precise adicionar uma \"/\" no final da url."</string>
|
||||||
<string name="prompt_login">"Usuário"</string>
|
<string name="prompt_login">"Usuário"</string>
|
||||||
@ -23,12 +22,19 @@
|
|||||||
<string name="wrong_infos">"Verifique os detalhes novamente."</string>
|
<string name="wrong_infos">"Verifique os detalhes novamente."</string>
|
||||||
<string name="all_posts_not_read">"Nenhum post foi lido"</string>
|
<string name="all_posts_not_read">"Nenhum post foi lido"</string>
|
||||||
<string name="all_posts_read">"Todos os posts foram lidos"</string>
|
<string name="all_posts_read">"Todos os posts foram lidos"</string>
|
||||||
|
<string name="nothing_here">"Nada aqui"</string>
|
||||||
|
<string name="tab_new">"Novo"</string>
|
||||||
|
<string name="tab_read">"Todos"</string>
|
||||||
|
<string name="tab_favs">"Favoritos"</string>
|
||||||
|
<string name="action_about">"Sobre"</string>
|
||||||
|
<string name="marked_as_read">"Item lido"</string>
|
||||||
|
<string name="marked_as_unread">"Item unread"</string>
|
||||||
<string name="undo_string">"Desfazer"</string>
|
<string name="undo_string">"Desfazer"</string>
|
||||||
<string name="addStringNoUrl">"Faça login para adicionar fontes."</string>
|
<string name="addStringNoUrl">"Faça login para adicionar fontes."</string>
|
||||||
<string name="cant_get_sources">"Não é possível obter a lista de fontes."</string>
|
<string name="cant_get_sources">"Não é possível obter a lista de fontes."</string>
|
||||||
<string name="cant_create_source">"Não é possível criar fonte."</string>
|
<string name="cant_create_source">"Não é possível criar fonte."</string>
|
||||||
<string name="cant_get_spouts_no_network">"Can't get spouts list because of a network issue."</string>
|
<string name="cant_get_spouts_no_network">"Can't get spouts list because of a network issue."</string>
|
||||||
<string name="cant_get_spouts">"Não é possível obter a lista de spouts."</string>
|
<string name="cant_get_spouts">"Can't get spouts list. There may ben an api issue."</string>
|
||||||
<string name="form_not_complete">"O formulário não está completo"</string>
|
<string name="form_not_complete">"O formulário não está completo"</string>
|
||||||
<string name="pref_header_links">"Links"</string>
|
<string name="pref_header_links">"Links"</string>
|
||||||
<string name="issue_tracker_link">"Rastreador de problemas"</string>
|
<string name="issue_tracker_link">"Rastreador de problemas"</string>
|
||||||
@ -106,6 +112,9 @@
|
|||||||
<string name="reader_text_align_left">Align left</string>
|
<string name="reader_text_align_left">Align left</string>
|
||||||
<string name="reader_text_align_justify">Justify</string>
|
<string name="reader_text_align_justify">Justify</string>
|
||||||
<string name="settings_reader_font">Reader font</string>
|
<string name="settings_reader_font">Reader font</string>
|
||||||
|
<string name="reader_static_bar_title">Static bottom bar in the article viewer</string>
|
||||||
|
<string name="reader_static_bar_on">The bottom bar will always be displayed</string>
|
||||||
|
<string name="reader_static_bar_off">The bottom bar can be shown through the floating button</string>
|
||||||
<string name="remove_source">Remove source</string>
|
<string name="remove_source">Remove source</string>
|
||||||
<string name="pref_theme_title">Light/Dark mode</string>
|
<string name="pref_theme_title">Light/Dark mode</string>
|
||||||
<string name="mode_dark">Dark mode</string>
|
<string name="mode_dark">Dark mode</string>
|
||||||
@ -114,19 +123,9 @@
|
|||||||
<string name="gdpr_dialog_title">The app does not share any personal data about you.</string>
|
<string name="gdpr_dialog_title">The app does not share any personal data about you.</string>
|
||||||
<string name="gdpr_dialog_message"><![CDATA[Crash reports sending is now enabled. It can be disabled from the settings page. Keep in mind that crash reports are essential for the app development.]]></string>
|
<string name="gdpr_dialog_message"><![CDATA[Crash reports sending is now enabled. It can be disabled from the settings page. Keep in mind that crash reports are essential for the app development.]]></string>
|
||||||
<string name="crash_toast_text">A crash occured. Sending the details to the developper.</string>
|
<string name="crash_toast_text">A crash occured. Sending the details to the developper.</string>
|
||||||
<string name="pref_switch_disable_acra">"Disable automatic bug reporting."</string>
|
<string name="pref_switch_disable_acra">"Disable automatic bug reporting. "</string>
|
||||||
<string name="menu_home_filter">Filters</string>
|
<string name="menu_home_filter">Filters</string>
|
||||||
<string name="application_selfoss_only">This app only works with a Selfoss instance, and no other RSS feed.</string>
|
<string name="application_selfoss_only">This app only works with a Selfoss instance, and no other RSS feed.</string>
|
||||||
<string name="menu_home_sources">Sources</string>
|
<string name="menu_home_sources">Sources</string>
|
||||||
<string name="update_source">Update source</string>
|
<string name="update_source">Update source</string>
|
||||||
<string name="confirm_disconnect_title">Disconnect ?</string>
|
|
||||||
<string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string>
|
|
||||||
<string name="no_browser">Can\'t open a link without a browser installed</string>
|
|
||||||
<string name="nothing_here">"Nada aqui"</string>
|
|
||||||
<string name="tab_new">"Novo"</string>
|
|
||||||
<string name="tab_read">"Todos"</string>
|
|
||||||
<string name="tab_favs">"Favoritos"</string>
|
|
||||||
<string name="action_about">"Sobre"</string>
|
|
||||||
<string name="marked_as_read">"Item lido"</string>
|
|
||||||
<string name="marked_as_unread">"Item unread"</string>
|
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||||
<string name="app_name">"Leitor para Selfoss"</string>
|
<string name="app_name">"Leitor para Selfoss"</string>
|
||||||
<string name="title_activity_login">"Iniciar sessão"</string>
|
<string name="title_activity_login">"Iniciar sessão"</string>
|
||||||
<string name="prompt_password">"Palavra passe"</string>
|
<string name="prompt_password">"Palavra passe"</string>
|
||||||
@ -7,7 +7,6 @@
|
|||||||
<string name="error_invalid_password">"Senha não é longa o suficiente"</string>
|
<string name="error_invalid_password">"Senha não é longa o suficiente"</string>
|
||||||
<string name="error_field_required">"Campo obrigatório"</string>
|
<string name="error_field_required">"Campo obrigatório"</string>
|
||||||
<string name="prompt_url">"Url"</string>
|
<string name="prompt_url">"Url"</string>
|
||||||
<string name="disable_ssl">"Disable SSL"</string>
|
|
||||||
<string name="withLoginSwitch">"É necessário fazer login?"</string>
|
<string name="withLoginSwitch">"É necessário fazer login?"</string>
|
||||||
<string name="login_url_problem">"Uups. Você pode precisar adicionar uma \"/\" no final da url."</string>
|
<string name="login_url_problem">"Uups. Você pode precisar adicionar uma \"/\" no final da url."</string>
|
||||||
<string name="prompt_login">"Nome do usuário"</string>
|
<string name="prompt_login">"Nome do usuário"</string>
|
||||||
@ -23,12 +22,19 @@
|
|||||||
<string name="wrong_infos">"Verifique seus dados novamente."</string>
|
<string name="wrong_infos">"Verifique seus dados novamente."</string>
|
||||||
<string name="all_posts_not_read">"Todas as postagens não foram lidas"</string>
|
<string name="all_posts_not_read">"Todas as postagens não foram lidas"</string>
|
||||||
<string name="all_posts_read">"Todas as postagens foram lidas"</string>
|
<string name="all_posts_read">"Todas as postagens foram lidas"</string>
|
||||||
|
<string name="nothing_here">"Nada aqui"</string>
|
||||||
|
<string name="tab_new">"Novo"</string>
|
||||||
|
<string name="tab_read">"Tudo"</string>
|
||||||
|
<string name="tab_favs">"Favoritos"</string>
|
||||||
|
<string name="action_about">"Sobre"</string>
|
||||||
|
<string name="marked_as_read">"Item lido"</string>
|
||||||
|
<string name="marked_as_unread">"Item unread"</string>
|
||||||
<string name="undo_string">"Desfazer"</string>
|
<string name="undo_string">"Desfazer"</string>
|
||||||
<string name="addStringNoUrl">"Logar para adicionar fontes."</string>
|
<string name="addStringNoUrl">"Logar para adicionar fontes."</string>
|
||||||
<string name="cant_get_sources">"Não é possível obter a lista de fontes."</string>
|
<string name="cant_get_sources">"Não é possível obter a lista de fontes."</string>
|
||||||
<string name="cant_create_source">"Não é possível criar a fonte."</string>
|
<string name="cant_create_source">"Não é possível criar a fonte."</string>
|
||||||
<string name="cant_get_spouts_no_network">"Can't get spouts list because of a network issue."</string>
|
<string name="cant_get_spouts_no_network">"Can't get spouts list because of a network issue."</string>
|
||||||
<string name="cant_get_spouts">"Não é possível obter a lista de bicos."</string>
|
<string name="cant_get_spouts">"Can't get spouts list. There may ben an api issue."</string>
|
||||||
<string name="form_not_complete">"O formulário não está completo"</string>
|
<string name="form_not_complete">"O formulário não está completo"</string>
|
||||||
<string name="pref_header_links">"Links"</string>
|
<string name="pref_header_links">"Links"</string>
|
||||||
<string name="issue_tracker_link">"Rastreador de problemas"</string>
|
<string name="issue_tracker_link">"Rastreador de problemas"</string>
|
||||||
@ -106,6 +112,9 @@
|
|||||||
<string name="reader_text_align_left">Align left</string>
|
<string name="reader_text_align_left">Align left</string>
|
||||||
<string name="reader_text_align_justify">Justify</string>
|
<string name="reader_text_align_justify">Justify</string>
|
||||||
<string name="settings_reader_font">Reader font</string>
|
<string name="settings_reader_font">Reader font</string>
|
||||||
|
<string name="reader_static_bar_title">Static bottom bar in the article viewer</string>
|
||||||
|
<string name="reader_static_bar_on">The bottom bar will always be displayed</string>
|
||||||
|
<string name="reader_static_bar_off">The bottom bar can be shown through the floating button</string>
|
||||||
<string name="remove_source">Remove source</string>
|
<string name="remove_source">Remove source</string>
|
||||||
<string name="pref_theme_title">Light/Dark mode</string>
|
<string name="pref_theme_title">Light/Dark mode</string>
|
||||||
<string name="mode_dark">Dark mode</string>
|
<string name="mode_dark">Dark mode</string>
|
||||||
@ -114,19 +123,9 @@
|
|||||||
<string name="gdpr_dialog_title">The app does not share any personal data about you.</string>
|
<string name="gdpr_dialog_title">The app does not share any personal data about you.</string>
|
||||||
<string name="gdpr_dialog_message"><![CDATA[Crash reports sending is now enabled. It can be disabled from the settings page. Keep in mind that crash reports are essential for the app development.]]></string>
|
<string name="gdpr_dialog_message"><![CDATA[Crash reports sending is now enabled. It can be disabled from the settings page. Keep in mind that crash reports are essential for the app development.]]></string>
|
||||||
<string name="crash_toast_text">A crash occured. Sending the details to the developper.</string>
|
<string name="crash_toast_text">A crash occured. Sending the details to the developper.</string>
|
||||||
<string name="pref_switch_disable_acra">"Disable automatic bug reporting."</string>
|
<string name="pref_switch_disable_acra">"Disable automatic bug reporting. "</string>
|
||||||
<string name="menu_home_filter">Filters</string>
|
<string name="menu_home_filter">Filters</string>
|
||||||
<string name="application_selfoss_only">This app only works with a Selfoss instance, and no other RSS feed.</string>
|
<string name="application_selfoss_only">This app only works with a Selfoss instance, and no other RSS feed.</string>
|
||||||
<string name="menu_home_sources">Sources</string>
|
<string name="menu_home_sources">Sources</string>
|
||||||
<string name="update_source">Update source</string>
|
<string name="update_source">Update source</string>
|
||||||
<string name="confirm_disconnect_title">Disconnect ?</string>
|
|
||||||
<string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string>
|
|
||||||
<string name="no_browser">Can\'t open a link without a browser installed</string>
|
|
||||||
<string name="nothing_here">"Nada aqui"</string>
|
|
||||||
<string name="tab_new">"Novo"</string>
|
|
||||||
<string name="tab_read">"Tudo"</string>
|
|
||||||
<string name="tab_favs">"Favoritos"</string>
|
|
||||||
<string name="action_about">"Sobre"</string>
|
|
||||||
<string name="marked_as_read">"Item lido"</string>
|
|
||||||
<string name="marked_as_unread">"Item unread"</string>
|
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||||
<string name="app_name">"Reader for Selfoss"</string>
|
<string name="app_name">"Reader for Selfoss"</string>
|
||||||
<string name="title_activity_login">"පිවිසෙන්න"</string>
|
<string name="title_activity_login">"පිවිසෙන්න"</string>
|
||||||
<string name="prompt_password">"මුර පදය"</string>
|
<string name="prompt_password">"මුර පදය"</string>
|
||||||
@ -7,7 +7,6 @@
|
|||||||
<string name="error_invalid_password">"Password not long enough"</string>
|
<string name="error_invalid_password">"Password not long enough"</string>
|
||||||
<string name="error_field_required">"Field required"</string>
|
<string name="error_field_required">"Field required"</string>
|
||||||
<string name="prompt_url">"Url"</string>
|
<string name="prompt_url">"Url"</string>
|
||||||
<string name="disable_ssl">"Disable SSL"</string>
|
|
||||||
<string name="withLoginSwitch">"Login required ?"</string>
|
<string name="withLoginSwitch">"Login required ?"</string>
|
||||||
<string name="login_url_problem">"Oops. You may need to add a \"/\" at the end of the url."</string>
|
<string name="login_url_problem">"Oops. You may need to add a \"/\" at the end of the url."</string>
|
||||||
<string name="prompt_login">"පරිශීලක නාමය"</string>
|
<string name="prompt_login">"පරිශීලක නාමය"</string>
|
||||||
@ -23,6 +22,13 @@
|
|||||||
<string name="wrong_infos">"Check your details again."</string>
|
<string name="wrong_infos">"Check your details again."</string>
|
||||||
<string name="all_posts_not_read">"All posts weren't read"</string>
|
<string name="all_posts_not_read">"All posts weren't read"</string>
|
||||||
<string name="all_posts_read">"All posts were read"</string>
|
<string name="all_posts_read">"All posts were read"</string>
|
||||||
|
<string name="nothing_here">"Nothing here"</string>
|
||||||
|
<string name="tab_new">"New"</string>
|
||||||
|
<string name="tab_read">"සියල්ල"</string>
|
||||||
|
<string name="tab_favs">"Favorites"</string>
|
||||||
|
<string name="action_about">"මේ ගැන"</string>
|
||||||
|
<string name="marked_as_read">"Item read"</string>
|
||||||
|
<string name="marked_as_unread">"Item unread"</string>
|
||||||
<string name="undo_string">"Undo"</string>
|
<string name="undo_string">"Undo"</string>
|
||||||
<string name="addStringNoUrl">"Log in to add sources."</string>
|
<string name="addStringNoUrl">"Log in to add sources."</string>
|
||||||
<string name="cant_get_sources">"Can't get sources list."</string>
|
<string name="cant_get_sources">"Can't get sources list."</string>
|
||||||
@ -106,6 +112,9 @@
|
|||||||
<string name="reader_text_align_left">Align left</string>
|
<string name="reader_text_align_left">Align left</string>
|
||||||
<string name="reader_text_align_justify">Justify</string>
|
<string name="reader_text_align_justify">Justify</string>
|
||||||
<string name="settings_reader_font">Reader font</string>
|
<string name="settings_reader_font">Reader font</string>
|
||||||
|
<string name="reader_static_bar_title">Static bottom bar in the article viewer</string>
|
||||||
|
<string name="reader_static_bar_on">The bottom bar will always be displayed</string>
|
||||||
|
<string name="reader_static_bar_off">The bottom bar can be shown through the floating button</string>
|
||||||
<string name="remove_source">Remove source</string>
|
<string name="remove_source">Remove source</string>
|
||||||
<string name="pref_theme_title">Light/Dark mode</string>
|
<string name="pref_theme_title">Light/Dark mode</string>
|
||||||
<string name="mode_dark">Dark mode</string>
|
<string name="mode_dark">Dark mode</string>
|
||||||
@ -114,19 +123,9 @@
|
|||||||
<string name="gdpr_dialog_title">The app does not share any personal data about you.</string>
|
<string name="gdpr_dialog_title">The app does not share any personal data about you.</string>
|
||||||
<string name="gdpr_dialog_message"><![CDATA[Crash reports sending is now enabled. It can be disabled from the settings page. Keep in mind that crash reports are essential for the app development.]]></string>
|
<string name="gdpr_dialog_message"><![CDATA[Crash reports sending is now enabled. It can be disabled from the settings page. Keep in mind that crash reports are essential for the app development.]]></string>
|
||||||
<string name="crash_toast_text">A crash occured. Sending the details to the developper.</string>
|
<string name="crash_toast_text">A crash occured. Sending the details to the developper.</string>
|
||||||
<string name="pref_switch_disable_acra">"Disable automatic bug reporting."</string>
|
<string name="pref_switch_disable_acra">"Disable automatic bug reporting. "</string>
|
||||||
<string name="menu_home_filter">Filters</string>
|
<string name="menu_home_filter">Filters</string>
|
||||||
<string name="application_selfoss_only">This app only works with a Selfoss instance, and no other RSS feed.</string>
|
<string name="application_selfoss_only">This app only works with a Selfoss instance, and no other RSS feed.</string>
|
||||||
<string name="menu_home_sources">Sources</string>
|
<string name="menu_home_sources">Sources</string>
|
||||||
<string name="update_source">Update source</string>
|
<string name="update_source">Update source</string>
|
||||||
<string name="confirm_disconnect_title">Disconnect ?</string>
|
|
||||||
<string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string>
|
|
||||||
<string name="no_browser">Can\'t open a link without a browser installed</string>
|
|
||||||
<string name="nothing_here">"Nothing here"</string>
|
|
||||||
<string name="tab_new">"New"</string>
|
|
||||||
<string name="tab_read">"සියල්ල"</string>
|
|
||||||
<string name="tab_favs">"Favorites"</string>
|
|
||||||
<string name="action_about">"මේ ගැන"</string>
|
|
||||||
<string name="marked_as_read">"Item read"</string>
|
|
||||||
<string name="marked_as_unread">"Item unread"</string>
|
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||||
<string name="app_name">"Selfoss için okuyucu"</string>
|
<string name="app_name">"Selfoss için okuyucu"</string>
|
||||||
<string name="title_activity_login">"Giriş"</string>
|
<string name="title_activity_login">"Giriş"</string>
|
||||||
<string name="prompt_password">"Şifre"</string>
|
<string name="prompt_password">"Şifre"</string>
|
||||||
@ -7,7 +7,6 @@
|
|||||||
<string name="error_invalid_password">"Parola yeterince uzun değil"</string>
|
<string name="error_invalid_password">"Parola yeterince uzun değil"</string>
|
||||||
<string name="error_field_required">"Alan gereklidir"</string>
|
<string name="error_field_required">"Alan gereklidir"</string>
|
||||||
<string name="prompt_url">"Url"</string>
|
<string name="prompt_url">"Url"</string>
|
||||||
<string name="disable_ssl">"Disable SSL"</string>
|
|
||||||
<string name="withLoginSwitch">"Kullanıcı Girişi Gerekli?"</string>
|
<string name="withLoginSwitch">"Kullanıcı Girişi Gerekli?"</string>
|
||||||
<string name="login_url_problem">"Oops. Url'nin sonuna \"/\" eklemek gerekebilir."</string>
|
<string name="login_url_problem">"Oops. Url'nin sonuna \"/\" eklemek gerekebilir."</string>
|
||||||
<string name="prompt_login">"Kullanıcı adı"</string>
|
<string name="prompt_login">"Kullanıcı adı"</string>
|
||||||
@ -23,12 +22,19 @@
|
|||||||
<string name="wrong_infos">"Detaylarınızı tekrar kontrol edin."</string>
|
<string name="wrong_infos">"Detaylarınızı tekrar kontrol edin."</string>
|
||||||
<string name="all_posts_not_read">"Tüm mesajlar okunmadı"</string>
|
<string name="all_posts_not_read">"Tüm mesajlar okunmadı"</string>
|
||||||
<string name="all_posts_read">"Tüm mesajlar okundu"</string>
|
<string name="all_posts_read">"Tüm mesajlar okundu"</string>
|
||||||
|
<string name="nothing_here">"Burada hiçbir şey yok"</string>
|
||||||
|
<string name="tab_new">"Yeni"</string>
|
||||||
|
<string name="tab_read">"Tüm"</string>
|
||||||
|
<string name="tab_favs">"Favoriler"</string>
|
||||||
|
<string name="action_about">"Hakkında"</string>
|
||||||
|
<string name="marked_as_read">"Öğeleri oku"</string>
|
||||||
|
<string name="marked_as_unread">"Item unread"</string>
|
||||||
<string name="undo_string">"Geri al"</string>
|
<string name="undo_string">"Geri al"</string>
|
||||||
<string name="addStringNoUrl">"Kaynakları eklemek için giriş yapın."</string>
|
<string name="addStringNoUrl">"Kaynakları eklemek için giriş yapın."</string>
|
||||||
<string name="cant_get_sources">"Kaynakları listesi alınamıyor."</string>
|
<string name="cant_get_sources">"Kaynakları listesi alınamıyor."</string>
|
||||||
<string name="cant_create_source">"Kaynak oluşturulamıyor."</string>
|
<string name="cant_create_source">"Kaynak oluşturulamıyor."</string>
|
||||||
<string name="cant_get_spouts_no_network">"Can't get spouts list because of a network issue."</string>
|
<string name="cant_get_spouts_no_network">"Can't get spouts list because of a network issue."</string>
|
||||||
<string name="cant_get_spouts">"Spouts listesine girilemiyor."</string>
|
<string name="cant_get_spouts">"Can't get spouts list. There may ben an api issue."</string>
|
||||||
<string name="form_not_complete">"Form tamamlanamadı"</string>
|
<string name="form_not_complete">"Form tamamlanamadı"</string>
|
||||||
<string name="pref_header_links">"Bağlantılar"</string>
|
<string name="pref_header_links">"Bağlantılar"</string>
|
||||||
<string name="issue_tracker_link">"Sorun İzleyici"</string>
|
<string name="issue_tracker_link">"Sorun İzleyici"</string>
|
||||||
@ -106,6 +112,9 @@
|
|||||||
<string name="reader_text_align_left">Align left</string>
|
<string name="reader_text_align_left">Align left</string>
|
||||||
<string name="reader_text_align_justify">Justify</string>
|
<string name="reader_text_align_justify">Justify</string>
|
||||||
<string name="settings_reader_font">Reader font</string>
|
<string name="settings_reader_font">Reader font</string>
|
||||||
|
<string name="reader_static_bar_title">Static bottom bar in the article viewer</string>
|
||||||
|
<string name="reader_static_bar_on">The bottom bar will always be displayed</string>
|
||||||
|
<string name="reader_static_bar_off">The bottom bar can be shown through the floating button</string>
|
||||||
<string name="remove_source">Remove source</string>
|
<string name="remove_source">Remove source</string>
|
||||||
<string name="pref_theme_title">Light/Dark mode</string>
|
<string name="pref_theme_title">Light/Dark mode</string>
|
||||||
<string name="mode_dark">Dark mode</string>
|
<string name="mode_dark">Dark mode</string>
|
||||||
@ -114,19 +123,9 @@
|
|||||||
<string name="gdpr_dialog_title">The app does not share any personal data about you.</string>
|
<string name="gdpr_dialog_title">The app does not share any personal data about you.</string>
|
||||||
<string name="gdpr_dialog_message"><![CDATA[Crash reports sending is now enabled. It can be disabled from the settings page. Keep in mind that crash reports are essential for the app development.]]></string>
|
<string name="gdpr_dialog_message"><![CDATA[Crash reports sending is now enabled. It can be disabled from the settings page. Keep in mind that crash reports are essential for the app development.]]></string>
|
||||||
<string name="crash_toast_text">A crash occured. Sending the details to the developper.</string>
|
<string name="crash_toast_text">A crash occured. Sending the details to the developper.</string>
|
||||||
<string name="pref_switch_disable_acra">"Disable automatic bug reporting."</string>
|
<string name="pref_switch_disable_acra">"Disable automatic bug reporting. "</string>
|
||||||
<string name="menu_home_filter">Filters</string>
|
<string name="menu_home_filter">Filters</string>
|
||||||
<string name="application_selfoss_only">This app only works with a Selfoss instance, and no other RSS feed.</string>
|
<string name="application_selfoss_only">This app only works with a Selfoss instance, and no other RSS feed.</string>
|
||||||
<string name="menu_home_sources">Sources</string>
|
<string name="menu_home_sources">Sources</string>
|
||||||
<string name="update_source">Update source</string>
|
<string name="update_source">Update source</string>
|
||||||
<string name="confirm_disconnect_title">Disconnect ?</string>
|
|
||||||
<string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string>
|
|
||||||
<string name="no_browser">Can\'t open a link without a browser installed</string>
|
|
||||||
<string name="nothing_here">"Burada hiçbir şey yok"</string>
|
|
||||||
<string name="tab_new">"Yeni"</string>
|
|
||||||
<string name="tab_read">"Tüm"</string>
|
|
||||||
<string name="tab_favs">"Favoriler"</string>
|
|
||||||
<string name="action_about">"Hakkında"</string>
|
|
||||||
<string name="marked_as_read">"Öğeleri oku"</string>
|
|
||||||
<string name="marked_as_unread">"Item unread"</string>
|
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||||
<string name="app_name">"Selfoss 阅读器"</string>
|
<string name="app_name">"Selfoss 阅读器"</string>
|
||||||
<string name="title_activity_login">"登录"</string>
|
<string name="title_activity_login">"登录"</string>
|
||||||
<string name="prompt_password">"密码"</string>
|
<string name="prompt_password">"密码"</string>
|
||||||
@ -7,7 +7,6 @@
|
|||||||
<string name="error_invalid_password">"密码不够长"</string>
|
<string name="error_invalid_password">"密码不够长"</string>
|
||||||
<string name="error_field_required">"必填字段"</string>
|
<string name="error_field_required">"必填字段"</string>
|
||||||
<string name="prompt_url">"网址"</string>
|
<string name="prompt_url">"网址"</string>
|
||||||
<string name="disable_ssl">"Disable SSL"</string>
|
|
||||||
<string name="withLoginSwitch">"需要登录?"</string>
|
<string name="withLoginSwitch">"需要登录?"</string>
|
||||||
<string name="login_url_problem">"哎呀。您可能需要在网址的末尾添加一个 \"/\"。"</string>
|
<string name="login_url_problem">"哎呀。您可能需要在网址的末尾添加一个 \"/\"。"</string>
|
||||||
<string name="prompt_login">"用户名"</string>
|
<string name="prompt_login">"用户名"</string>
|
||||||
@ -23,6 +22,13 @@
|
|||||||
<string name="wrong_infos">"再次检查您的详细信息。"</string>
|
<string name="wrong_infos">"再次检查您的详细信息。"</string>
|
||||||
<string name="all_posts_not_read">"所有帖子都未读"</string>
|
<string name="all_posts_not_read">"所有帖子都未读"</string>
|
||||||
<string name="all_posts_read">"所有帖子已读"</string>
|
<string name="all_posts_read">"所有帖子已读"</string>
|
||||||
|
<string name="nothing_here">"暂无内容!"</string>
|
||||||
|
<string name="tab_new">"新建"</string>
|
||||||
|
<string name="tab_read">"所有"</string>
|
||||||
|
<string name="tab_favs">"收藏夹"</string>
|
||||||
|
<string name="action_about">"关于我们"</string>
|
||||||
|
<string name="marked_as_read">"已读"</string>
|
||||||
|
<string name="marked_as_unread">"未读条目"</string>
|
||||||
<string name="undo_string">"撤销"</string>
|
<string name="undo_string">"撤销"</string>
|
||||||
<string name="addStringNoUrl">"登录以添加数据源。"</string>
|
<string name="addStringNoUrl">"登录以添加数据源。"</string>
|
||||||
<string name="cant_get_sources">"无法获取数据列表。"</string>
|
<string name="cant_get_sources">"无法获取数据列表。"</string>
|
||||||
@ -106,6 +112,9 @@
|
|||||||
<string name="reader_text_align_left">左对齐</string>
|
<string name="reader_text_align_left">左对齐</string>
|
||||||
<string name="reader_text_align_justify">左右对齐</string>
|
<string name="reader_text_align_justify">左右对齐</string>
|
||||||
<string name="settings_reader_font">阅读器字体</string>
|
<string name="settings_reader_font">阅读器字体</string>
|
||||||
|
<string name="reader_static_bar_title">文章查看器中的静态底部栏</string>
|
||||||
|
<string name="reader_static_bar_on">底部栏将始终显示</string>
|
||||||
|
<string name="reader_static_bar_off">底部栏可以通过浮动按钮显示</string>
|
||||||
<string name="remove_source">删除源</string>
|
<string name="remove_source">删除源</string>
|
||||||
<string name="pref_theme_title">浅色/深色模式</string>
|
<string name="pref_theme_title">浅色/深色模式</string>
|
||||||
<string name="mode_dark">深色模式</string>
|
<string name="mode_dark">深色模式</string>
|
||||||
@ -114,19 +123,9 @@
|
|||||||
<string name="gdpr_dialog_title">该应用不分享任何关于您的个人数据。</string>
|
<string name="gdpr_dialog_title">该应用不分享任何关于您的个人数据。</string>
|
||||||
<string name="gdpr_dialog_message"><![CDATA[崩溃报告发送现已启用。 可以从设置页面禁用它。 请记住,崩溃报告对于应用程序开发是必需的。]]></string>
|
<string name="gdpr_dialog_message"><![CDATA[崩溃报告发送现已启用。 可以从设置页面禁用它。 请记住,崩溃报告对于应用程序开发是必需的。]]></string>
|
||||||
<string name="crash_toast_text">发生崩溃。请将细节发送给开发人员。</string>
|
<string name="crash_toast_text">发生崩溃。请将细节发送给开发人员。</string>
|
||||||
<string name="pref_switch_disable_acra">"禁用自动错误报告"</string>
|
<string name="pref_switch_disable_acra">"禁用自动错误报告 "</string>
|
||||||
<string name="menu_home_filter">筛选器</string>
|
<string name="menu_home_filter">筛选器</string>
|
||||||
<string name="application_selfoss_only">此应用只适用于 Selfoss 实例,不适用于其他 RSS 。</string>
|
<string name="application_selfoss_only">This app only works with a Selfoss instance, and no other RSS feed.</string>
|
||||||
<string name="menu_home_sources">源</string>
|
<string name="menu_home_sources">Sources</string>
|
||||||
<string name="update_source">更新源</string>
|
<string name="update_source">Update source</string>
|
||||||
<string name="confirm_disconnect_title">断开连接?</string>
|
|
||||||
<string name="confirm_disconnect_description">您将断开与 selfoss 实例的连接。</string>
|
|
||||||
<string name="no_browser">Can\'t open a link without a browser installed</string>
|
|
||||||
<string name="nothing_here">"暂无内容!"</string>
|
|
||||||
<string name="tab_new">"新建"</string>
|
|
||||||
<string name="tab_read">"所有"</string>
|
|
||||||
<string name="tab_favs">"收藏夹"</string>
|
|
||||||
<string name="action_about">"关于我们"</string>
|
|
||||||
<string name="marked_as_read">"已读"</string>
|
|
||||||
<string name="marked_as_unread">"未读条目"</string>
|
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||||
<string name="app_name">"Selfoss 阅读器"</string>
|
<string name="app_name">"Selfoss 阅读器"</string>
|
||||||
<string name="title_activity_login">"登录"</string>
|
<string name="title_activity_login">"登录"</string>
|
||||||
<string name="prompt_password">"密码"</string>
|
<string name="prompt_password">"密码"</string>
|
||||||
@ -7,7 +7,6 @@
|
|||||||
<string name="error_invalid_password">"密码不够长"</string>
|
<string name="error_invalid_password">"密码不够长"</string>
|
||||||
<string name="error_field_required">"欄位必填"</string>
|
<string name="error_field_required">"欄位必填"</string>
|
||||||
<string name="prompt_url">"网址"</string>
|
<string name="prompt_url">"网址"</string>
|
||||||
<string name="disable_ssl">"Disable SSL"</string>
|
|
||||||
<string name="withLoginSwitch">"需要登入?"</string>
|
<string name="withLoginSwitch">"需要登入?"</string>
|
||||||
<string name="login_url_problem">"哎呀。您可能需要在网址的末尾添加一个 \"/\"。"</string>
|
<string name="login_url_problem">"哎呀。您可能需要在网址的末尾添加一个 \"/\"。"</string>
|
||||||
<string name="prompt_login">"使用者名稱"</string>
|
<string name="prompt_login">"使用者名稱"</string>
|
||||||
@ -23,12 +22,19 @@
|
|||||||
<string name="wrong_infos">"再次检查您的详细信息。"</string>
|
<string name="wrong_infos">"再次检查您的详细信息。"</string>
|
||||||
<string name="all_posts_not_read">"所有帖子都未读"</string>
|
<string name="all_posts_not_read">"所有帖子都未读"</string>
|
||||||
<string name="all_posts_read">"所有帖子已读"</string>
|
<string name="all_posts_read">"所有帖子已读"</string>
|
||||||
|
<string name="nothing_here">"暂无内容!"</string>
|
||||||
|
<string name="tab_new">"新建"</string>
|
||||||
|
<string name="tab_read">"所有"</string>
|
||||||
|
<string name="tab_favs">"收藏夹"</string>
|
||||||
|
<string name="action_about">"关于我们"</string>
|
||||||
|
<string name="marked_as_read">"已读"</string>
|
||||||
|
<string name="marked_as_unread">"未讀項目"</string>
|
||||||
<string name="undo_string">"撤销"</string>
|
<string name="undo_string">"撤销"</string>
|
||||||
<string name="addStringNoUrl">"登录以添加数据源。"</string>
|
<string name="addStringNoUrl">"登录以添加数据源。"</string>
|
||||||
<string name="cant_get_sources">"无法获取数据列表。"</string>
|
<string name="cant_get_sources">"无法获取数据列表。"</string>
|
||||||
<string name="cant_create_source">"无法创建源数据。"</string>
|
<string name="cant_create_source">"无法创建源数据。"</string>
|
||||||
<string name="cant_get_spouts_no_network">"Can't get spouts list because of a network issue."</string>
|
<string name="cant_get_spouts_no_network">"Can't get spouts list because of a network issue."</string>
|
||||||
<string name="cant_get_spouts">"无法获取数据列表"</string>
|
<string name="cant_get_spouts">"Can't get spouts list. There may ben an api issue."</string>
|
||||||
<string name="form_not_complete">"窗体未完成"</string>
|
<string name="form_not_complete">"窗体未完成"</string>
|
||||||
<string name="pref_header_links">"链接"</string>
|
<string name="pref_header_links">"链接"</string>
|
||||||
<string name="issue_tracker_link">"问题追踪器"</string>
|
<string name="issue_tracker_link">"问题追踪器"</string>
|
||||||
@ -106,6 +112,9 @@
|
|||||||
<string name="reader_text_align_left">Align left</string>
|
<string name="reader_text_align_left">Align left</string>
|
||||||
<string name="reader_text_align_justify">Justify</string>
|
<string name="reader_text_align_justify">Justify</string>
|
||||||
<string name="settings_reader_font">Reader font</string>
|
<string name="settings_reader_font">Reader font</string>
|
||||||
|
<string name="reader_static_bar_title">Static bottom bar in the article viewer</string>
|
||||||
|
<string name="reader_static_bar_on">The bottom bar will always be displayed</string>
|
||||||
|
<string name="reader_static_bar_off">The bottom bar can be shown through the floating button</string>
|
||||||
<string name="remove_source">Remove source</string>
|
<string name="remove_source">Remove source</string>
|
||||||
<string name="pref_theme_title">Light/Dark mode</string>
|
<string name="pref_theme_title">Light/Dark mode</string>
|
||||||
<string name="mode_dark">Dark mode</string>
|
<string name="mode_dark">Dark mode</string>
|
||||||
@ -114,19 +123,9 @@
|
|||||||
<string name="gdpr_dialog_title">The app does not share any personal data about you.</string>
|
<string name="gdpr_dialog_title">The app does not share any personal data about you.</string>
|
||||||
<string name="gdpr_dialog_message"><![CDATA[Crash reports sending is now enabled. It can be disabled from the settings page. Keep in mind that crash reports are essential for the app development.]]></string>
|
<string name="gdpr_dialog_message"><![CDATA[Crash reports sending is now enabled. It can be disabled from the settings page. Keep in mind that crash reports are essential for the app development.]]></string>
|
||||||
<string name="crash_toast_text">A crash occured. Sending the details to the developper.</string>
|
<string name="crash_toast_text">A crash occured. Sending the details to the developper.</string>
|
||||||
<string name="pref_switch_disable_acra">"Disable automatic bug reporting."</string>
|
<string name="pref_switch_disable_acra">"Disable automatic bug reporting. "</string>
|
||||||
<string name="menu_home_filter">Filters</string>
|
<string name="menu_home_filter">Filters</string>
|
||||||
<string name="application_selfoss_only">This app only works with a Selfoss instance, and no other RSS feed.</string>
|
<string name="application_selfoss_only">This app only works with a Selfoss instance, and no other RSS feed.</string>
|
||||||
<string name="menu_home_sources">Sources</string>
|
<string name="menu_home_sources">Sources</string>
|
||||||
<string name="update_source">Update source</string>
|
<string name="update_source">Update source</string>
|
||||||
<string name="confirm_disconnect_title">Disconnect ?</string>
|
|
||||||
<string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string>
|
|
||||||
<string name="no_browser">Can\'t open a link without a browser installed</string>
|
|
||||||
<string name="nothing_here">"暂无内容!"</string>
|
|
||||||
<string name="tab_new">"新建"</string>
|
|
||||||
<string name="tab_read">"所有"</string>
|
|
||||||
<string name="tab_favs">"收藏夹"</string>
|
|
||||||
<string name="action_about">"关于我们"</string>
|
|
||||||
<string name="marked_as_read">"已读"</string>
|
|
||||||
<string name="marked_as_unread">"未讀項目"</string>
|
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -12,5 +12,4 @@
|
|||||||
<color name="refresh_progress_2">@color/colorAccent</color>
|
<color name="refresh_progress_2">@color/colorAccent</color>
|
||||||
<color name="refresh_progress_3">@color/pink</color>
|
<color name="refresh_progress_3">@color/pink</color>
|
||||||
<color name="dark">#FF282828</color>
|
<color name="dark">#FF282828</color>
|
||||||
<color name="transparent_dark_background">#33000000</color>
|
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -1,6 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<resources>
|
|
||||||
<item name="unread_action" type="id" />
|
|
||||||
<item name="open_action" type="id" />
|
|
||||||
<item name="share_action" type="id" />
|
|
||||||
</resources>
|
|
@ -1,4 +1,4 @@
|
|||||||
<resources>
|
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||||
<string name="app_name">"Reader for Selfoss"</string>
|
<string name="app_name">"Reader for Selfoss"</string>
|
||||||
<string name="title_activity_login">"Log in"</string>
|
<string name="title_activity_login">"Log in"</string>
|
||||||
<string name="prompt_password">"Password"</string>
|
<string name="prompt_password">"Password"</string>
|
||||||
@ -6,7 +6,6 @@
|
|||||||
<string name="error_invalid_password">"Password not long enough"</string>
|
<string name="error_invalid_password">"Password not long enough"</string>
|
||||||
<string name="error_field_required">"Field required"</string>
|
<string name="error_field_required">"Field required"</string>
|
||||||
<string name="prompt_url">"Url"</string>
|
<string name="prompt_url">"Url"</string>
|
||||||
<string name="disable_ssl">"Disable SSL"</string>
|
|
||||||
<string name="withLoginSwitch">"Login required ?"</string>
|
<string name="withLoginSwitch">"Login required ?"</string>
|
||||||
<string name="login_url_problem">"Oops. You may need to add a \"/\" at the end of the url."</string>
|
<string name="login_url_problem">"Oops. You may need to add a \"/\" at the end of the url."</string>
|
||||||
<string name="prompt_login">"Username"</string>
|
<string name="prompt_login">"Username"</string>
|
||||||
@ -22,6 +21,13 @@
|
|||||||
<string name="wrong_infos">"Check your details again."</string>
|
<string name="wrong_infos">"Check your details again."</string>
|
||||||
<string name="all_posts_not_read">"All posts weren't read"</string>
|
<string name="all_posts_not_read">"All posts weren't read"</string>
|
||||||
<string name="all_posts_read">"All posts were read"</string>
|
<string name="all_posts_read">"All posts were read"</string>
|
||||||
|
<string name="nothing_here">"Nothing here"</string>
|
||||||
|
<string name="tab_new">"New"</string>
|
||||||
|
<string name="tab_read">"All"</string>
|
||||||
|
<string name="tab_favs">"Favorites"</string>
|
||||||
|
<string name="action_about">"About"</string>
|
||||||
|
<string name="marked_as_read">"Item read"</string>
|
||||||
|
<string name="marked_as_unread">"Item unread"</string>
|
||||||
<string name="undo_string">"Undo"</string>
|
<string name="undo_string">"Undo"</string>
|
||||||
<string name="addStringNoUrl">"Log in to add sources."</string>
|
<string name="addStringNoUrl">"Log in to add sources."</string>
|
||||||
<string name="cant_get_sources">"Can't get sources list."</string>
|
<string name="cant_get_sources">"Can't get sources list."</string>
|
||||||
@ -48,7 +54,8 @@
|
|||||||
<string name="pref_switch_card_view_off">"The articles will be displayed as a list"</string>
|
<string name="pref_switch_card_view_off">"The articles will be displayed as a list"</string>
|
||||||
<string name="menu_home_refresh">"Update remote"</string>
|
<string name="menu_home_refresh">"Update remote"</string>
|
||||||
<string name="refresh_success_response">"The remote is updated, you can now reload the articles list"</string>
|
<string name="refresh_success_response">"The remote is updated, you can now reload the articles list"</string>
|
||||||
<string name="refresh_failer_message">"The update didn't work, try again later, or check your selfoss logs."</string>
|
<string
|
||||||
|
name="refresh_failer_message">"The update didn't work, try again later, or check your selfoss logs."</string>
|
||||||
<string name="refresh_in_progress">"Refresh in progress"</string>
|
<string name="refresh_in_progress">"Refresh in progress"</string>
|
||||||
<string name="card_height_title">Full height cards</string>
|
<string name="card_height_title">Full height cards</string>
|
||||||
<string name="card_height_on">Cards height will adjust to its content</string>
|
<string name="card_height_on">Cards height will adjust to its content</string>
|
||||||
@ -108,6 +115,9 @@
|
|||||||
<string name="source_code_pro_font_id" translatable="false">source_code_pro_medium</string>
|
<string name="source_code_pro_font_id" translatable="false">source_code_pro_medium</string>
|
||||||
<string name="open_sans_font_id" translatable="false">open_sans</string>
|
<string name="open_sans_font_id" translatable="false">open_sans</string>
|
||||||
<string name="roboto_font_id" translatable="false">roboto</string>
|
<string name="roboto_font_id" translatable="false">roboto</string>
|
||||||
|
<string name="reader_static_bar_title">Static bottom bar in the article viewer</string>
|
||||||
|
<string name="reader_static_bar_on">The bottom bar will always be displayed</string>
|
||||||
|
<string name="reader_static_bar_off">The bottom bar can be shown through the floating button</string>
|
||||||
<string name="remove_source">Remove source</string>
|
<string name="remove_source">Remove source</string>
|
||||||
<string name="pref_theme_title">Light/Dark mode</string>
|
<string name="pref_theme_title">Light/Dark mode</string>
|
||||||
<string name="mode_dark">Dark mode</string>
|
<string name="mode_dark">Dark mode</string>
|
||||||
@ -116,19 +126,9 @@
|
|||||||
<string name="gdpr_dialog_title">The app does not share any personal data about you.</string>
|
<string name="gdpr_dialog_title">The app does not share any personal data about you.</string>
|
||||||
<string name="gdpr_dialog_message"><![CDATA[Crash reports sending is now enabled. It can be disabled from the settings page. Keep in mind that crash reports are essential for the app development.]]></string>
|
<string name="gdpr_dialog_message"><![CDATA[Crash reports sending is now enabled. It can be disabled from the settings page. Keep in mind that crash reports are essential for the app development.]]></string>
|
||||||
<string name="crash_toast_text">A crash occured. Sending the details to the developper.</string>
|
<string name="crash_toast_text">A crash occured. Sending the details to the developper.</string>
|
||||||
<string name="pref_switch_disable_acra">"Disable automatic bug reporting."</string>
|
<string name="pref_switch_disable_acra">"Disable automatic bug reporting. "</string>
|
||||||
<string name="menu_home_filter">Filters</string>
|
<string name="menu_home_filter">Filters</string>
|
||||||
<string name="application_selfoss_only">This app only works with a Selfoss instance, and no other RSS feed.</string>
|
<string name="application_selfoss_only">This app only works with a Selfoss instance, and no other RSS feed.</string>
|
||||||
<string name="menu_home_sources">Sources</string>
|
<string name="menu_home_sources">Sources</string>
|
||||||
<string name="update_source">Update source</string>
|
<string name="update_source">Update source</string>
|
||||||
<string name="confirm_disconnect_title">Disconnect ?</string>
|
|
||||||
<string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string>
|
|
||||||
<string name="no_browser">Can\'t open a link without a browser installed</string>
|
|
||||||
<string name="nothing_here">"Nothing here"</string>
|
|
||||||
<string name="tab_new">"New"</string>
|
|
||||||
<string name="tab_read">"All"</string>
|
|
||||||
<string name="tab_favs">"Favorites"</string>
|
|
||||||
<string name="action_about">"About"</string>
|
|
||||||
<string name="marked_as_read">"Item read"</string>
|
|
||||||
<string name="marked_as_unread">"Item unread"</string>
|
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -26,16 +26,4 @@
|
|||||||
<item name="android:textColorSecondary">@color/white</item>
|
<item name="android:textColorSecondary">@color/white</item>
|
||||||
<item name="actionMenuTextColor">@color/white</item>
|
<item name="actionMenuTextColor">@color/white</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style name="Theme.AppCompat.ImageActivity" parent="NoBar">
|
|
||||||
<item name="android:windowBackground">@color/transparent_dark_background</item>
|
|
||||||
<item name="android:colorBackgroundCacheHint">@null</item>
|
|
||||||
<item name="android:windowIsTranslucent">true</item>
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<style name="circleImageView" parent="">
|
|
||||||
<item name="cornerFamily">rounded</item>
|
|
||||||
<item name="cornerSize">50%</item>
|
|
||||||
</style>
|
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -1,41 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<MotionScene xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:motion="http://schemas.android.com/apk/res-auto"
|
|
||||||
android:id="@+id/motionLayout">
|
|
||||||
|
|
||||||
<Transition
|
|
||||||
motion:constraintSetStart="@+id/start"
|
|
||||||
motion:constraintSetEnd="@+id/end"
|
|
||||||
motion:duration="500"
|
|
||||||
motion:motionInterpolator="linear">
|
|
||||||
|
|
||||||
<OnSwipe
|
|
||||||
motion:touchAnchorId="@+id/scrollView"
|
|
||||||
motion:touchAnchorSide="top"
|
|
||||||
motion:onTouchUp="autoCompleteToStart" />
|
|
||||||
</Transition>
|
|
||||||
|
|
||||||
<ConstraintSet android:id="@+id/start">
|
|
||||||
|
|
||||||
<Constraint
|
|
||||||
android:id="@id/scrollView"
|
|
||||||
motion:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
motion:layout_constraintTop_toBottomOf="@+id/appBarLayout"
|
|
||||||
motion:layout_constraintEnd_toEndOf="parent"
|
|
||||||
motion:layout_constraintStart_toStartOf="parent">
|
|
||||||
</Constraint>
|
|
||||||
</ConstraintSet>
|
|
||||||
|
|
||||||
<ConstraintSet android:id="@+id/end">
|
|
||||||
|
|
||||||
<Constraint
|
|
||||||
android:id="@+id/scrollView"
|
|
||||||
android:layout_width="100dp"
|
|
||||||
android:layout_height="100dp"
|
|
||||||
android:layout_marginBottom="0dp"
|
|
||||||
motion:layout_constraintStart_toStartOf="parent"
|
|
||||||
motion:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
motion:layout_constraintEnd_toEndOf="parent">
|
|
||||||
</Constraint>
|
|
||||||
</ConstraintSet>
|
|
||||||
</MotionScene>
|
|
@ -30,6 +30,14 @@
|
|||||||
android:summaryOn="@string/pref_article_viewer_on"
|
android:summaryOn="@string/pref_article_viewer_on"
|
||||||
android:title="@string/pref_article_viewer_title"
|
android:title="@string/pref_article_viewer_title"
|
||||||
app:iconSpaceReserved="false"/>
|
app:iconSpaceReserved="false"/>
|
||||||
|
<SwitchPreference
|
||||||
|
android:defaultValue="false"
|
||||||
|
android:dependency="prefer_article_viewer"
|
||||||
|
android:key="reader_static_bar"
|
||||||
|
android:summaryOff="@string/reader_static_bar_off"
|
||||||
|
android:summaryOn="@string/reader_static_bar_on"
|
||||||
|
android:title="@string/reader_static_bar_title"
|
||||||
|
app:iconSpaceReserved="false"/>
|
||||||
|
|
||||||
<PreferenceCategory
|
<PreferenceCategory
|
||||||
android:title="@string/pref_general_category_displaying">
|
android:title="@string/pref_general_category_displaying">
|
||||||
|
13
androidApp/src/main/res/xml/pref_theme.xml
Normal file
13
androidApp/src/main/res/xml/pref_theme.xml
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
|
||||||
|
<ListPreference
|
||||||
|
android:defaultValue="0"
|
||||||
|
android:entries="@array/ModeTitles"
|
||||||
|
android:entryValues="@array/ModeValues"
|
||||||
|
android:key="currentMode"
|
||||||
|
app:iconSpaceReserved="false"
|
||||||
|
android:title="@string/pref_theme_title"
|
||||||
|
app:useSimpleSummaryProvider="false" />
|
||||||
|
</PreferenceScreen>
|
46
androidApp/src/test/kotlin/DatesTest.kt
Normal file
46
androidApp/src/test/kotlin/DatesTest.kt
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
package bou.amine.apps.readerforselfossv2.repository
|
||||||
|
|
||||||
|
import bou.amine.apps.readerforselfossv2.utils.DateUtils
|
||||||
|
import junit.framework.TestCase.assertEquals
|
||||||
|
import kotlinx.datetime.LocalDateTime
|
||||||
|
import kotlinx.datetime.TimeZone
|
||||||
|
import kotlinx.datetime.toInstant
|
||||||
|
import org.junit.Test
|
||||||
|
|
||||||
|
class DatesTest {
|
||||||
|
|
||||||
|
private val v3Date = "2013-04-07T13:43:00+01:00"
|
||||||
|
private val v4Date = "2013-04-07 13:43:00"
|
||||||
|
private val bug1Date = "2022-12-24T17:00:08+00"
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun v3_date_should_be_parsed() {
|
||||||
|
val date = DateUtils.parseDate(v3Date)
|
||||||
|
val expected =
|
||||||
|
LocalDateTime(2013, 4, 7, 14, 43, 0, 0).toInstant(TimeZone.currentSystemDefault())
|
||||||
|
.toEpochMilliseconds()
|
||||||
|
|
||||||
|
assertEquals(date, expected)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun v4_date_should_be_parsed() {
|
||||||
|
val date = DateUtils.parseDate(v4Date)
|
||||||
|
val expected =
|
||||||
|
LocalDateTime(2013, 4, 7, 13, 43, 0, 0).toInstant(TimeZone.currentSystemDefault())
|
||||||
|
.toEpochMilliseconds()
|
||||||
|
|
||||||
|
assertEquals(date, expected)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun bug1_date_should_be_parsed() {
|
||||||
|
val date = DateUtils.parseDate(bug1Date)
|
||||||
|
val expected =
|
||||||
|
LocalDateTime(2022, 12, 24, 18, 0, 8, 0).toInstant(TimeZone.currentSystemDefault())
|
||||||
|
.toEpochMilliseconds()
|
||||||
|
|
||||||
|
assertEquals(date, expected)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,6 +1,4 @@
|
|||||||
@file:Suppress("detekt:LargeClass")
|
package bou.amine.apps.readerforselfossv2.repository
|
||||||
|
|
||||||
package bou.amine.apps.readerforselfossv2.tests.repository
|
|
||||||
|
|
||||||
import bou.amine.apps.readerforselfossv2.dao.ReaderForSelfossDB
|
import bou.amine.apps.readerforselfossv2.dao.ReaderForSelfossDB
|
||||||
import bou.amine.apps.readerforselfossv2.dao.SOURCE
|
import bou.amine.apps.readerforselfossv2.dao.SOURCE
|
||||||
@ -8,22 +6,12 @@ import bou.amine.apps.readerforselfossv2.dao.TAG
|
|||||||
import bou.amine.apps.readerforselfossv2.model.SelfossModel
|
import bou.amine.apps.readerforselfossv2.model.SelfossModel
|
||||||
import bou.amine.apps.readerforselfossv2.model.StatusAndData
|
import bou.amine.apps.readerforselfossv2.model.StatusAndData
|
||||||
import bou.amine.apps.readerforselfossv2.model.SuccessResponse
|
import bou.amine.apps.readerforselfossv2.model.SuccessResponse
|
||||||
import bou.amine.apps.readerforselfossv2.repository.Repository
|
|
||||||
import bou.amine.apps.readerforselfossv2.rest.SelfossApi
|
import bou.amine.apps.readerforselfossv2.rest.SelfossApi
|
||||||
import bou.amine.apps.readerforselfossv2.service.AppSettingsService
|
import bou.amine.apps.readerforselfossv2.service.AppSettingsService
|
||||||
import bou.amine.apps.readerforselfossv2.utils.ItemType
|
import bou.amine.apps.readerforselfossv2.utils.ItemType
|
||||||
import bou.amine.apps.readerforselfossv2.utils.toView
|
import bou.amine.apps.readerforselfossv2.utils.toView
|
||||||
import io.mockk.clearAllMocks
|
import io.mockk.*
|
||||||
import io.mockk.coEvery
|
import junit.framework.TestCase.*
|
||||||
import io.mockk.coVerify
|
|
||||||
import io.mockk.every
|
|
||||||
import io.mockk.mockk
|
|
||||||
import io.mockk.verify
|
|
||||||
import junit.framework.TestCase.assertEquals
|
|
||||||
import junit.framework.TestCase.assertFalse
|
|
||||||
import junit.framework.TestCase.assertNotSame
|
|
||||||
import junit.framework.TestCase.assertSame
|
|
||||||
import junit.framework.TestCase.assertTrue
|
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import org.junit.Assert.assertNotEquals
|
import org.junit.Assert.assertNotEquals
|
||||||
@ -32,8 +20,6 @@ import org.junit.Test
|
|||||||
|
|
||||||
private const val BASE_URL = "https://test.com/selfoss/"
|
private const val BASE_URL = "https://test.com/selfoss/"
|
||||||
|
|
||||||
private const val USERNAME = "username"
|
|
||||||
|
|
||||||
private const val SPOUT = "spouts\\rss\\fulltextrss"
|
private const val SPOUT = "spouts\\rss\\fulltextrss"
|
||||||
|
|
||||||
private const val IMAGE_URL = "b3aa8a664d08eb15d6ff1db2fa83e0d9.png"
|
private const val IMAGE_URL = "b3aa8a664d08eb15d6ff1db2fa83e0d9.png"
|
||||||
@ -44,26 +30,26 @@ private const val FEED_URL = "https://test.com/feed"
|
|||||||
|
|
||||||
private const val TAGS = "Test, New"
|
private const val TAGS = "Test, New"
|
||||||
|
|
||||||
private const val NUMBER_ARTICLES = 100
|
|
||||||
private const val NUMBER_UNREAD = 50
|
|
||||||
private const val NUMBER_STARRED = 20
|
|
||||||
|
|
||||||
class RepositoryTest {
|
class RepositoryTest {
|
||||||
private val db = mockk<ReaderForSelfossDB>(relaxed = true)
|
private val db = mockk<ReaderForSelfossDB>(relaxed = true)
|
||||||
private val appSettingsService = mockk<AppSettingsService>()
|
private val appSettingsService = mockk<AppSettingsService>()
|
||||||
private val api = mockk<SelfossApi>()
|
private val api = mockk<SelfossApi>()
|
||||||
|
|
||||||
|
private val NUMBER_ARTICLES = 100
|
||||||
|
private val NUMBER_UNREAD = 50
|
||||||
|
private val NUMBER_STARRED = 20
|
||||||
private lateinit var repository: Repository
|
private lateinit var repository: Repository
|
||||||
|
|
||||||
|
|
||||||
private fun initializeRepository(
|
private fun initializeRepository(
|
||||||
isConnectionAvailable: MutableStateFlow<Boolean> =
|
isConnectionAvailable: MutableStateFlow<Boolean> = MutableStateFlow(
|
||||||
MutableStateFlow(
|
true
|
||||||
true,
|
)
|
||||||
),
|
|
||||||
) {
|
) {
|
||||||
repository = Repository(api, appSettingsService, isConnectionAvailable, db)
|
repository = Repository(api, appSettingsService, isConnectionAvailable, db)
|
||||||
|
|
||||||
runBlocking {
|
runBlocking {
|
||||||
repository.updateApiInformation()
|
repository.updateApiVersion()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -72,25 +58,17 @@ class RepositoryTest {
|
|||||||
clearAllMocks()
|
clearAllMocks()
|
||||||
every { appSettingsService.getApiVersion() } returns 4
|
every { appSettingsService.getApiVersion() } returns 4
|
||||||
every { appSettingsService.getBaseUrl() } returns BASE_URL
|
every { appSettingsService.getBaseUrl() } returns BASE_URL
|
||||||
every { appSettingsService.getUserName() } returns USERNAME
|
|
||||||
every { appSettingsService.isItemCachingEnabled() } returns false
|
every { appSettingsService.isItemCachingEnabled() } returns false
|
||||||
every { appSettingsService.isUpdateSourcesEnabled() } returns false
|
every { appSettingsService.isUpdateSourcesEnabled() } returns false
|
||||||
|
|
||||||
coEvery { api.apiInformation() } returns
|
coEvery { api.version() } returns StatusAndData(
|
||||||
StatusAndData(
|
success = true,
|
||||||
success = true,
|
data = SelfossModel.ApiVersion("2.19-ba1e8e3", "4.0.0")
|
||||||
data =
|
)
|
||||||
SelfossModel.ApiInformation(
|
coEvery { api.stats() } returns StatusAndData(
|
||||||
"2.19-ba1e8e3",
|
success = true,
|
||||||
"4.0.0",
|
data = SelfossModel.Stats(NUMBER_ARTICLES, NUMBER_UNREAD, NUMBER_STARRED)
|
||||||
SelfossModel.ApiConfiguration(false, true),
|
)
|
||||||
),
|
|
||||||
)
|
|
||||||
coEvery { api.stats() } returns
|
|
||||||
StatusAndData(
|
|
||||||
success = true,
|
|
||||||
data = SelfossModel.Stats(NUMBER_ARTICLES, NUMBER_UNREAD, NUMBER_STARRED),
|
|
||||||
)
|
|
||||||
|
|
||||||
every { db.itemsQueries.deleteItemsWhereSource(any()) } returns Unit
|
every { db.itemsQueries.deleteItemsWhereSource(any()) } returns Unit
|
||||||
every { db.itemsQueries.items().executeAsList() } returns generateTestDBItems()
|
every { db.itemsQueries.items().executeAsList() } returns generateTestDBItems()
|
||||||
@ -103,7 +81,7 @@ class RepositoryTest {
|
|||||||
fun instantiate_repository() {
|
fun instantiate_repository() {
|
||||||
initializeRepository()
|
initializeRepository()
|
||||||
|
|
||||||
coVerify(exactly = 1) { api.apiInformation() }
|
coVerify(exactly = 1) { api.version() }
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -112,7 +90,7 @@ class RepositoryTest {
|
|||||||
|
|
||||||
initializeRepository(MutableStateFlow(false))
|
initializeRepository(MutableStateFlow(false))
|
||||||
|
|
||||||
coVerify(exactly = 0) { api.apiInformation() }
|
coVerify(exactly = 0) { api.version() }
|
||||||
coVerify(exactly = 0) { api.stats() }
|
coVerify(exactly = 0) { api.stats() }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -120,7 +98,7 @@ class RepositoryTest {
|
|||||||
fun get_api_4_date_with_api_1_version_stored() {
|
fun get_api_4_date_with_api_1_version_stored() {
|
||||||
every { appSettingsService.getApiVersion() } returns 1
|
every { appSettingsService.getApiVersion() } returns 1
|
||||||
coEvery { api.getItems(any(), any(), any(), any(), any(), any(), any()) } returns
|
coEvery { api.getItems(any(), any(), any(), any(), any(), any(), any()) } returns
|
||||||
StatusAndData(success = true, data = generateTestApiItem())
|
StatusAndData(success = true, data = generateTestApiItem())
|
||||||
every { appSettingsService.updateApiVersion(any()) } returns Unit
|
every { appSettingsService.updateApiVersion(any()) } returns Unit
|
||||||
|
|
||||||
initializeRepository()
|
initializeRepository()
|
||||||
@ -132,101 +110,17 @@ class RepositoryTest {
|
|||||||
verify(exactly = 1) { appSettingsService.updateApiVersion(4) }
|
verify(exactly = 1) { appSettingsService.updateApiVersion(4) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
fun get_public_access() {
|
|
||||||
every { appSettingsService.updatePublicAccess(any()) } returns Unit
|
|
||||||
coEvery { api.apiInformation() } returns
|
|
||||||
StatusAndData(
|
|
||||||
success = true,
|
|
||||||
data =
|
|
||||||
SelfossModel.ApiInformation(
|
|
||||||
"2.19-ba1e8e3",
|
|
||||||
"4.0.0",
|
|
||||||
SelfossModel.ApiConfiguration(true, true),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
every { appSettingsService.getUserName() } returns ""
|
|
||||||
|
|
||||||
initializeRepository()
|
|
||||||
|
|
||||||
coVerify(exactly = 1) { api.apiInformation() }
|
|
||||||
coVerify(exactly = 1) { appSettingsService.updatePublicAccess(true) }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun get_public_access_username_not_empty() {
|
|
||||||
every { appSettingsService.updatePublicAccess(any()) } returns Unit
|
|
||||||
coEvery { api.apiInformation() } returns
|
|
||||||
StatusAndData(
|
|
||||||
success = true,
|
|
||||||
data =
|
|
||||||
SelfossModel.ApiInformation(
|
|
||||||
"2.19-ba1e8e3",
|
|
||||||
"4.0.0",
|
|
||||||
SelfossModel.ApiConfiguration(true, true),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
every { appSettingsService.getUserName() } returns "username"
|
|
||||||
|
|
||||||
initializeRepository()
|
|
||||||
|
|
||||||
coVerify(exactly = 1) { api.apiInformation() }
|
|
||||||
coVerify(exactly = 0) { appSettingsService.updatePublicAccess(true) }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun get_public_access_no_auth() {
|
|
||||||
every { appSettingsService.updatePublicAccess(any()) } returns Unit
|
|
||||||
coEvery { api.apiInformation() } returns
|
|
||||||
StatusAndData(
|
|
||||||
success = true,
|
|
||||||
data =
|
|
||||||
SelfossModel.ApiInformation(
|
|
||||||
"2.19-ba1e8e3",
|
|
||||||
"4.0.0",
|
|
||||||
SelfossModel.ApiConfiguration(true, false),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
every { appSettingsService.getUserName() } returns ""
|
|
||||||
|
|
||||||
initializeRepository()
|
|
||||||
|
|
||||||
coVerify(exactly = 1) { api.apiInformation() }
|
|
||||||
coVerify(exactly = 0) { appSettingsService.updatePublicAccess(true) }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun get_public_access_disabled() {
|
|
||||||
every { appSettingsService.updatePublicAccess(any()) } returns Unit
|
|
||||||
coEvery { api.apiInformation() } returns
|
|
||||||
StatusAndData(
|
|
||||||
success = true,
|
|
||||||
data =
|
|
||||||
SelfossModel.ApiInformation(
|
|
||||||
"2.19-ba1e8e3",
|
|
||||||
"4.0.0",
|
|
||||||
SelfossModel.ApiConfiguration(false, true),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
every { appSettingsService.getUserName() } returns ""
|
|
||||||
|
|
||||||
initializeRepository()
|
|
||||||
|
|
||||||
coVerify(exactly = 1) { api.apiInformation() }
|
|
||||||
coVerify(exactly = 0) { appSettingsService.updatePublicAccess(true) }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun get_api_1_date_with_api_4_version_stored() {
|
fun get_api_1_date_with_api_4_version_stored() {
|
||||||
every { appSettingsService.getApiVersion() } returns 4
|
every { appSettingsService.getApiVersion() } returns 4
|
||||||
coEvery { api.apiInformation() } returns StatusAndData(success = false, null)
|
coEvery { api.version() } returns StatusAndData(success = false, null)
|
||||||
val itemParameters = FakeItemParameters()
|
val itemParameters = FakeItemParameters()
|
||||||
itemParameters.datetime = "2021-04-23 11:45:32"
|
itemParameters.datetime = "2021-04-23 11:45:32"
|
||||||
coEvery { api.getItems(any(), any(), any(), any(), any(), any(), any()) } returns
|
coEvery { api.getItems(any(), any(), any(), any(), any(), any(), any()) } returns
|
||||||
StatusAndData(
|
StatusAndData(
|
||||||
success = true,
|
success = true,
|
||||||
data = generateTestApiItem(itemParameters),
|
data = generateTestApiItem(itemParameters)
|
||||||
)
|
)
|
||||||
|
|
||||||
initializeRepository()
|
initializeRepository()
|
||||||
runBlocking {
|
runBlocking {
|
||||||
@ -239,7 +133,7 @@ class RepositoryTest {
|
|||||||
@Test
|
@Test
|
||||||
fun get_newer_items() {
|
fun get_newer_items() {
|
||||||
coEvery { api.getItems(any(), any(), any(), any(), any(), any(), any()) } returns
|
coEvery { api.getItems(any(), any(), any(), any(), any(), any(), any()) } returns
|
||||||
StatusAndData(success = true, data = generateTestApiItem())
|
StatusAndData(success = true, data = generateTestApiItem())
|
||||||
|
|
||||||
initializeRepository()
|
initializeRepository()
|
||||||
runBlocking {
|
runBlocking {
|
||||||
@ -254,7 +148,7 @@ class RepositoryTest {
|
|||||||
@Test
|
@Test
|
||||||
fun get_all_newer_items() {
|
fun get_all_newer_items() {
|
||||||
coEvery { api.getItems(any(), any(), any(), any(), any(), any(), any()) } returns
|
coEvery { api.getItems(any(), any(), any(), any(), any(), any(), any()) } returns
|
||||||
StatusAndData(success = true, data = generateTestApiItem())
|
StatusAndData(success = true, data = generateTestApiItem())
|
||||||
|
|
||||||
initializeRepository()
|
initializeRepository()
|
||||||
repository.displayedItems = ItemType.ALL
|
repository.displayedItems = ItemType.ALL
|
||||||
@ -270,7 +164,7 @@ class RepositoryTest {
|
|||||||
@Test
|
@Test
|
||||||
fun get_newer_starred_items() {
|
fun get_newer_starred_items() {
|
||||||
coEvery { api.getItems(any(), any(), any(), any(), any(), any(), any()) } returns
|
coEvery { api.getItems(any(), any(), any(), any(), any(), any(), any()) } returns
|
||||||
StatusAndData(success = true, data = generateTestApiItem())
|
StatusAndData(success = true, data = generateTestApiItem())
|
||||||
|
|
||||||
initializeRepository()
|
initializeRepository()
|
||||||
repository.displayedItems = ItemType.STARRED
|
repository.displayedItems = ItemType.STARRED
|
||||||
@ -307,10 +201,10 @@ class RepositoryTest {
|
|||||||
itemParameter3.tags = "Other, Tag"
|
itemParameter3.tags = "Other, Tag"
|
||||||
itemParameter3.id = "3"
|
itemParameter3.id = "3"
|
||||||
coEvery { db.itemsQueries.items().executeAsList() } returns generateTestDBItems(
|
coEvery { db.itemsQueries.items().executeAsList() } returns generateTestDBItems(
|
||||||
itemParameter1,
|
itemParameter1
|
||||||
) +
|
) +
|
||||||
generateTestDBItems(itemParameter2) +
|
generateTestDBItems(itemParameter2) +
|
||||||
generateTestDBItems(itemParameter3)
|
generateTestDBItems(itemParameter3)
|
||||||
|
|
||||||
every { appSettingsService.isItemCachingEnabled() } returns true
|
every { appSettingsService.isItemCachingEnabled() } returns true
|
||||||
|
|
||||||
@ -335,26 +229,23 @@ class RepositoryTest {
|
|||||||
itemParameter3.sourcetitle = "Other"
|
itemParameter3.sourcetitle = "Other"
|
||||||
itemParameter3.id = "3"
|
itemParameter3.id = "3"
|
||||||
coEvery { db.itemsQueries.items().executeAsList() } returns generateTestDBItems(
|
coEvery { db.itemsQueries.items().executeAsList() } returns generateTestDBItems(
|
||||||
itemParameter1,
|
itemParameter1
|
||||||
) +
|
) +
|
||||||
generateTestDBItems(itemParameter2) +
|
generateTestDBItems(itemParameter2) +
|
||||||
generateTestDBItems(itemParameter3)
|
generateTestDBItems(itemParameter3)
|
||||||
|
|
||||||
every { appSettingsService.isItemCachingEnabled() } returns true
|
every { appSettingsService.isItemCachingEnabled() } returns true
|
||||||
|
|
||||||
initializeRepository(MutableStateFlow(false))
|
initializeRepository(MutableStateFlow(false))
|
||||||
repository.setSourceFilter(
|
repository.setSourceFilter(SelfossModel.Source(
|
||||||
SelfossModel.SourceDetail(
|
1,
|
||||||
1,
|
"Test",
|
||||||
"Test",
|
listOf("tags"),
|
||||||
null,
|
SPOUT,
|
||||||
listOf("tags"),
|
"",
|
||||||
SPOUT,
|
IMAGE_URL,
|
||||||
"",
|
SelfossModel.SourceParams("url")
|
||||||
IMAGE_URL,
|
))
|
||||||
SelfossModel.SourceParams("url"),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
runBlocking {
|
runBlocking {
|
||||||
repository.getNewerItems()
|
repository.getNewerItems()
|
||||||
}
|
}
|
||||||
@ -367,7 +258,7 @@ class RepositoryTest {
|
|||||||
@Test
|
@Test
|
||||||
fun get_older_items() {
|
fun get_older_items() {
|
||||||
coEvery { api.getItems(any(), any(), any(), any(), any(), any(), any()) } returns
|
coEvery { api.getItems(any(), any(), any(), any(), any(), any(), any()) } returns
|
||||||
StatusAndData(success = true, data = generateTestApiItem())
|
StatusAndData(success = true, data = generateTestApiItem())
|
||||||
|
|
||||||
initializeRepository()
|
initializeRepository()
|
||||||
repository.items = ArrayList(generateTestApiItem())
|
repository.items = ArrayList(generateTestApiItem())
|
||||||
@ -383,7 +274,7 @@ class RepositoryTest {
|
|||||||
@Test
|
@Test
|
||||||
fun get_all_older_items() {
|
fun get_all_older_items() {
|
||||||
coEvery { api.getItems(any(), any(), any(), any(), any(), any(), any()) } returns
|
coEvery { api.getItems(any(), any(), any(), any(), any(), any(), any()) } returns
|
||||||
StatusAndData(success = true, data = generateTestApiItem())
|
StatusAndData(success = true, data = generateTestApiItem())
|
||||||
|
|
||||||
initializeRepository()
|
initializeRepository()
|
||||||
repository.items = ArrayList(generateTestApiItem())
|
repository.items = ArrayList(generateTestApiItem())
|
||||||
@ -400,7 +291,7 @@ class RepositoryTest {
|
|||||||
@Test
|
@Test
|
||||||
fun get_older_starred_items() {
|
fun get_older_starred_items() {
|
||||||
coEvery { api.getItems(any(), any(), any(), any(), any(), any(), any()) } returns
|
coEvery { api.getItems(any(), any(), any(), any(), any(), any(), any()) } returns
|
||||||
StatusAndData(success = true, data = generateTestApiItem())
|
StatusAndData(success = true, data = generateTestApiItem())
|
||||||
|
|
||||||
initializeRepository()
|
initializeRepository()
|
||||||
repository.displayedItems = ItemType.STARRED
|
repository.displayedItems = ItemType.STARRED
|
||||||
@ -637,16 +528,14 @@ class RepositoryTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun prepareTags(): Pair<List<SelfossModel.Tag>, List<TAG>> {
|
private fun prepareTags(): Pair<List<SelfossModel.Tag>, List<TAG>> {
|
||||||
val tags =
|
val tags = listOf(
|
||||||
listOf(
|
SelfossModel.Tag("test", "red", 6),
|
||||||
SelfossModel.Tag("test", "red", 6),
|
SelfossModel.Tag("second", "yellow", 0)
|
||||||
SelfossModel.Tag("second", "yellow", 0),
|
)
|
||||||
)
|
val tagsDB = listOf(
|
||||||
val tagsDB =
|
TAG("test_DB", "red", 6),
|
||||||
listOf(
|
TAG("second_DB", "yellow", 0)
|
||||||
TAG("test_DB", "red", 6),
|
)
|
||||||
TAG("second_DB", "yellow", 0),
|
|
||||||
)
|
|
||||||
|
|
||||||
coEvery { api.tags() } returns StatusAndData(success = true, data = tags)
|
coEvery { api.tags() } returns StatusAndData(success = true, data = tags)
|
||||||
coEvery { db.tagsQueries.tags().executeAsList() } returns tagsDB
|
coEvery { db.tagsQueries.tags().executeAsList() } returns tagsDB
|
||||||
@ -657,63 +546,59 @@ class RepositoryTest {
|
|||||||
fun get_sources() {
|
fun get_sources() {
|
||||||
val (sources, sourcesDB) = prepareSources()
|
val (sources, sourcesDB) = prepareSources()
|
||||||
initializeRepository()
|
initializeRepository()
|
||||||
var testSources: List<SelfossModel.Source>
|
var testSources: List<SelfossModel.Source>?
|
||||||
runBlocking {
|
runBlocking {
|
||||||
testSources = repository.getSourcesDetails()
|
testSources = repository.getSources()
|
||||||
}
|
}
|
||||||
|
|
||||||
assertEquals(sources, testSources)
|
assertSame(sources, testSources)
|
||||||
assertNotEquals(sourcesDB.map { it.toView() }, testSources)
|
assertNotEquals(sourcesDB.map { it.toView() }, testSources)
|
||||||
coVerify(exactly = 1) { api.sourcesDetailed() }
|
coVerify(exactly = 1) { api.sources() }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun prepareSources(): Pair<ArrayList<SelfossModel.SourceDetail>, List<SOURCE>> {
|
private fun prepareSources(): Pair<ArrayList<SelfossModel.Source>, List<SOURCE>> {
|
||||||
val sources =
|
val sources = arrayListOf(
|
||||||
arrayListOf(
|
SelfossModel.Source(
|
||||||
SelfossModel.SourceDetail(
|
1,
|
||||||
1,
|
"First source",
|
||||||
"First source",
|
listOf("Test", "second"),
|
||||||
null,
|
SPOUT,
|
||||||
listOf("Test", "second"),
|
"",
|
||||||
SPOUT,
|
IMAGE_URL_2,
|
||||||
"",
|
SelfossModel.SourceParams("url")
|
||||||
IMAGE_URL_2,
|
),
|
||||||
SelfossModel.SourceParams("url"),
|
SelfossModel.Source(
|
||||||
),
|
2,
|
||||||
SelfossModel.SourceDetail(
|
"Second source",
|
||||||
2,
|
listOf("second"),
|
||||||
"Second source",
|
SPOUT,
|
||||||
null,
|
"",
|
||||||
listOf("second"),
|
IMAGE_URL,
|
||||||
SPOUT,
|
SelfossModel.SourceParams("url")
|
||||||
"",
|
|
||||||
IMAGE_URL,
|
|
||||||
SelfossModel.SourceParams("url"),
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
val sourcesDB =
|
)
|
||||||
listOf(
|
val sourcesDB = listOf(
|
||||||
SOURCE(
|
SOURCE(
|
||||||
"1",
|
"1",
|
||||||
"First DB source",
|
"First DB source",
|
||||||
"Test,second",
|
"Test,second",
|
||||||
SPOUT,
|
SPOUT,
|
||||||
"",
|
"",
|
||||||
IMAGE_URL_2,
|
IMAGE_URL_2,
|
||||||
"url",
|
"url"
|
||||||
),
|
),
|
||||||
SOURCE(
|
SOURCE(
|
||||||
"2",
|
"2",
|
||||||
"Second source",
|
"Second source",
|
||||||
"second",
|
"second",
|
||||||
SPOUT,
|
SPOUT,
|
||||||
"",
|
"",
|
||||||
IMAGE_URL,
|
IMAGE_URL,
|
||||||
"url",
|
"url"
|
||||||
),
|
|
||||||
)
|
)
|
||||||
|
)
|
||||||
|
|
||||||
coEvery { api.sourcesDetailed() } returns StatusAndData(success = true, data = sources)
|
coEvery { api.sources() } returns StatusAndData(success = true, data = sources)
|
||||||
every { db.sourcesQueries.sources().executeAsList() } returns sourcesDB
|
every { db.sourcesQueries.sources().executeAsList() } returns sourcesDB
|
||||||
return Pair(sources, sourcesDB)
|
return Pair(sources, sourcesDB)
|
||||||
}
|
}
|
||||||
@ -727,13 +612,13 @@ class RepositoryTest {
|
|||||||
initializeRepository()
|
initializeRepository()
|
||||||
var testSources: List<SelfossModel.Source>?
|
var testSources: List<SelfossModel.Source>?
|
||||||
runBlocking {
|
runBlocking {
|
||||||
testSources = repository.getSourcesDetails()
|
testSources = repository.getSources()
|
||||||
// Sources will be fetched from the database on the second call, thus testSources != sources
|
// Sources will be fetched from the database on the second call, thus testSources != sources
|
||||||
testSources = repository.getSourcesDetails()
|
testSources = repository.getSources()
|
||||||
}
|
}
|
||||||
|
|
||||||
coVerify(exactly = 1) { api.sourcesDetailed() }
|
coVerify(exactly = 1) { api.sources() }
|
||||||
assertNotEquals(sources, testSources)
|
assertNotSame(sources, testSources)
|
||||||
assertEquals(sourcesDB.map { it.toView() }, testSources)
|
assertEquals(sourcesDB.map { it.toView() }, testSources)
|
||||||
verify(atLeast = 1) { db.sourcesQueries.sources().executeAsList() }
|
verify(atLeast = 1) { db.sourcesQueries.sources().executeAsList() }
|
||||||
}
|
}
|
||||||
@ -745,13 +630,13 @@ class RepositoryTest {
|
|||||||
every { appSettingsService.isUpdateSourcesEnabled() } returns true
|
every { appSettingsService.isUpdateSourcesEnabled() } returns true
|
||||||
every { appSettingsService.isItemCachingEnabled() } returns false
|
every { appSettingsService.isItemCachingEnabled() } returns false
|
||||||
initializeRepository()
|
initializeRepository()
|
||||||
var testSources: List<SelfossModel.Source>
|
var testSources: List<SelfossModel.Source>?
|
||||||
runBlocking {
|
runBlocking {
|
||||||
testSources = repository.getSourcesDetails()
|
testSources = repository.getSources()
|
||||||
}
|
}
|
||||||
|
|
||||||
assertEquals(sources, testSources)
|
assertSame(sources, testSources)
|
||||||
coVerify(exactly = 1) { api.sourcesDetailed() }
|
coVerify(exactly = 1) { api.sources() }
|
||||||
verify(exactly = 0) { db.sourcesQueries }
|
verify(exactly = 0) { db.sourcesQueries }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -762,13 +647,13 @@ class RepositoryTest {
|
|||||||
every { appSettingsService.isUpdateSourcesEnabled() } returns false
|
every { appSettingsService.isUpdateSourcesEnabled() } returns false
|
||||||
every { appSettingsService.isItemCachingEnabled() } returns false
|
every { appSettingsService.isItemCachingEnabled() } returns false
|
||||||
initializeRepository()
|
initializeRepository()
|
||||||
var testSources: List<SelfossModel.Source>
|
var testSources: List<SelfossModel.Source>?
|
||||||
runBlocking {
|
runBlocking {
|
||||||
testSources = repository.getSourcesDetails()
|
testSources = repository.getSources()
|
||||||
}
|
}
|
||||||
|
|
||||||
assertEquals(sources, testSources)
|
assertSame(sources, testSources)
|
||||||
coVerify(exactly = 1) { api.sourcesDetailed() }
|
coVerify(exactly = 1) { api.sources() }
|
||||||
verify(atLeast = 1) { db.sourcesQueries }
|
verify(atLeast = 1) { db.sourcesQueries }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -776,13 +661,13 @@ class RepositoryTest {
|
|||||||
fun get_sources_without_connection() {
|
fun get_sources_without_connection() {
|
||||||
val (_, sourcesDB) = prepareSources()
|
val (_, sourcesDB) = prepareSources()
|
||||||
initializeRepository(MutableStateFlow(false))
|
initializeRepository(MutableStateFlow(false))
|
||||||
var testSources: List<SelfossModel.Source>
|
var testSources: List<SelfossModel.Source>?
|
||||||
runBlocking {
|
runBlocking {
|
||||||
testSources = repository.getSourcesDetails()
|
testSources = repository.getSources()
|
||||||
}
|
}
|
||||||
|
|
||||||
assertEquals(sourcesDB.map { it.toView() }, testSources)
|
assertEquals(sourcesDB.map { it.toView() }, testSources)
|
||||||
coVerify(exactly = 0) { api.sourcesDetailed() }
|
coVerify(exactly = 0) { api.sources() }
|
||||||
verify(atLeast = 1) { db.sourcesQueries.sources().executeAsList() }
|
verify(atLeast = 1) { db.sourcesQueries.sources().executeAsList() }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -793,13 +678,13 @@ class RepositoryTest {
|
|||||||
every { appSettingsService.isItemCachingEnabled() } returns false
|
every { appSettingsService.isItemCachingEnabled() } returns false
|
||||||
every { appSettingsService.isUpdateSourcesEnabled() } returns true
|
every { appSettingsService.isUpdateSourcesEnabled() } returns true
|
||||||
initializeRepository(MutableStateFlow(false))
|
initializeRepository(MutableStateFlow(false))
|
||||||
var testSources: List<SelfossModel.Source>
|
var testSources: List<SelfossModel.Source>?
|
||||||
runBlocking {
|
runBlocking {
|
||||||
testSources = repository.getSourcesDetails()
|
testSources = repository.getSources()
|
||||||
}
|
}
|
||||||
|
|
||||||
assertEquals(emptyList<SelfossModel.Source>(), testSources)
|
assertEquals(emptyList<SelfossModel.Source>(), testSources)
|
||||||
coVerify(exactly = 0) { api.sourcesDetailed() }
|
coVerify(exactly = 0) { api.sources() }
|
||||||
verify(exactly = 0) { db.sourcesQueries.sources().executeAsList() }
|
verify(exactly = 0) { db.sourcesQueries.sources().executeAsList() }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -810,13 +695,13 @@ class RepositoryTest {
|
|||||||
every { appSettingsService.isItemCachingEnabled() } returns true
|
every { appSettingsService.isItemCachingEnabled() } returns true
|
||||||
every { appSettingsService.isUpdateSourcesEnabled() } returns false
|
every { appSettingsService.isUpdateSourcesEnabled() } returns false
|
||||||
initializeRepository(MutableStateFlow(false))
|
initializeRepository(MutableStateFlow(false))
|
||||||
var testSources: List<SelfossModel.Source>
|
var testSources: List<SelfossModel.Source>?
|
||||||
runBlocking {
|
runBlocking {
|
||||||
testSources = repository.getSourcesDetails()
|
testSources = repository.getSources()
|
||||||
}
|
}
|
||||||
|
|
||||||
assertEquals(sourcesDB.map { it.toView() }, testSources)
|
assertEquals(sourcesDB.map { it.toView() }, testSources)
|
||||||
coVerify(exactly = 0) { api.sourcesDetailed() }
|
coVerify(exactly = 0) { api.sources() }
|
||||||
verify(atLeast = 1) { db.sourcesQueries.sources().executeAsList() }
|
verify(atLeast = 1) { db.sourcesQueries.sources().executeAsList() }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -827,31 +712,30 @@ class RepositoryTest {
|
|||||||
every { appSettingsService.isItemCachingEnabled() } returns false
|
every { appSettingsService.isItemCachingEnabled() } returns false
|
||||||
every { appSettingsService.isUpdateSourcesEnabled() } returns false
|
every { appSettingsService.isUpdateSourcesEnabled() } returns false
|
||||||
initializeRepository(MutableStateFlow(false))
|
initializeRepository(MutableStateFlow(false))
|
||||||
var testSources: List<SelfossModel.Source>
|
var testSources: List<SelfossModel.Source>?
|
||||||
runBlocking {
|
runBlocking {
|
||||||
testSources = repository.getSourcesDetails()
|
testSources = repository.getSources()
|
||||||
}
|
}
|
||||||
|
|
||||||
assertEquals(sourcesDB.map { it.toView() }, testSources)
|
assertEquals(sourcesDB.map { it.toView() }, testSources)
|
||||||
coVerify(exactly = 0) { api.sourcesDetailed() }
|
coVerify(exactly = 0) { api.sources() }
|
||||||
verify(atLeast = 1) { db.sourcesQueries.sources().executeAsList() }
|
verify(atLeast = 1) { db.sourcesQueries.sources().executeAsList() }
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun create_source() {
|
fun create_source() {
|
||||||
coEvery { api.createSourceForVersion(any(), any(), any(), any()) } returns
|
coEvery { api.createSourceForVersion(any(), any(), any(), any()) } returns
|
||||||
SuccessResponse(true)
|
SuccessResponse(true)
|
||||||
|
|
||||||
initializeRepository()
|
initializeRepository()
|
||||||
var response: Boolean
|
var response: Boolean
|
||||||
runBlocking {
|
runBlocking {
|
||||||
response =
|
response = repository.createSource(
|
||||||
repository.createSource(
|
"test",
|
||||||
"test",
|
FEED_URL,
|
||||||
FEED_URL,
|
SPOUT,
|
||||||
SPOUT,
|
TAGS,
|
||||||
TAGS,
|
)
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
coVerify(exactly = 1) {
|
coVerify(exactly = 1) {
|
||||||
@ -868,18 +752,17 @@ class RepositoryTest {
|
|||||||
@Test
|
@Test
|
||||||
fun create_source_but_response_fails() {
|
fun create_source_but_response_fails() {
|
||||||
coEvery { api.createSourceForVersion(any(), any(), any(), any()) } returns
|
coEvery { api.createSourceForVersion(any(), any(), any(), any()) } returns
|
||||||
SuccessResponse(false)
|
SuccessResponse(false)
|
||||||
|
|
||||||
initializeRepository()
|
initializeRepository()
|
||||||
var response: Boolean
|
var response: Boolean
|
||||||
runBlocking {
|
runBlocking {
|
||||||
response =
|
response = repository.createSource(
|
||||||
repository.createSource(
|
"test",
|
||||||
"test",
|
FEED_URL,
|
||||||
FEED_URL,
|
SPOUT,
|
||||||
SPOUT,
|
TAGS
|
||||||
TAGS,
|
)
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
coVerify(exactly = 1) {
|
coVerify(exactly = 1) {
|
||||||
@ -887,7 +770,7 @@ class RepositoryTest {
|
|||||||
any(),
|
any(),
|
||||||
any(),
|
any(),
|
||||||
any(),
|
any(),
|
||||||
any(),
|
any()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
assertSame(false, response)
|
assertSame(false, response)
|
||||||
@ -896,18 +779,17 @@ class RepositoryTest {
|
|||||||
@Test
|
@Test
|
||||||
fun create_source_without_connection() {
|
fun create_source_without_connection() {
|
||||||
coEvery { api.createSourceForVersion(any(), any(), any(), any()) } returns
|
coEvery { api.createSourceForVersion(any(), any(), any(), any()) } returns
|
||||||
SuccessResponse(true)
|
SuccessResponse(true)
|
||||||
|
|
||||||
initializeRepository(MutableStateFlow(false))
|
initializeRepository(MutableStateFlow(false))
|
||||||
var response: Boolean
|
var response: Boolean
|
||||||
runBlocking {
|
runBlocking {
|
||||||
response =
|
response = repository.createSource(
|
||||||
repository.createSource(
|
"test",
|
||||||
"test",
|
FEED_URL,
|
||||||
FEED_URL,
|
SPOUT,
|
||||||
SPOUT,
|
TAGS
|
||||||
TAGS,
|
)
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
coVerify(exactly = 0) {
|
coVerify(exactly = 0) {
|
||||||
@ -968,11 +850,10 @@ class RepositoryTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun update_remote() {
|
fun update_remote() {
|
||||||
coEvery { api.update() } returns
|
coEvery { api.update() } returns StatusAndData(
|
||||||
StatusAndData(
|
success = true,
|
||||||
success = true,
|
data = "finished"
|
||||||
data = "finished",
|
)
|
||||||
)
|
|
||||||
|
|
||||||
initializeRepository()
|
initializeRepository()
|
||||||
var response: Boolean
|
var response: Boolean
|
||||||
@ -986,11 +867,10 @@ class RepositoryTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun update_remote_but_response_fails() {
|
fun update_remote_but_response_fails() {
|
||||||
coEvery { api.update() } returns
|
coEvery { api.update() } returns StatusAndData(
|
||||||
StatusAndData(
|
success = false,
|
||||||
success = false,
|
data = "unallowed access"
|
||||||
data = "unallowed access",
|
)
|
||||||
)
|
|
||||||
|
|
||||||
initializeRepository()
|
initializeRepository()
|
||||||
var response: Boolean
|
var response: Boolean
|
||||||
@ -1004,11 +884,10 @@ class RepositoryTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun update_remote_with_unallowed_access() {
|
fun update_remote_with_unallowed_access() {
|
||||||
coEvery { api.update() } returns
|
coEvery { api.update() } returns StatusAndData(
|
||||||
StatusAndData(
|
success = true,
|
||||||
success = true,
|
data = "unallowed access"
|
||||||
data = "unallowed access",
|
)
|
||||||
)
|
|
||||||
|
|
||||||
initializeRepository()
|
initializeRepository()
|
||||||
var response: Boolean
|
var response: Boolean
|
||||||
@ -1022,11 +901,10 @@ class RepositoryTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun update_remote_without_connection() {
|
fun update_remote_without_connection() {
|
||||||
coEvery { api.update() } returns
|
coEvery { api.update() } returns StatusAndData(
|
||||||
StatusAndData(
|
success = true,
|
||||||
success = true,
|
data = "undocumented..."
|
||||||
data = "undocumented...",
|
)
|
||||||
)
|
|
||||||
|
|
||||||
initializeRepository(MutableStateFlow(false))
|
initializeRepository(MutableStateFlow(false))
|
||||||
var response: Boolean
|
var response: Boolean
|
||||||
@ -1093,7 +971,7 @@ class RepositoryTest {
|
|||||||
appSettingsService.refreshLoginInformation(
|
appSettingsService.refreshLoginInformation(
|
||||||
BASE_URL,
|
BASE_URL,
|
||||||
"login",
|
"login",
|
||||||
"password",
|
"password"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1113,14 +991,13 @@ class RepositoryTest {
|
|||||||
any(),
|
any(),
|
||||||
any(),
|
any(),
|
||||||
any(),
|
any(),
|
||||||
any(),
|
any()
|
||||||
)
|
|
||||||
} returnsMany
|
|
||||||
listOf(
|
|
||||||
StatusAndData(success = true, data = generateTestApiItem(itemParameter1)),
|
|
||||||
StatusAndData(success = true, data = generateTestApiItem(itemParameter2)),
|
|
||||||
StatusAndData(success = true, data = generateTestApiItem(itemParameter1)),
|
|
||||||
)
|
)
|
||||||
|
} returnsMany listOf(
|
||||||
|
StatusAndData(success = true, data = generateTestApiItem(itemParameter1)),
|
||||||
|
StatusAndData(success = true, data = generateTestApiItem(itemParameter2)),
|
||||||
|
StatusAndData(success = true, data = generateTestApiItem(itemParameter1)),
|
||||||
|
)
|
||||||
|
|
||||||
initializeRepository()
|
initializeRepository()
|
||||||
prepareSearch()
|
prepareSearch()
|
||||||
@ -1134,7 +1011,7 @@ class RepositoryTest {
|
|||||||
@Test
|
@Test
|
||||||
fun cache_items_but_response_fails() {
|
fun cache_items_but_response_fails() {
|
||||||
coEvery { api.getItems(any(), any(), any(), any(), any(), any(), any()) } returns
|
coEvery { api.getItems(any(), any(), any(), any(), any(), any(), any()) } returns
|
||||||
StatusAndData(success = false, data = generateTestApiItem())
|
StatusAndData(success = false, data = generateTestApiItem())
|
||||||
|
|
||||||
initializeRepository()
|
initializeRepository()
|
||||||
prepareSearch()
|
prepareSearch()
|
||||||
@ -1148,7 +1025,7 @@ class RepositoryTest {
|
|||||||
@Test
|
@Test
|
||||||
fun cache_items_without_connection() {
|
fun cache_items_without_connection() {
|
||||||
coEvery { api.getItems(any(), any(), any(), any(), any(), any(), any()) } returns
|
coEvery { api.getItems(any(), any(), any(), any(), any(), any(), any()) } returns
|
||||||
StatusAndData(success = false, data = generateTestApiItem())
|
StatusAndData(success = false, data = generateTestApiItem())
|
||||||
|
|
||||||
initializeRepository(MutableStateFlow(false))
|
initializeRepository(MutableStateFlow(false))
|
||||||
prepareSearch()
|
prepareSearch()
|
||||||
@ -1162,16 +1039,15 @@ class RepositoryTest {
|
|||||||
private fun prepareSearch() {
|
private fun prepareSearch() {
|
||||||
repository.setTagFilter(SelfossModel.Tag("Tag", "read", 0))
|
repository.setTagFilter(SelfossModel.Tag("Tag", "read", 0))
|
||||||
repository.setSourceFilter(
|
repository.setSourceFilter(
|
||||||
SelfossModel.SourceDetail(
|
SelfossModel.Source(
|
||||||
1,
|
1,
|
||||||
"First source",
|
"First source",
|
||||||
5,
|
|
||||||
listOf("Test", "second"),
|
listOf("Test", "second"),
|
||||||
SPOUT,
|
SPOUT,
|
||||||
"",
|
"",
|
||||||
IMAGE_URL_2,
|
IMAGE_URL_2,
|
||||||
SelfossModel.SourceParams("url"),
|
SelfossModel.SourceParams("url")
|
||||||
),
|
)
|
||||||
)
|
)
|
||||||
repository.searchFilter = "search"
|
repository.searchFilter = "search"
|
||||||
}
|
}
|
@ -1,10 +1,11 @@
|
|||||||
package bou.amine.apps.readerforselfossv2.tests.repository
|
package bou.amine.apps.readerforselfossv2.repository
|
||||||
|
|
||||||
import bou.amine.apps.readerforselfossv2.dao.ITEM
|
import bou.amine.apps.readerforselfossv2.dao.ITEM
|
||||||
import bou.amine.apps.readerforselfossv2.model.SelfossModel
|
import bou.amine.apps.readerforselfossv2.model.SelfossModel
|
||||||
|
|
||||||
fun generateTestDBItems(item: FakeItemParameters = FakeItemParameters()): List<ITEM> =
|
|
||||||
listOf(
|
fun generateTestDBItems(item: FakeItemParameters = FakeItemParameters()): List<ITEM> {
|
||||||
|
return listOf(
|
||||||
ITEM(
|
ITEM(
|
||||||
id = item.id,
|
id = item.id,
|
||||||
datetime = item.datetime,
|
datetime = item.datetime,
|
||||||
@ -17,12 +18,13 @@ fun generateTestDBItems(item: FakeItemParameters = FakeItemParameters()): List<I
|
|||||||
link = item.link,
|
link = item.link,
|
||||||
sourcetitle = item.sourcetitle,
|
sourcetitle = item.sourcetitle,
|
||||||
tags = item.tags,
|
tags = item.tags,
|
||||||
author = item.author,
|
author = item.author
|
||||||
),
|
)
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
fun generateTestApiItem(item: FakeItemParameters = FakeItemParameters()): List<SelfossModel.Item> =
|
fun generateTestApiItem(item: FakeItemParameters = FakeItemParameters()): List<SelfossModel.Item> {
|
||||||
listOf(
|
return listOf(
|
||||||
SelfossModel.Item(
|
SelfossModel.Item(
|
||||||
id = item.id.toInt(),
|
id = item.id.toInt(),
|
||||||
datetime = item.datetime,
|
datetime = item.datetime,
|
||||||
@ -35,9 +37,10 @@ fun generateTestApiItem(item: FakeItemParameters = FakeItemParameters()): List<S
|
|||||||
link = item.link,
|
link = item.link,
|
||||||
sourcetitle = item.sourcetitle,
|
sourcetitle = item.sourcetitle,
|
||||||
tags = item.tags.split(','),
|
tags = item.tags.split(','),
|
||||||
author = item.author,
|
author = item.author
|
||||||
),
|
)
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
class FakeItemParameters {
|
class FakeItemParameters {
|
||||||
var id = "20"
|
var id = "20"
|
@ -1,27 +0,0 @@
|
|||||||
package bou.amine.apps.readerforselfossv2.android.tests.robolectric
|
|
||||||
|
|
||||||
import android.view.Menu
|
|
||||||
import android.widget.TextView
|
|
||||||
import androidx.annotation.IdRes
|
|
||||||
import org.junit.Assert.assertTrue
|
|
||||||
import org.robolectric.shadows.ShadowDialog
|
|
||||||
|
|
||||||
fun dialogMessage(): String {
|
|
||||||
val latestDialog = ShadowDialog.getLatestDialog()
|
|
||||||
return latestDialog.findViewById<TextView>(android.R.id.message)?.text.toString()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun Menu.assertClickable(
|
|
||||||
@IdRes id: Int,
|
|
||||||
) {
|
|
||||||
this.assertVisible(id)
|
|
||||||
val item = this.findItem(id)
|
|
||||||
assertTrue(item.isEnabled)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun Menu.assertVisible(
|
|
||||||
@IdRes id: Int,
|
|
||||||
) {
|
|
||||||
val item = this.findItem(id)
|
|
||||||
assertTrue(item.isVisible)
|
|
||||||
}
|
|
@ -1,75 +0,0 @@
|
|||||||
package bou.amine.apps.readerforselfossv2.android.tests.robolectric
|
|
||||||
|
|
||||||
import android.widget.Button
|
|
||||||
import android.widget.EditText
|
|
||||||
import androidx.core.view.isVisible
|
|
||||||
import bou.amine.apps.readerforselfossv2.android.LoginActivity
|
|
||||||
import bou.amine.apps.readerforselfossv2.android.R
|
|
||||||
import com.google.android.material.switchmaterial.SwitchMaterial
|
|
||||||
import org.junit.Assert.assertEquals
|
|
||||||
import org.junit.Test
|
|
||||||
import org.junit.runner.RunWith
|
|
||||||
import org.robolectric.Robolectric
|
|
||||||
|
|
||||||
@RunWith(RobotElectriqueRunner::class)
|
|
||||||
class LoginActivityTest {
|
|
||||||
@Test
|
|
||||||
fun login_shouldDisplay() {
|
|
||||||
Robolectric.buildActivity(LoginActivity::class.java).use { controller ->
|
|
||||||
controller.setup() // Moves the Activity to the RESUMED state
|
|
||||||
|
|
||||||
val activity = controller.get()
|
|
||||||
assert(activity.findViewById<EditText>(R.id.urlView).isVisible)
|
|
||||||
assert(activity.findViewById<SwitchMaterial>(R.id.selfSigned).isVisible)
|
|
||||||
assert(activity.findViewById<SwitchMaterial>(R.id.selfSigned).isChecked.not())
|
|
||||||
assert(activity.findViewById<SwitchMaterial>(R.id.withLogin).isVisible)
|
|
||||||
assert(activity.findViewById<SwitchMaterial>(R.id.withLogin).isChecked.not())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun urlError() {
|
|
||||||
Robolectric.buildActivity(LoginActivity::class.java).use { controller ->
|
|
||||||
controller.setup() // Moves the Activity to the RESUMED state
|
|
||||||
val activity = controller.get()
|
|
||||||
|
|
||||||
val urlView = activity.findViewById<EditText>(R.id.urlView)
|
|
||||||
urlView.setText("172.17.0.1:8888")
|
|
||||||
|
|
||||||
activity.findViewById<Button>(R.id.signInButton).performClick()
|
|
||||||
|
|
||||||
urlView.performClick()
|
|
||||||
assertEquals(activity.getString(R.string.login_url_problem), urlView.error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun multiError() {
|
|
||||||
Robolectric.buildActivity(LoginActivity::class.java).use { controller ->
|
|
||||||
controller.setup() // Moves the Activity to the RESUMED state
|
|
||||||
val activity = controller.get()
|
|
||||||
|
|
||||||
val signInButton = activity.findViewById<Button>(R.id.signInButton)
|
|
||||||
repeat(3) { signInButton.performClick() }
|
|
||||||
|
|
||||||
// Vérifie que l'avertissement est affiché
|
|
||||||
assertEquals(activity.getString(R.string.text_wrong_url), dialogMessage())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* @Test
|
|
||||||
fun connect() {
|
|
||||||
Robolectric.buildActivity(LoginActivity::class.java).use { controller ->
|
|
||||||
controller.setup() // Moves the Activity to the RESUMED state
|
|
||||||
val activity = controller.get()
|
|
||||||
val signInButton = activity.findViewById<Button>(R.id.signInButton)
|
|
||||||
val urlView = activity.findViewById<EditText>(R.id.urlView)
|
|
||||||
urlView.setText("http://10.0.2.2:8888")
|
|
||||||
signInButton.performClick()
|
|
||||||
|
|
||||||
val expectedIntent = Intent(activity, HomeActivity::class.java)
|
|
||||||
val actual = shadowOf(activity).nextStartedActivity
|
|
||||||
assertEquals(expectedIntent.component, actual.component)
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
}
|
|
@ -1,10 +0,0 @@
|
|||||||
package bou.amine.apps.readerforselfossv2.android.tests.robolectric
|
|
||||||
|
|
||||||
import org.robolectric.RobolectricTestRunner
|
|
||||||
import org.robolectric.annotation.Config
|
|
||||||
|
|
||||||
class RobotElectriqueRunner(
|
|
||||||
testClass: Class<*>?,
|
|
||||||
) : RobolectricTestRunner(testClass) {
|
|
||||||
override fun buildGlobalConfig(): Config = Config.Builder().setSdk(25, 30, 33).build()
|
|
||||||
}
|
|
@ -1,26 +1,35 @@
|
|||||||
|
buildscript {
|
||||||
|
dependencies {
|
||||||
|
// SqlDelight
|
||||||
|
classpath("com.squareup.sqldelight:gradle-plugin:1.5.4")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
// trick: for the same plugin versions in all sub-modules
|
//trick: for the same plugin versions in all sub-modules
|
||||||
id("com.android.application").version("8.8.1").apply(false)
|
id("com.android.application").version("7.3.1").apply(false)
|
||||||
id("com.android.library").version("8.8.1").apply(false)
|
id("com.android.library").version("7.3.1").apply(false)
|
||||||
id("org.jetbrains.kotlin.android").version("2.1.0").apply(false)
|
kotlin("android").version("1.7.20").apply(false)
|
||||||
kotlin("multiplatform").version("2.1.0").apply(false)
|
kotlin("multiplatform").version("1.7.20").apply(false)
|
||||||
id("com.mikepenz.aboutlibraries.plugin").version("10.5.1").apply(false)
|
id("com.mikepenz.aboutlibraries.plugin").version("10.5.1").apply(false)
|
||||||
id("org.jetbrains.kotlinx.kover") version "0.9.0" apply true
|
id("org.jetbrains.kotlinx.kover") version "0.6.1"
|
||||||
}
|
}
|
||||||
|
|
||||||
allprojects {
|
allprojects {
|
||||||
repositories {
|
repositories {
|
||||||
|
// maven { url = uri("https://nexus.amine-louveau.fr/repository/maven-public/")}
|
||||||
google()
|
google()
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
|
jcenter()
|
||||||
maven { url = uri("https://www.jitpack.io") }
|
maven { url = uri("https://www.jitpack.io") }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
tasks.register("clean", Delete::class) {
|
tasks.register("clean", Delete::class) {
|
||||||
delete(layout.buildDirectory)
|
delete(rootProject.buildDir)
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
koverMerged {
|
||||||
kover(project(":shared"))
|
enable()
|
||||||
kover(project(":androidApp"))
|
|
||||||
}
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user