mirror of https://github.com/tasks/tasks
Compare commits
No commits in common. 'main' and '9.7.2' have entirely different histories.
@ -1,4 +0,0 @@
|
|||||||
github: abaker
|
|
||||||
liberapay: tasks
|
|
||||||
patreon: tasks
|
|
||||||
custom: tasks.org/donate
|
|
||||||
@ -1,52 +0,0 @@
|
|||||||
name: Assemble bundle
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
workflow_dispatch:
|
|
||||||
workflow_call:
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
check:
|
|
||||||
uses: ./.github/workflows/check.yml
|
|
||||||
bundle:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs: [ check ]
|
|
||||||
steps:
|
|
||||||
- name: Decode Keystore
|
|
||||||
run: |
|
|
||||||
echo ${{ secrets.KEY_STORE }} | base64 -di > "${RUNNER_TEMP}"/keystore.jks
|
|
||||||
- uses: actions/checkout@v6
|
|
||||||
- uses: ruby/setup-ruby@v1
|
|
||||||
with:
|
|
||||||
bundler-cache: true
|
|
||||||
|
|
||||||
- name: Set up JDK 17
|
|
||||||
uses: actions/setup-java@v5
|
|
||||||
with:
|
|
||||||
distribution: 'temurin'
|
|
||||||
java-version: '21'
|
|
||||||
|
|
||||||
- name: Setup Gradle
|
|
||||||
uses: gradle/actions/setup-gradle@v4
|
|
||||||
|
|
||||||
- name: Bundle
|
|
||||||
env:
|
|
||||||
KEY_PATH: ${{ runner.temp }}/keystore.jks
|
|
||||||
KEY_ALIAS: ${{ secrets.KEY_ALIAS }}
|
|
||||||
KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}
|
|
||||||
KEY_STORE_PASSWORD: ${{ secrets.KEY_STORE_PASSWORD }}
|
|
||||||
MAPBOX_KEY: ${{ secrets.MAPBOX_KEY }}
|
|
||||||
GOOGLE_KEY: ${{ secrets.GOOGLE_KEY }}
|
|
||||||
run: bundle exec fastlane bundle
|
|
||||||
- name: Upload artifacts
|
|
||||||
uses: actions/upload-artifact@v6
|
|
||||||
with:
|
|
||||||
name: release
|
|
||||||
path: |
|
|
||||||
app/build/outputs/**
|
|
||||||
wear/build/outputs/**
|
|
||||||
@ -1,96 +0,0 @@
|
|||||||
name: Run automated checks
|
|
||||||
|
|
||||||
on:
|
|
||||||
pull_request:
|
|
||||||
workflow_call:
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
lint:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v6
|
|
||||||
- uses: ruby/setup-ruby@v1
|
|
||||||
with:
|
|
||||||
bundler-cache: true
|
|
||||||
|
|
||||||
- name: Set up JDK 21
|
|
||||||
uses: actions/setup-java@v5
|
|
||||||
with:
|
|
||||||
distribution: 'temurin'
|
|
||||||
java-version: '21'
|
|
||||||
|
|
||||||
- name: Setup Gradle
|
|
||||||
uses: gradle/actions/setup-gradle@v4
|
|
||||||
|
|
||||||
- name: Lint checks
|
|
||||||
run: bundle exec fastlane lint
|
|
||||||
|
|
||||||
- name: Archive lint reports
|
|
||||||
uses: actions/upload-artifact@v6
|
|
||||||
if: ${{ always() }}
|
|
||||||
with:
|
|
||||||
name: lint-reports
|
|
||||||
path: app/build/reports/*.html
|
|
||||||
|
|
||||||
test:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
flavor: [Googleplay, Generic]
|
|
||||||
api-level: [29]
|
|
||||||
steps:
|
|
||||||
- name: checkout
|
|
||||||
uses: actions/checkout@v6
|
|
||||||
|
|
||||||
- name: Set up JDK 21
|
|
||||||
uses: actions/setup-java@v5
|
|
||||||
with:
|
|
||||||
distribution: 'temurin'
|
|
||||||
java-version: '21'
|
|
||||||
|
|
||||||
- name: Setup Gradle
|
|
||||||
uses: gradle/actions/setup-gradle@v4
|
|
||||||
|
|
||||||
- name: Enable KVM
|
|
||||||
run: |
|
|
||||||
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
|
|
||||||
sudo udevadm control --reload-rules
|
|
||||||
sudo udevadm trigger --name-match=kvm
|
|
||||||
|
|
||||||
# - name: AVD cache
|
|
||||||
# uses: actions/cache@v4
|
|
||||||
# id: avd-cache
|
|
||||||
# with:
|
|
||||||
# path: |
|
|
||||||
# ~/.android/avd/*
|
|
||||||
# ~/.android/adb*
|
|
||||||
# key: avd-${{ matrix.api-level }}
|
|
||||||
#
|
|
||||||
# - name: create AVD and generate snapshot for caching
|
|
||||||
# if: steps.avd-cache.outputs.cache-hit != 'true'
|
|
||||||
# uses: reactivecircus/android-emulator-runner@v2
|
|
||||||
# with:
|
|
||||||
# api-level: ${{ matrix.api-level }}
|
|
||||||
# force-avd-creation: false
|
|
||||||
# emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
|
|
||||||
# disable-animations: false
|
|
||||||
# script: echo "Generated AVD snapshot for caching."
|
|
||||||
|
|
||||||
- name: run tests
|
|
||||||
uses: reactivecircus/android-emulator-runner@v2
|
|
||||||
with:
|
|
||||||
api-level: ${{ matrix.api-level }}
|
|
||||||
force-avd-creation: false
|
|
||||||
emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
|
|
||||||
disable-animations: true
|
|
||||||
script: ./gradlew -Pcoverage app:test${{ matrix.flavor }}DebugUnitTest app:connected${{ matrix.flavor }}DebugAndroidTest
|
|
||||||
|
|
||||||
- name: Upload test reports
|
|
||||||
uses: actions/upload-artifact@v6
|
|
||||||
if: ${{ always() }}
|
|
||||||
with:
|
|
||||||
name: test-reports-${{ matrix.flavor }}
|
|
||||||
path: app/build/reports/**
|
|
||||||
@ -1,47 +0,0 @@
|
|||||||
name: Update Dependency Diff
|
|
||||||
|
|
||||||
concurrency:
|
|
||||||
group: ${{ github.workflow }}-${{ github.ref }}
|
|
||||||
cancel-in-progress: true
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
paths:
|
|
||||||
- 'gradle/libs.versions.toml'
|
|
||||||
pull_request:
|
|
||||||
paths:
|
|
||||||
- 'gradle/libs.versions.toml'
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
update-deps:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
permissions:
|
|
||||||
contents: write
|
|
||||||
pull-requests: write
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v6
|
|
||||||
with:
|
|
||||||
ref: ${{ github.head_ref }}
|
|
||||||
|
|
||||||
- name: Set up JDK
|
|
||||||
uses: actions/setup-java@v5
|
|
||||||
with:
|
|
||||||
distribution: 'temurin'
|
|
||||||
java-version: '21'
|
|
||||||
|
|
||||||
- name: Setup Gradle
|
|
||||||
uses: gradle/actions/setup-gradle@v4
|
|
||||||
|
|
||||||
- name: Update dependency diffs
|
|
||||||
run: ./update_dependency_diff
|
|
||||||
|
|
||||||
- name: Commit changes
|
|
||||||
run: |
|
|
||||||
git config --local user.email "github-actions[bot]@users.noreply.github.com"
|
|
||||||
git config --local user.name "github-actions[bot]"
|
|
||||||
git add deps_*.txt
|
|
||||||
git diff --staged --quiet || git commit -m "Update dependency diffs"
|
|
||||||
git push
|
|
||||||
@ -1,32 +0,0 @@
|
|||||||
name: Deploy
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
|
|
||||||
env:
|
|
||||||
FASTLANE: ${{ secrets.FASTLANE }}
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
bundle:
|
|
||||||
uses: ./.github/workflows/bundle.yml
|
|
||||||
secrets: inherit
|
|
||||||
deploy:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs: [ bundle ]
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v6
|
|
||||||
- name: Fastlane key
|
|
||||||
run: |
|
|
||||||
echo "$FASTLANE" > ./fastlane.json
|
|
||||||
- uses: ruby/setup-ruby@v1
|
|
||||||
with:
|
|
||||||
bundler-cache: true
|
|
||||||
- uses: actions/download-artifact@v7
|
|
||||||
with:
|
|
||||||
name: release
|
|
||||||
path: .
|
|
||||||
- name: Deploy
|
|
||||||
run: bundle exec fastlane deploy
|
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="CMakeSettings">
|
||||||
|
<configurations>
|
||||||
|
<configuration PROFILE_NAME="Debug" CONFIG_NAME="Debug" />
|
||||||
|
</configurations>
|
||||||
|
</component>
|
||||||
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" project-jdk-name="1.8" project-jdk-type="JavaSDK">
|
||||||
|
<output url="file://$PROJECT_DIR$/build/classes" />
|
||||||
|
</component>
|
||||||
|
<component name="ProjectType">
|
||||||
|
<option name="id" value="Android" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
@ -1 +0,0 @@
|
|||||||
3.4.8
|
|
||||||
@ -1,70 +0,0 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
|
||||||
<configuration default="false" name="wear" type="AndroidRunConfigurationType" factoryName="Android App" activateToolWindowBeforeRun="false" singleton="true">
|
|
||||||
<module name="tasks.Tasks.wear.main" />
|
|
||||||
<option name="DEPLOY" value="true" />
|
|
||||||
<option name="DEPLOY_APK_FROM_BUNDLE" value="false" />
|
|
||||||
<option name="DEPLOY_AS_INSTANT" value="false" />
|
|
||||||
<option name="ARTIFACT_NAME" value="" />
|
|
||||||
<option name="PM_INSTALL_OPTIONS" value="" />
|
|
||||||
<option name="ALL_USERS" value="false" />
|
|
||||||
<option name="ALWAYS_INSTALL_WITH_PM" value="false" />
|
|
||||||
<option name="CLEAR_APP_STORAGE" value="false" />
|
|
||||||
<option name="DYNAMIC_FEATURES_DISABLED_LIST" value="" />
|
|
||||||
<option name="ACTIVITY_EXTRA_FLAGS" value="" />
|
|
||||||
<option name="MODE" value="default_activity" />
|
|
||||||
<option name="RESTORE_ENABLED" value="false" />
|
|
||||||
<option name="RESTORE_FILE" value="" />
|
|
||||||
<option name="CLEAR_LOGCAT" value="true" />
|
|
||||||
<option name="SHOW_LOGCAT_AUTOMATICALLY" value="true" />
|
|
||||||
<option name="TARGET_SELECTION_MODE" value="DEVICE_AND_SNAPSHOT_COMBO_BOX" />
|
|
||||||
<option name="SELECTED_CLOUD_MATRIX_CONFIGURATION_ID" value="-1" />
|
|
||||||
<option name="SELECTED_CLOUD_MATRIX_PROJECT_ID" value="" />
|
|
||||||
<option name="DEBUGGER_TYPE" value="Auto" />
|
|
||||||
<Auto>
|
|
||||||
<option name="USE_JAVA_AWARE_DEBUGGER" value="false" />
|
|
||||||
<option name="SHOW_STATIC_VARS" value="true" />
|
|
||||||
<option name="WORKING_DIR" value="" />
|
|
||||||
<option name="TARGET_LOGGING_CHANNELS" value="lldb process:gdb-remote packets" />
|
|
||||||
<option name="SHOW_OPTIMIZED_WARNING" value="true" />
|
|
||||||
<option name="ATTACH_ON_WAIT_FOR_DEBUGGER" value="false" />
|
|
||||||
<option name="DEBUG_SANDBOX_SDK" value="false" />
|
|
||||||
</Auto>
|
|
||||||
<Hybrid>
|
|
||||||
<option name="USE_JAVA_AWARE_DEBUGGER" value="false" />
|
|
||||||
<option name="SHOW_STATIC_VARS" value="true" />
|
|
||||||
<option name="WORKING_DIR" value="" />
|
|
||||||
<option name="TARGET_LOGGING_CHANNELS" value="lldb process:gdb-remote packets" />
|
|
||||||
<option name="SHOW_OPTIMIZED_WARNING" value="true" />
|
|
||||||
<option name="ATTACH_ON_WAIT_FOR_DEBUGGER" value="false" />
|
|
||||||
<option name="DEBUG_SANDBOX_SDK" value="false" />
|
|
||||||
</Hybrid>
|
|
||||||
<Java>
|
|
||||||
<option name="ATTACH_ON_WAIT_FOR_DEBUGGER" value="false" />
|
|
||||||
<option name="DEBUG_SANDBOX_SDK" value="false" />
|
|
||||||
</Java>
|
|
||||||
<Native>
|
|
||||||
<option name="USE_JAVA_AWARE_DEBUGGER" value="false" />
|
|
||||||
<option name="SHOW_STATIC_VARS" value="true" />
|
|
||||||
<option name="WORKING_DIR" value="" />
|
|
||||||
<option name="TARGET_LOGGING_CHANNELS" value="lldb process:gdb-remote packets" />
|
|
||||||
<option name="SHOW_OPTIMIZED_WARNING" value="true" />
|
|
||||||
<option name="ATTACH_ON_WAIT_FOR_DEBUGGER" value="false" />
|
|
||||||
<option name="DEBUG_SANDBOX_SDK" value="false" />
|
|
||||||
</Native>
|
|
||||||
<Profilers>
|
|
||||||
<option name="ADVANCED_PROFILING_ENABLED" value="false" />
|
|
||||||
<option name="STARTUP_PROFILING_ENABLED" value="false" />
|
|
||||||
<option name="STARTUP_CPU_PROFILING_ENABLED" value="false" />
|
|
||||||
<option name="STARTUP_CPU_PROFILING_CONFIGURATION_NAME" value="Java/Kotlin Method Sample (legacy)" />
|
|
||||||
<option name="STARTUP_NATIVE_MEMORY_PROFILING_ENABLED" value="false" />
|
|
||||||
<option name="NATIVE_MEMORY_SAMPLE_RATE_BYTES" value="2048" />
|
|
||||||
</Profilers>
|
|
||||||
<option name="DEEP_LINK" value="" />
|
|
||||||
<option name="ACTIVITY_CLASS" value="" />
|
|
||||||
<option name="SEARCH_ACTIVITY_IN_GLOBAL_SCOPE" value="false" />
|
|
||||||
<option name="SKIP_ACTIVITY_VALIDATION" value="false" />
|
|
||||||
<method v="2">
|
|
||||||
<option name="Android.Gradle.BeforeRunTask" enabled="true" />
|
|
||||||
</method>
|
|
||||||
</configuration>
|
|
||||||
</component>
|
|
||||||
@ -0,0 +1,99 @@
|
|||||||
|
# https://github.com/andstatus/todoagenda/blob/aa2bc56effa379e145eecb104cc5bac33975b7aa/.travis.yml
|
||||||
|
# Based on https://travis-ci.org/ankidroid/Anki-Android/builds/624268367
|
||||||
|
# See also https://travis-ci.community/t/is-android-28-emulator-supported/1718/6
|
||||||
|
sudo: true
|
||||||
|
language: bash
|
||||||
|
# ignored on non-linux platforms, but bionic is required for nested virtualization
|
||||||
|
dist: bionic
|
||||||
|
|
||||||
|
stages:
|
||||||
|
- install
|
||||||
|
- test
|
||||||
|
- cache
|
||||||
|
|
||||||
|
env:
|
||||||
|
global:
|
||||||
|
- COMPILE_API=29
|
||||||
|
- ANDROID_BUILD_TOOLS=29.0.2
|
||||||
|
- ABI=x86_64
|
||||||
|
- ADB_INSTALL_TIMEOUT=8
|
||||||
|
- ANDROID_HOME=${HOME}/android-sdk
|
||||||
|
- ANDROID_TOOLS_URL="https://dl.google.com/android/repository/sdk-tools-linux-4333796.zip"
|
||||||
|
- EMU_FLAVOR=default # use google_apis flavor if no default flavor emulator
|
||||||
|
- GRAVIS="https://raw.githubusercontent.com/DanySK/Gravis-CI/master/"
|
||||||
|
- JDK="1.8"
|
||||||
|
- TOOLS=${ANDROID_HOME}/tools
|
||||||
|
# PATH order is incredibly important. e.g. the 'emulator' script exists in more than one place!
|
||||||
|
- PATH=${ANDROID_HOME}:${ANDROID_HOME}/emulator:${TOOLS}:${TOOLS}/bin:${ANDROID_HOME}/platform-tools:${PATH}
|
||||||
|
matrix:
|
||||||
|
- API=29
|
||||||
|
|
||||||
|
before_install:
|
||||||
|
# This section may run on all platforms, and may run for unit tests or for coverage finalization
|
||||||
|
# It should not make assumptions about os platform or desired tool installation
|
||||||
|
|
||||||
|
# Set up JDK 8 for Android SDK - Java is universally needed: codacy, unit tests, emulators
|
||||||
|
- curl "${GRAVIS}.install-jdk-travis.sh" --output ~/.install-jdk-travis.sh
|
||||||
|
- export TARGET_JDK="${JDK}"
|
||||||
|
- JDK="1.8"
|
||||||
|
- source ~/.install-jdk-travis.sh
|
||||||
|
|
||||||
|
# Set up Android SDK - this is needed everywhere but coverage finalization, so toggle on that
|
||||||
|
- wget -q "${ANDROID_TOOLS_URL}" -O android-sdk-tools.zip
|
||||||
|
- unzip -q android-sdk-tools.zip -d ${ANDROID_HOME}
|
||||||
|
- rm android-sdk-tools.zip
|
||||||
|
- mkdir ~/.android # avoid harmless sdkmanager warning
|
||||||
|
- echo 'count=0' > ~/.android/repositories.cfg # avoid harmless sdkmanager warning
|
||||||
|
- yes | sdkmanager --licenses >/dev/null # accept all sdkmanager warnings
|
||||||
|
- echo y | sdkmanager --no_https "platform-tools" >/dev/null
|
||||||
|
- echo y | sdkmanager --no_https "tools" >/dev/null # A second time per Travis docs, gets latest versions
|
||||||
|
- echo y | sdkmanager --no_https "build-tools;${ANDROID_BUILD_TOOLS}" >/dev/null # Implicit gradle dependency - gradle drives changes
|
||||||
|
- echo y | sdkmanager --no_https "platforms;android-${COMPILE_API}" >/dev/null # We need the API of the current compileSdkVersion from gradle.properties
|
||||||
|
|
||||||
|
install:
|
||||||
|
# In our setup, install only runs on matrix entries we want full emulator tests on
|
||||||
|
# That only happens currently on linux, so this section can assume linux + emulator is desired
|
||||||
|
# Download required emulator tools
|
||||||
|
- echo y | sdkmanager --no_https "platforms;android-$API" >/dev/null # We need the API of the emulator we will run
|
||||||
|
- echo y | sdkmanager --no_https "emulator" >/dev/null
|
||||||
|
- echo y | sdkmanager --no_https "system-images;android-$API;$EMU_FLAVOR;$ABI" >/dev/null # install our emulator
|
||||||
|
|
||||||
|
# Set up KVM on linux for hardware acceleration. Manually here so it only happens for emulator tests, takes ~30s
|
||||||
|
- sudo -E apt-get -yq --no-install-suggests --no-install-recommends install bridge-utils libpulse0 libvirt-bin qemu-kvm virtinst ubuntu-vm-builder
|
||||||
|
- sudo adduser $USER libvirt
|
||||||
|
- sudo adduser $USER kvm
|
||||||
|
|
||||||
|
# Create an Android emulator
|
||||||
|
- echo no | avdmanager create avd --force -n test -k "system-images;android-$API;$EMU_FLAVOR;$ABI" -c 10M
|
||||||
|
- |
|
||||||
|
EMU_PARAMS="-verbose -no-snapshot -no-window -camera-back none -camera-front none -selinux permissive -qemu -m 2048"
|
||||||
|
EMU_COMMAND="emulator"
|
||||||
|
# This double "sudo" monstrosity is used to have Travis execute the
|
||||||
|
# emulator with its new group permissions and help preserve the rule
|
||||||
|
# of least privilege.
|
||||||
|
sudo -E sudo -u $USER -E bash -c "${ANDROID_HOME}/emulator/${EMU_COMMAND} -avd test ${AUDIO} ${EMU_PARAMS} &"
|
||||||
|
|
||||||
|
# Wait for emulator to be ready
|
||||||
|
- ./.wait_for_emulator.sh
|
||||||
|
- adb shell input keyevent 82 &
|
||||||
|
|
||||||
|
# Switch back to our target JDK version to build and run tests
|
||||||
|
- JDK="${TARGET_JDK}"
|
||||||
|
- source ~/.install-jdk-travis.sh
|
||||||
|
|
||||||
|
script:
|
||||||
|
- ./gradlew :app:lintGoogleplayRelease
|
||||||
|
- ./gradlew :app:jacocoTestReportGoogleplayDebug
|
||||||
|
- ./gradlew -Pcoverage :app:createGoogleplayDebugAndroidTestCoverageReport
|
||||||
|
|
||||||
|
before_cache:
|
||||||
|
- rm -f $HOME/.gradle/caches/modules-2/modules-2.lock
|
||||||
|
- curl "${GRAVIS}.clean_gradle_cache.sh" --output ~/.clean_gradle_cache.sh
|
||||||
|
- bash ~/.clean_gradle_cache.sh > /dev/null
|
||||||
|
|
||||||
|
cache:
|
||||||
|
directories:
|
||||||
|
- $HOME/.gradle/caches/
|
||||||
|
- $HOME/.gradle/wrapper/
|
||||||
|
after_success:
|
||||||
|
- bash <(curl -s https://codecov.io/bash)
|
||||||
@ -0,0 +1,24 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set +e
|
||||||
|
|
||||||
|
bootanim=""
|
||||||
|
failcounter=0
|
||||||
|
timeout_in_sec=600 # 10 minutes
|
||||||
|
|
||||||
|
until [[ "$bootanim" =~ "stopped" ]]; do
|
||||||
|
bootanim=`adb -e shell getprop init.svc.bootanim 2>&1 &`
|
||||||
|
if [[ "$bootanim" =~ "device not found" || "$bootanim" =~ "device offline"
|
||||||
|
|| "$bootanim" =~ "running" || "$bootanim" =~ "error: no emulators found" ]]; then
|
||||||
|
let "failcounter += 1"
|
||||||
|
echo "Waiting for emulator to start"
|
||||||
|
if [[ $failcounter -gt timeout_in_sec ]]; then
|
||||||
|
echo "Timeout ($timeout_in_sec seconds) reached; failed to start emulator"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "Emulator is ready"
|
||||||
|
|
||||||
File diff suppressed because it is too large
Load Diff
@ -1,76 +0,0 @@
|
|||||||
# Contributor Covenant Code of Conduct
|
|
||||||
|
|
||||||
## Our Pledge
|
|
||||||
|
|
||||||
In the interest of fostering an open and welcoming environment, we as
|
|
||||||
contributors and maintainers pledge to making participation in our project and
|
|
||||||
our community a harassment-free experience for everyone, regardless of age, body
|
|
||||||
size, disability, ethnicity, sex characteristics, gender identity and expression,
|
|
||||||
level of experience, education, socio-economic status, nationality, personal
|
|
||||||
appearance, race, religion, or sexual identity and orientation.
|
|
||||||
|
|
||||||
## Our Standards
|
|
||||||
|
|
||||||
Examples of behavior that contributes to creating a positive environment
|
|
||||||
include:
|
|
||||||
|
|
||||||
* Using welcoming and inclusive language
|
|
||||||
* Being respectful of differing viewpoints and experiences
|
|
||||||
* Gracefully accepting constructive criticism
|
|
||||||
* Focusing on what is best for the community
|
|
||||||
* Showing empathy towards other community members
|
|
||||||
|
|
||||||
Examples of unacceptable behavior by participants include:
|
|
||||||
|
|
||||||
* The use of sexualized language or imagery and unwelcome sexual attention or
|
|
||||||
advances
|
|
||||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
|
||||||
* Public or private harassment
|
|
||||||
* Publishing others' private information, such as a physical or electronic
|
|
||||||
address, without explicit permission
|
|
||||||
* Other conduct which could reasonably be considered inappropriate in a
|
|
||||||
professional setting
|
|
||||||
|
|
||||||
## Our Responsibilities
|
|
||||||
|
|
||||||
Project maintainers are responsible for clarifying the standards of acceptable
|
|
||||||
behavior and are expected to take appropriate and fair corrective action in
|
|
||||||
response to any instances of unacceptable behavior.
|
|
||||||
|
|
||||||
Project maintainers have the right and responsibility to remove, edit, or
|
|
||||||
reject comments, commits, code, wiki edits, issues, and other contributions
|
|
||||||
that are not aligned to this Code of Conduct, or to ban temporarily or
|
|
||||||
permanently any contributor for other behaviors that they deem inappropriate,
|
|
||||||
threatening, offensive, or harmful.
|
|
||||||
|
|
||||||
## Scope
|
|
||||||
|
|
||||||
This Code of Conduct applies both within project spaces and in public spaces
|
|
||||||
when an individual is representing the project or its community. Examples of
|
|
||||||
representing a project or community include using an official project e-mail
|
|
||||||
address, posting via an official social media account, or acting as an appointed
|
|
||||||
representative at an online or offline event. Representation of a project may be
|
|
||||||
further defined and clarified by project maintainers.
|
|
||||||
|
|
||||||
## Enforcement
|
|
||||||
|
|
||||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
|
||||||
reported by contacting the project team at github@tasks.org. All
|
|
||||||
complaints will be reviewed and investigated and will result in a response that
|
|
||||||
is deemed necessary and appropriate to the circumstances. The project team is
|
|
||||||
obligated to maintain confidentiality with regard to the reporter of an incident.
|
|
||||||
Further details of specific enforcement policies may be posted separately.
|
|
||||||
|
|
||||||
Project maintainers who do not follow or enforce the Code of Conduct in good
|
|
||||||
faith may face temporary or permanent repercussions as determined by other
|
|
||||||
members of the project's leadership.
|
|
||||||
|
|
||||||
## Attribution
|
|
||||||
|
|
||||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
|
||||||
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
|
|
||||||
|
|
||||||
[homepage]: https://www.contributor-covenant.org
|
|
||||||
|
|
||||||
For answers to common questions about this code of conduct, see
|
|
||||||
https://www.contributor-covenant.org/faq
|
|
||||||
@ -1,34 +0,0 @@
|
|||||||
### Translation
|
|
||||||
|
|
||||||
You can translate Tasks using [Weblate](https://hosted.weblate.org/projects/tasks/android). To get started, register a new account or login with your GitHub account if you have one.
|
|
||||||
|
|
||||||
### Opening issues
|
|
||||||
|
|
||||||
Before opening an issue, please make sure that your issue:
|
|
||||||
- is not a duplicate (i.e. it has not been reported before, closed or open)
|
|
||||||
- has not been fixed
|
|
||||||
- is in English (issues in a language other than English will be closed unless someone translates them)
|
|
||||||
- does not contain multiple feature requests/bug reports. Please open a separate issue for each one.
|
|
||||||
|
|
||||||
### Code contribution
|
|
||||||
|
|
||||||
#### To get started with development:
|
|
||||||
1. [Fork](https://help.github.com/articles/fork-a-repo/) and [clone](https://help.github.com/articles/cloning-a-repository/) the repository
|
|
||||||
2. Install and launch [Android Studio's canary build](https://developer.android.com/studio/preview) (Tasks depends on some bleeding-edge features of the canary build, but in the future when those features are stabilized, you will be able to use the stable release of Android Studio)
|
|
||||||
3. Select `File > Open`, select the Tasks directory, and accept prompts to install missing SDK components
|
|
||||||
|
|
||||||
#### Set up Mapbox
|
|
||||||
1. Register at [mapbox.com](https://www.mapbox.com)
|
|
||||||
2. Add `tasks_mapbox_key_debug="<your_api_key>"` to your [`gradle.properties`](https://docs.gradle.org/current/userguide/build_environment.html#sec:gradle_configuration_properties) file. You can create an access token or use your [default public token](https://docs.mapbox.com/help/glossary/access-token/#default-public-token)
|
|
||||||
|
|
||||||
#### Set up Google Tasks and Google Drive
|
|
||||||
1. Register at [cloud.google.com](https://cloud.google.com)
|
|
||||||
2. Enable [Google Tasks API](https://console.cloud.google.com/apis/library/tasks.googleapis.com) and [Google Drive API](https://console.cloud.google.com/apis/library/drive.googleapis.com)
|
|
||||||
3. [Create android authorization credentials](https://developers.google.com/identity/protocols/OAuth2InstalledApp#creatingcred)
|
|
||||||
|
|
||||||
#### Set up Google Maps and Google Places
|
|
||||||
1. Register at [cloud.google.com](https://cloud.google.com)
|
|
||||||
2. Enable [Google Maps SDK](https://console.cloud.google.com/apis/library/maps-android-backend.googleapis.com) and [Google Places API](https://console.cloud.google.com/apis/library/places-backend.googleapis.com)
|
|
||||||
3. [Set up an API key](https://cloud.google.com/video-intelligence/docs/common/auth#set_up_an_api_key)
|
|
||||||
4. Add `tasks_google_key_debug="<your_api_key>"` to your [`gradle.properties`](https://docs.gradle.org/current/userguide/build_environment.html#sec:gradle_configuration_properties) file
|
|
||||||
5. Select `Build > Select Build Variant` and choose the `googleplay` variant
|
|
||||||
@ -1,230 +1,161 @@
|
|||||||
GEM
|
GEM
|
||||||
remote: https://rubygems.org/
|
remote: https://rubygems.org/
|
||||||
specs:
|
specs:
|
||||||
CFPropertyList (3.0.7)
|
CFPropertyList (3.0.2)
|
||||||
base64
|
addressable (2.7.0)
|
||||||
nkf
|
public_suffix (>= 2.0.2, < 5.0)
|
||||||
rexml
|
|
||||||
abbrev (0.1.2)
|
|
||||||
addressable (2.8.7)
|
|
||||||
public_suffix (>= 2.0.2, < 7.0)
|
|
||||||
artifactory (3.0.17)
|
|
||||||
atomos (0.1.3)
|
atomos (0.1.3)
|
||||||
aws-eventstream (1.4.0)
|
babosa (1.0.3)
|
||||||
aws-partitions (1.1196.0)
|
claide (1.0.3)
|
||||||
aws-sdk-core (3.240.0)
|
|
||||||
aws-eventstream (~> 1, >= 1.3.0)
|
|
||||||
aws-partitions (~> 1, >= 1.992.0)
|
|
||||||
aws-sigv4 (~> 1.9)
|
|
||||||
base64
|
|
||||||
bigdecimal
|
|
||||||
jmespath (~> 1, >= 1.6.1)
|
|
||||||
logger
|
|
||||||
aws-sdk-kms (1.118.0)
|
|
||||||
aws-sdk-core (~> 3, >= 3.239.1)
|
|
||||||
aws-sigv4 (~> 1.5)
|
|
||||||
aws-sdk-s3 (1.208.0)
|
|
||||||
aws-sdk-core (~> 3, >= 3.234.0)
|
|
||||||
aws-sdk-kms (~> 1)
|
|
||||||
aws-sigv4 (~> 1.5)
|
|
||||||
aws-sigv4 (1.12.1)
|
|
||||||
aws-eventstream (~> 1, >= 1.0.2)
|
|
||||||
babosa (1.0.4)
|
|
||||||
base64 (0.3.0)
|
|
||||||
bigdecimal (4.0.1)
|
|
||||||
claide (1.1.0)
|
|
||||||
colored (1.2)
|
colored (1.2)
|
||||||
colored2 (3.1.2)
|
colored2 (3.1.2)
|
||||||
commander (4.6.0)
|
commander-fastlane (4.4.6)
|
||||||
highline (~> 2.0.0)
|
highline (~> 1.7.2)
|
||||||
declarative (0.0.20)
|
declarative (0.0.10)
|
||||||
digest-crc (0.7.0)
|
declarative-option (0.1.0)
|
||||||
rake (>= 12.0.0, < 14.0.0)
|
digest-crc (0.4.1)
|
||||||
domain_name (0.6.20240107)
|
domain_name (0.5.20190701)
|
||||||
dotenv (2.8.1)
|
unf (>= 0.0.5, < 1.0.0)
|
||||||
emoji_regex (3.2.3)
|
dotenv (2.7.5)
|
||||||
excon (0.112.0)
|
emoji_regex (1.0.1)
|
||||||
faraday (1.10.4)
|
excon (0.72.0)
|
||||||
faraday-em_http (~> 1.0)
|
faraday (0.17.3)
|
||||||
faraday-em_synchrony (~> 1.0)
|
multipart-post (>= 1.2, < 3)
|
||||||
faraday-excon (~> 1.1)
|
faraday-cookie_jar (0.0.6)
|
||||||
faraday-httpclient (~> 1.0)
|
faraday (>= 0.7.4)
|
||||||
faraday-multipart (~> 1.0)
|
|
||||||
faraday-net_http (~> 1.0)
|
|
||||||
faraday-net_http_persistent (~> 1.0)
|
|
||||||
faraday-patron (~> 1.0)
|
|
||||||
faraday-rack (~> 1.0)
|
|
||||||
faraday-retry (~> 1.0)
|
|
||||||
ruby2_keywords (>= 0.0.4)
|
|
||||||
faraday-cookie_jar (0.0.7)
|
|
||||||
faraday (>= 0.8.0)
|
|
||||||
http-cookie (~> 1.0.0)
|
http-cookie (~> 1.0.0)
|
||||||
faraday-em_http (1.0.0)
|
faraday_middleware (0.13.1)
|
||||||
faraday-em_synchrony (1.0.1)
|
faraday (>= 0.7.4, < 1.0)
|
||||||
faraday-excon (1.1.0)
|
fastimage (2.1.7)
|
||||||
faraday-httpclient (1.0.1)
|
fastlane (2.141.0)
|
||||||
faraday-multipart (1.1.1)
|
|
||||||
multipart-post (~> 2.0)
|
|
||||||
faraday-net_http (1.0.2)
|
|
||||||
faraday-net_http_persistent (1.2.0)
|
|
||||||
faraday-patron (1.0.0)
|
|
||||||
faraday-rack (1.0.0)
|
|
||||||
faraday-retry (1.0.3)
|
|
||||||
faraday_middleware (1.2.1)
|
|
||||||
faraday (~> 1.0)
|
|
||||||
fastimage (2.4.0)
|
|
||||||
fastlane (2.228.0)
|
|
||||||
CFPropertyList (>= 2.3, < 4.0.0)
|
CFPropertyList (>= 2.3, < 4.0.0)
|
||||||
addressable (>= 2.8, < 3.0.0)
|
addressable (>= 2.3, < 3.0.0)
|
||||||
artifactory (~> 3.0)
|
babosa (>= 1.0.2, < 2.0.0)
|
||||||
aws-sdk-s3 (~> 1.0)
|
|
||||||
babosa (>= 1.0.3, < 2.0.0)
|
|
||||||
bundler (>= 1.12.0, < 3.0.0)
|
bundler (>= 1.12.0, < 3.0.0)
|
||||||
colored (~> 1.2)
|
colored
|
||||||
commander (~> 4.6)
|
commander-fastlane (>= 4.4.6, < 5.0.0)
|
||||||
dotenv (>= 2.1.1, < 3.0.0)
|
dotenv (>= 2.1.1, < 3.0.0)
|
||||||
emoji_regex (>= 0.1, < 4.0)
|
emoji_regex (>= 0.1, < 2.0)
|
||||||
excon (>= 0.71.0, < 1.0.0)
|
excon (>= 0.71.0, < 1.0.0)
|
||||||
faraday (~> 1.0)
|
faraday (~> 0.17)
|
||||||
faraday-cookie_jar (~> 0.0.6)
|
faraday-cookie_jar (~> 0.0.6)
|
||||||
faraday_middleware (~> 1.0)
|
faraday_middleware (~> 0.13.1)
|
||||||
fastimage (>= 2.1.0, < 3.0.0)
|
fastimage (>= 2.1.0, < 3.0.0)
|
||||||
fastlane-sirp (>= 1.0.0)
|
|
||||||
gh_inspector (>= 1.1.2, < 2.0.0)
|
gh_inspector (>= 1.1.2, < 2.0.0)
|
||||||
google-apis-androidpublisher_v3 (~> 0.3)
|
google-api-client (>= 0.29.2, < 0.37.0)
|
||||||
google-apis-playcustomapp_v1 (~> 0.1)
|
google-cloud-storage (>= 1.15.0, < 2.0.0)
|
||||||
google-cloud-env (>= 1.6.0, < 2.0.0)
|
highline (>= 1.7.2, < 2.0.0)
|
||||||
google-cloud-storage (~> 1.31)
|
|
||||||
highline (~> 2.0)
|
|
||||||
http-cookie (~> 1.0.5)
|
|
||||||
json (< 3.0.0)
|
json (< 3.0.0)
|
||||||
jwt (>= 2.1.0, < 3)
|
jwt (~> 2.1.0)
|
||||||
mini_magick (>= 4.9.4, < 5.0.0)
|
mini_magick (>= 4.9.4, < 5.0.0)
|
||||||
multipart-post (>= 2.0.0, < 3.0.0)
|
multi_xml (~> 0.5)
|
||||||
naturally (~> 2.2)
|
multipart-post (~> 2.0.0)
|
||||||
optparse (>= 0.1.1, < 1.0.0)
|
|
||||||
plist (>= 3.1.0, < 4.0.0)
|
plist (>= 3.1.0, < 4.0.0)
|
||||||
rubyzip (>= 2.0.0, < 3.0.0)
|
public_suffix (~> 2.0.0)
|
||||||
security (= 0.1.5)
|
rubyzip (>= 1.3.0, < 2.0.0)
|
||||||
|
security (= 0.1.3)
|
||||||
simctl (~> 1.6.3)
|
simctl (~> 1.6.3)
|
||||||
|
slack-notifier (>= 2.0.0, < 3.0.0)
|
||||||
terminal-notifier (>= 2.0.0, < 3.0.0)
|
terminal-notifier (>= 2.0.0, < 3.0.0)
|
||||||
terminal-table (~> 3)
|
terminal-table (>= 1.4.5, < 2.0.0)
|
||||||
tty-screen (>= 0.6.3, < 1.0.0)
|
tty-screen (>= 0.6.3, < 1.0.0)
|
||||||
tty-spinner (>= 0.8.0, < 1.0.0)
|
tty-spinner (>= 0.8.0, < 1.0.0)
|
||||||
word_wrap (~> 1.0.0)
|
word_wrap (~> 1.0.0)
|
||||||
xcodeproj (>= 1.13.0, < 2.0.0)
|
xcodeproj (>= 1.13.0, < 2.0.0)
|
||||||
xcpretty (~> 0.4.1)
|
xcpretty (~> 0.3.0)
|
||||||
xcpretty-travis-formatter (>= 0.0.3, < 2.0.0)
|
xcpretty-travis-formatter (>= 0.0.3)
|
||||||
fastlane-sirp (1.0.0)
|
|
||||||
sysrandom (~> 1.0)
|
|
||||||
gh_inspector (1.1.3)
|
gh_inspector (1.1.3)
|
||||||
google-apis-androidpublisher_v3 (0.54.0)
|
google-api-client (0.36.4)
|
||||||
google-apis-core (>= 0.11.0, < 2.a)
|
|
||||||
google-apis-core (0.11.3)
|
|
||||||
addressable (~> 2.5, >= 2.5.1)
|
addressable (~> 2.5, >= 2.5.1)
|
||||||
googleauth (>= 0.16.2, < 2.a)
|
googleauth (~> 0.9)
|
||||||
httpclient (>= 2.8.1, < 3.a)
|
httpclient (>= 2.8.1, < 3.0)
|
||||||
mini_mime (~> 1.0)
|
mini_mime (~> 1.0)
|
||||||
representable (~> 3.0)
|
representable (~> 3.0)
|
||||||
retriable (>= 2.0, < 4.a)
|
retriable (>= 2.0, < 4.0)
|
||||||
rexml
|
signet (~> 0.12)
|
||||||
google-apis-iamcredentials_v1 (0.17.0)
|
google-cloud-core (1.5.0)
|
||||||
google-apis-core (>= 0.11.0, < 2.a)
|
google-cloud-env (~> 1.0)
|
||||||
google-apis-playcustomapp_v1 (0.13.0)
|
|
||||||
google-apis-core (>= 0.11.0, < 2.a)
|
|
||||||
google-apis-storage_v1 (0.31.0)
|
|
||||||
google-apis-core (>= 0.11.0, < 2.a)
|
|
||||||
google-cloud-core (1.8.0)
|
|
||||||
google-cloud-env (>= 1.0, < 3.a)
|
|
||||||
google-cloud-errors (~> 1.0)
|
google-cloud-errors (~> 1.0)
|
||||||
google-cloud-env (1.6.0)
|
google-cloud-env (1.3.0)
|
||||||
faraday (>= 0.17.3, < 3.0)
|
faraday (~> 0.11)
|
||||||
google-cloud-errors (1.5.0)
|
google-cloud-errors (1.0.0)
|
||||||
google-cloud-storage (1.47.0)
|
google-cloud-storage (1.25.1)
|
||||||
addressable (~> 2.8)
|
addressable (~> 2.5)
|
||||||
digest-crc (~> 0.4)
|
digest-crc (~> 0.4)
|
||||||
google-apis-iamcredentials_v1 (~> 0.1)
|
google-api-client (~> 0.33)
|
||||||
google-apis-storage_v1 (~> 0.31.0)
|
google-cloud-core (~> 1.2)
|
||||||
google-cloud-core (~> 1.6)
|
googleauth (~> 0.9)
|
||||||
googleauth (>= 0.16.2, < 2.a)
|
|
||||||
mini_mime (~> 1.0)
|
mini_mime (~> 1.0)
|
||||||
googleauth (1.8.1)
|
googleauth (0.10.0)
|
||||||
faraday (>= 0.17.3, < 3.a)
|
faraday (~> 0.12)
|
||||||
jwt (>= 1.4, < 3.0)
|
jwt (>= 1.4, < 3.0)
|
||||||
|
memoist (~> 0.16)
|
||||||
multi_json (~> 1.11)
|
multi_json (~> 1.11)
|
||||||
os (>= 0.9, < 2.0)
|
os (>= 0.9, < 2.0)
|
||||||
signet (>= 0.16, < 2.a)
|
signet (~> 0.12)
|
||||||
highline (2.0.3)
|
highline (1.7.10)
|
||||||
http-cookie (1.0.8)
|
http-cookie (1.0.3)
|
||||||
domain_name (~> 0.5)
|
domain_name (~> 0.5)
|
||||||
httpclient (2.9.0)
|
httpclient (2.8.3)
|
||||||
mutex_m
|
json (2.3.0)
|
||||||
jmespath (1.6.2)
|
jwt (2.1.0)
|
||||||
json (2.12.2)
|
memoist (0.16.2)
|
||||||
jwt (2.10.2)
|
mini_magick (4.10.1)
|
||||||
base64
|
mini_mime (1.0.2)
|
||||||
logger (1.7.0)
|
multi_json (1.14.1)
|
||||||
mini_magick (4.13.2)
|
multi_xml (0.6.0)
|
||||||
mini_mime (1.1.5)
|
multipart-post (2.0.0)
|
||||||
multi_json (1.15.0)
|
nanaimo (0.2.6)
|
||||||
multipart-post (2.4.1)
|
naturally (2.2.0)
|
||||||
mutex_m (0.3.0)
|
os (1.0.1)
|
||||||
nanaimo (0.4.0)
|
plist (3.5.0)
|
||||||
naturally (2.3.0)
|
public_suffix (2.0.5)
|
||||||
nkf (0.2.0)
|
representable (3.0.4)
|
||||||
optparse (0.6.0)
|
|
||||||
os (1.1.4)
|
|
||||||
plist (3.7.2)
|
|
||||||
public_suffix (6.0.2)
|
|
||||||
rake (13.3.0)
|
|
||||||
representable (3.2.0)
|
|
||||||
declarative (< 0.1.0)
|
declarative (< 0.1.0)
|
||||||
trailblazer-option (>= 0.1.1, < 0.2.0)
|
declarative-option (< 0.2.0)
|
||||||
uber (< 0.2.0)
|
uber (< 0.2.0)
|
||||||
retriable (3.1.2)
|
retriable (3.1.2)
|
||||||
rexml (3.4.2)
|
rouge (2.0.7)
|
||||||
rouge (3.28.0)
|
rubyzip (1.3.0)
|
||||||
ruby2_keywords (0.0.5)
|
security (0.1.3)
|
||||||
rubyzip (2.4.1)
|
signet (0.12.0)
|
||||||
security (0.1.5)
|
addressable (~> 2.3)
|
||||||
signet (0.20.0)
|
faraday (~> 0.9)
|
||||||
addressable (~> 2.8)
|
|
||||||
faraday (>= 0.17.5, < 3.a)
|
|
||||||
jwt (>= 1.5, < 3.0)
|
jwt (>= 1.5, < 3.0)
|
||||||
multi_json (~> 1.10)
|
multi_json (~> 1.10)
|
||||||
simctl (1.6.10)
|
simctl (1.6.8)
|
||||||
CFPropertyList
|
CFPropertyList
|
||||||
naturally
|
naturally
|
||||||
sysrandom (1.0.5)
|
slack-notifier (2.3.2)
|
||||||
terminal-notifier (2.0.0)
|
terminal-notifier (2.0.0)
|
||||||
terminal-table (3.0.2)
|
terminal-table (1.8.0)
|
||||||
unicode-display_width (>= 1.1.1, < 3)
|
unicode-display_width (~> 1.1, >= 1.1.1)
|
||||||
trailblazer-option (0.1.2)
|
|
||||||
tty-cursor (0.7.1)
|
tty-cursor (0.7.1)
|
||||||
tty-screen (0.8.2)
|
tty-screen (0.7.1)
|
||||||
tty-spinner (0.9.3)
|
tty-spinner (0.9.3)
|
||||||
tty-cursor (~> 0.7)
|
tty-cursor (~> 0.7)
|
||||||
uber (0.1.0)
|
uber (0.1.0)
|
||||||
unicode-display_width (2.6.0)
|
unf (0.1.4)
|
||||||
|
unf_ext
|
||||||
|
unf_ext (0.0.7.6)
|
||||||
|
unicode-display_width (1.6.1)
|
||||||
word_wrap (1.0.0)
|
word_wrap (1.0.0)
|
||||||
xcodeproj (1.27.0)
|
xcodeproj (1.15.0)
|
||||||
CFPropertyList (>= 2.3.3, < 4.0)
|
CFPropertyList (>= 2.3.3, < 4.0)
|
||||||
atomos (~> 0.1.3)
|
atomos (~> 0.1.3)
|
||||||
claide (>= 1.0.2, < 2.0)
|
claide (>= 1.0.2, < 2.0)
|
||||||
colored2 (~> 3.1)
|
colored2 (~> 3.1)
|
||||||
nanaimo (~> 0.4.0)
|
nanaimo (~> 0.2.6)
|
||||||
rexml (>= 3.3.6, < 4.0)
|
xcpretty (0.3.0)
|
||||||
xcpretty (0.4.1)
|
rouge (~> 2.0.7)
|
||||||
rouge (~> 3.28.0)
|
xcpretty-travis-formatter (1.0.0)
|
||||||
xcpretty-travis-formatter (1.0.1)
|
|
||||||
xcpretty (~> 0.2, >= 0.0.7)
|
xcpretty (~> 0.2, >= 0.0.7)
|
||||||
|
|
||||||
PLATFORMS
|
PLATFORMS
|
||||||
ruby
|
ruby
|
||||||
|
|
||||||
DEPENDENCIES
|
DEPENDENCIES
|
||||||
abbrev
|
|
||||||
fastlane
|
fastlane
|
||||||
|
|
||||||
BUNDLED WITH
|
BUNDLED WITH
|
||||||
2.6.9
|
2.1.2
|
||||||
|
|||||||
@ -1,579 +0,0 @@
|
|||||||
[Newer releases](https://github.com/tasks/tasks/blob/main/CHANGELOG.md)
|
|
||||||
|
|
||||||
### 9.7.3 (2020-07-07)
|
|
||||||
|
|
||||||
* Fix Google Task bugs
|
|
||||||
|
|
||||||
### 9.7.2 (2020-06-22)
|
|
||||||
|
|
||||||
* Downgrade Mapbox SDK to remove non-free library (F-Droid only)
|
|
||||||
|
|
||||||
### 9.7.1 (2020-06-19)
|
|
||||||
|
|
||||||
* Fix crash on backup import
|
|
||||||
* Fix CalDAV/EteSync subtask move bug
|
|
||||||
|
|
||||||
### 9.7 (2020-06-12)
|
|
||||||
|
|
||||||
* Added '☰ > Manage lists'
|
|
||||||
* Drag and drop to rearrange the drawer
|
|
||||||
* Tap to edit or delete a list
|
|
||||||
* Display 2 additional snooze options - @rangzen
|
|
||||||
|
|
||||||
### 9.6 (2020-06-06)
|
|
||||||
|
|
||||||
* Add support for offline lists. Offline lists support manual ordering and infinite-depth subtasks
|
|
||||||
* Rename 'My order' to 'Astrid manual sorting' for 'My Tasks', 'Today', and tags
|
|
||||||
* Add '⚙ > Look and feel > Disable sort groups'
|
|
||||||
* Add '⚙ > Look and feel > Open last viewed list'
|
|
||||||
* Add '⚙ > Look and feel > Chips' toggles for subtasks, places, lists, and tags
|
|
||||||
* Add '⚙ > Navigation drawer > Lists'
|
|
||||||
* Add '⚙ > Task defaults > Default list'
|
|
||||||
* Add '⚙ > Task defaults > New tasks on top'
|
|
||||||
* Add '⚙ > Advanced > Astrid manual sorting'
|
|
||||||
* Fix preference reset button
|
|
||||||
|
|
||||||
### 9.5 (2020-06-03)
|
|
||||||
|
|
||||||
* Drag and drop to change subtasks in all list types
|
|
||||||
* Drag and drop to reprioritize or reschedule tasks while sorting by due date
|
|
||||||
or priority
|
|
||||||
* Bug fixes
|
|
||||||
|
|
||||||
### 9.4.1 (2020-06-01)
|
|
||||||
|
|
||||||
* Add 'Tasks settings > Advanced > Improve performance' toggle
|
|
||||||
* Bug fixes
|
|
||||||
|
|
||||||
### 9.4 (2020-05-27)
|
|
||||||
|
|
||||||
* Add collapsible group headers when sorting by due date, priority, created, or modified
|
|
||||||
|
|
||||||
### 9.3.1 (2020-05-26)
|
|
||||||
|
|
||||||
* Fix offline subtasks
|
|
||||||
|
|
||||||
### 9.3 (2020-05-22)
|
|
||||||
|
|
||||||
* Add manual sorting support for CalDAV and EteSync
|
|
||||||
|
|
||||||
### 9.2 (2020-05-13)
|
|
||||||
|
|
||||||
* 'New task' quick settings tile (Android 7+)
|
|
||||||
* Search results match place names and addresses, caldav list names, google task list names, and comments
|
|
||||||
* Fix duplicated search results
|
|
||||||
* Began migrating codebase to Kotlin
|
|
||||||
|
|
||||||
### 9.1 (2020-05-04)
|
|
||||||
|
|
||||||
* 'New task' launcher shortcut (Android 7.1+)
|
|
||||||
* Add option to disable subtask chip on widget
|
|
||||||
|
|
||||||
### 9.0 (2020-05-03)
|
|
||||||
|
|
||||||
* Show What's New after update
|
|
||||||
* Collapsible subtasks enabled by default
|
|
||||||
* 20 new icons
|
|
||||||
* Show subtask chip even if list chips are disabled
|
|
||||||
* Indent subtasks in 'Share' output
|
|
||||||
* Don't trigger location reminders for snoozed or hidden tasks
|
|
||||||
* Minimum supported version is now Android 6.0
|
|
||||||
|
|
||||||
### 8.11 (2020-04-27)
|
|
||||||
|
|
||||||
* Edit existing custom filters
|
|
||||||
* Drag and drop to rearrange filter criteria
|
|
||||||
* Swipe to delete filter criteria
|
|
||||||
* Tap on filter criteria to choose filter operator
|
|
||||||
* Offer additional built-in filters
|
|
||||||
* Add sort by creation time
|
|
||||||
* Choose any day as start of week
|
|
||||||
|
|
||||||
### 8.10 (2020-04-20)
|
|
||||||
|
|
||||||
* New widget features
|
|
||||||
* Menu button to quickly change list
|
|
||||||
* Expand and collapse subtasks
|
|
||||||
* Click on due date to reschedule
|
|
||||||
* Access widget settings from main app preferences
|
|
||||||
* Show description
|
|
||||||
* Show hidden task indicators
|
|
||||||
* New widget settings
|
|
||||||
* Row spacing: default, compact, none
|
|
||||||
* Due date: after title, below title, or hidden
|
|
||||||
* Configure header, row, and footer opacity
|
|
||||||
* Configure footer click behavior
|
|
||||||
* Show full task title
|
|
||||||
* Show full description
|
|
||||||
* Hide dividers
|
|
||||||
* Improve widget touch targets
|
|
||||||
* Expand/collapse Google Task subtasks in 'My order' mode
|
|
||||||
* Fix bug when changing sort order to/from 'My order'
|
|
||||||
* Fix crash when switching to 'My order' list with subtasks disabled
|
|
||||||
|
|
||||||
### 8.9.2 (2020-04-10)
|
|
||||||
|
|
||||||
* Fix 'Add reminder' layout issues
|
|
||||||
* Fix move between EteSync lists
|
|
||||||
* Accept date time changes when dismissing dialog
|
|
||||||
* Improve date time picker behavior in landscape mode
|
|
||||||
|
|
||||||
### 8.9.1 (2020-04-08)
|
|
||||||
|
|
||||||
* Add option to always hide check button
|
|
||||||
* Hide check button for new tasks
|
|
||||||
* Rearrange multi-select buttons
|
|
||||||
* Allow more space for time buttons in date time picker
|
|
||||||
* Fix priority button layout on smaller devices
|
|
||||||
* Fix clicking on hidden task titles
|
|
||||||
* Fix tag picker checkbox tint on Android 4.4
|
|
||||||
* Fix EteSync crash on malformed iCalendar data
|
|
||||||
|
|
||||||
### 8.9 (2020-04-06)
|
|
||||||
|
|
||||||
* Add 'Select all' option to multi-select menu
|
|
||||||
* Add 'Share' to menu and multi-select menu
|
|
||||||
* Display 'Calendar event created' snackbar after creating a calendar event
|
|
||||||
|
|
||||||
### 8.8 (2020-04-01)
|
|
||||||
|
|
||||||
* New bottom sheet due date picker
|
|
||||||
* Shortcuts and calendar displayed together (Android 6+)
|
|
||||||
* Click on due date in task list to reschedule
|
|
||||||
* Option to autoclose due date picker after selecting a date or time
|
|
||||||
* Redesigned title in edit screen
|
|
||||||
* 'Discard' in overflow menu when 'Back button saves task' enabled
|
|
||||||
* Add preference for linkifying edit screen
|
|
||||||
* Updated date and time formatting
|
|
||||||
* Minimum supported version is now Android 4.4
|
|
||||||
* Custom backup/attachment directory requires Android 5+
|
|
||||||
|
|
||||||
### 8.7.1 (2020-03-31)
|
|
||||||
|
|
||||||
* Fix multi-account Google Task synchronization
|
|
||||||
|
|
||||||
### 8.7 (2020-03-19)
|
|
||||||
|
|
||||||
* Places are now lists
|
|
||||||
* Rename a place
|
|
||||||
* Assign an icon and color to a place
|
|
||||||
* Add new navigation drawer settings
|
|
||||||
* Option to remove filters, tags, and places from drawer
|
|
||||||
* Option to hide unused tags and places in drawer
|
|
||||||
|
|
||||||
### 8.6.1 (2020-03-19)
|
|
||||||
|
|
||||||
* Fix crash on startup
|
|
||||||
|
|
||||||
### 8.6 (2020-03-17)
|
|
||||||
|
|
||||||
* Expand and collapse navigation drawer groups
|
|
||||||
|
|
||||||
### 8.5 (2020-03-13)
|
|
||||||
|
|
||||||
* Synchronize locations with CalDAV and EteSync
|
|
||||||
* Fix crash when clearing completed from recently modified filter
|
|
||||||
|
|
||||||
### 8.4 (2020-03-11)
|
|
||||||
|
|
||||||
* New chip configuration options
|
|
||||||
* Outlined or filled
|
|
||||||
* Text and icon, text only, or icon only
|
|
||||||
* Add option to disable color desaturation
|
|
||||||
* Fix EteSync shared lists
|
|
||||||
* Google Task sync requires Android 4.4+
|
|
||||||
|
|
||||||
### 8.3 (2020-03-08)
|
|
||||||
|
|
||||||
* Synchronize CalDAV and EteSync colors
|
|
||||||
* Rename CalDAV and EteSync lists
|
|
||||||
* Update Turkish translations - @emintufan
|
|
||||||
|
|
||||||
### 8.2.1 (2020-03-07)
|
|
||||||
|
|
||||||
* Increase default chip text contrast
|
|
||||||
* New purchase activity
|
|
||||||
* Fix dividers on Android 4.x
|
|
||||||
|
|
||||||
### 8.2 (2020-03-04)
|
|
||||||
|
|
||||||
* Choose your own app and widget colors with a color wheel
|
|
||||||
* Dark theme now free for all
|
|
||||||
* New 'System default' theme
|
|
||||||
* New outlined chip style
|
|
||||||
* Dark theme is now darker
|
|
||||||
* Light theme is now lighter
|
|
||||||
* Desaturate theme colors in dark mode
|
|
||||||
* Improve dialog theming consistency
|
|
||||||
* Bug fixes
|
|
||||||
|
|
||||||
### 8.1 (2020-02-21)
|
|
||||||
|
|
||||||
* Updated app settings screen
|
|
||||||
|
|
||||||
### 8.0.1 (2020-02-16)
|
|
||||||
|
|
||||||
* Fix missing sync settings on fdroid
|
|
||||||
|
|
||||||
### 8.0 (2020-02-12)
|
|
||||||
|
|
||||||
* EteSync support
|
|
||||||
|
|
||||||
### 7.8 (2020-01-24)
|
|
||||||
|
|
||||||
* Android AutoBackup integration
|
|
||||||
|
|
||||||
### 7.7 (2020-01-21)
|
|
||||||
|
|
||||||
* Add support for offline multi-level subtasks
|
|
||||||
* Update Simplified Chinese translations - @sr093906
|
|
||||||
|
|
||||||
### 7.6.1 (2020-01-17)
|
|
||||||
|
|
||||||
* Fix long press in Google Task and CalDAV lists
|
|
||||||
* Fix bug when moving multi-level CalDAV subtasks
|
|
||||||
* Preserve remote VTODO when moving CalDAV tasks
|
|
||||||
* Add Interlingua translations - @softinterlingua
|
|
||||||
|
|
||||||
### 7.6 (2020-01-10)
|
|
||||||
|
|
||||||
* Change tags with multi-select
|
|
||||||
* Fix custom filter crash on deleted tag
|
|
||||||
|
|
||||||
### 7.5 (2020-01-07)
|
|
||||||
|
|
||||||
* New tag picker
|
|
||||||
* Support self-signed SSL certificates
|
|
||||||
|
|
||||||
### 7.4.2 (2019-12-30)
|
|
||||||
|
|
||||||
* Fix Tasker plugin settings
|
|
||||||
|
|
||||||
### 7.4.1 (2019-12-27)
|
|
||||||
|
|
||||||
* Add option to enable subtasks in task list
|
|
||||||
* Performance improvements
|
|
||||||
* Ask Play Services to update security provider
|
|
||||||
* Display custom icons in tag picker
|
|
||||||
* Fix case comparison when sorting navigation drawer
|
|
||||||
|
|
||||||
### 7.4 (2019-12-16)
|
|
||||||
|
|
||||||
* Add Google Task and CalDAV subtasks from the edit screen
|
|
||||||
* 'Recently modified' shows all modifications in past 24 hours
|
|
||||||
* Fix duplicated multi-level subtask count
|
|
||||||
* Increase checkbox touch target
|
|
||||||
* Naturally order lists and filters
|
|
||||||
|
|
||||||
### 7.3.2 (2019-12-12)
|
|
||||||
|
|
||||||
* Fix slow query for subtasks
|
|
||||||
* Fix setting icon on new CalDAV list
|
|
||||||
* Fix clear completed for subtasks
|
|
||||||
* Fix crash when clearing 1000+ tasks
|
|
||||||
|
|
||||||
### 7.3.1 (2019-12-05)
|
|
||||||
|
|
||||||
* Fix crash on missing filter
|
|
||||||
|
|
||||||
### 7.3 (2019-12-03)
|
|
||||||
|
|
||||||
* Expand and collapse subtasks
|
|
||||||
|
|
||||||
### 7.2.2 (2019-12-03)
|
|
||||||
|
|
||||||
* Fix Google Task sorting
|
|
||||||
* Fix crash when deleting 500+ tasks
|
|
||||||
|
|
||||||
### 7.2.1 (2019-11-27)
|
|
||||||
|
|
||||||
* Bug fixes and minor improvements
|
|
||||||
|
|
||||||
### 7.2 (2019-11-25)
|
|
||||||
|
|
||||||
* Display Google Task and CalDAV subtasks in all lists (Android 5+)
|
|
||||||
* Remove completed tasks immediately - @creywood
|
|
||||||
|
|
||||||
### 7.1.2 (2019-11-22)
|
|
||||||
|
|
||||||
* Add CalDAV account setting for repeating tasks
|
|
||||||
* Fix CalDAV repeating tasks
|
|
||||||
* Fix Google Tasks HTTP 400 response
|
|
||||||
|
|
||||||
### 7.1.1 (2019-11-18)
|
|
||||||
|
|
||||||
* Improve subtask query performance
|
|
||||||
* Fix crash when deleting 1000+ CalDAV tasks
|
|
||||||
|
|
||||||
### 7.1 (2019-11-14)
|
|
||||||
|
|
||||||
* Display subtasks on Google Task and CalDAV widgets (Android 5+)
|
|
||||||
* Fix subtasks after backup import
|
|
||||||
* Fix chained subtask completion
|
|
||||||
|
|
||||||
### 7.0 (2019-11-12)
|
|
||||||
|
|
||||||
* Add support for CalDAV subtasks (Android 5+) - @creywood
|
|
||||||
* Display Google subtasks in all sort modes (Android 5+)
|
|
||||||
|
|
||||||
### 6.9.3 (2019-10-31)
|
|
||||||
|
|
||||||
* Fix disappearance of remotely completed recurring Google Tasks
|
|
||||||
* Fix '0 tasks' notification
|
|
||||||
* Limit to 20 active notifications due to change in Android 10
|
|
||||||
|
|
||||||
### 6.9.2 (2019-10-25)
|
|
||||||
|
|
||||||
* Fix bug forcing new Google Tasks to top
|
|
||||||
* Fix bug preventing deleted tasks from being synchronized - @creywood
|
|
||||||
|
|
||||||
### 6.9.1 (2019-10-09)
|
|
||||||
|
|
||||||
* Fix location reminders on Android 10
|
|
||||||
* Fix CalDAV time zone issue
|
|
||||||
|
|
||||||
### 6.9 (2019-09-23)
|
|
||||||
|
|
||||||
* Synchronize tags with CalDAV
|
|
||||||
* Target Android 10
|
|
||||||
* Bug fixes
|
|
||||||
|
|
||||||
### 6.8.1 (2019-08-05)
|
|
||||||
|
|
||||||
* Fix CalDAV filter migration
|
|
||||||
* Fix native date picker crash
|
|
||||||
|
|
||||||
### 6.8 (2019-07-30)
|
|
||||||
|
|
||||||
* Name your own subscription price! Upgrade, downgrade, or cancel at any time
|
|
||||||
* Choose icons for lists (requires [subscription](https://tasks.org/subscribe))
|
|
||||||
* Choose color for custom filters
|
|
||||||
* Performance improvements
|
|
||||||
* Allow duplicate CalDAV list names
|
|
||||||
* Fix duplicate tag name bug
|
|
||||||
|
|
||||||
### 6.7.3 (2019-07-16)
|
|
||||||
|
|
||||||
* Workaround for [list updated time bug](https://issuetracker.google.com/issues/136123247) in Google Tasks API
|
|
||||||
* Fix crash in CalDAV sync
|
|
||||||
|
|
||||||
### 6.7.2 (2019-07-08)
|
|
||||||
|
|
||||||
* Handle 404 errors when creating new Google Tasks
|
|
||||||
* Ignore 404 errors when deleting Google Drive files
|
|
||||||
* Don't report connection errors
|
|
||||||
|
|
||||||
### 6.7.1 (2019-07-05)
|
|
||||||
|
|
||||||
* Add location chip to task list
|
|
||||||
* Reduce chip sizes
|
|
||||||
* Accept 'send to' for more attachment types
|
|
||||||
* Synchronize multiple accounts in parallel
|
|
||||||
* Fix Google Task migration from older versions
|
|
||||||
* Fix corrupted checkbox issue
|
|
||||||
* Fix some RTL issues
|
|
||||||
|
|
||||||
### 6.7 (2019-06-13)
|
|
||||||
|
|
||||||
* Use drag and drop to indent tasks
|
|
||||||
* Add new Google Tasks to top or bottom
|
|
||||||
* Toggle hidden and completed in manually sorted Google Task lists
|
|
||||||
* Rearrange Google Tasks without a network connection
|
|
||||||
* Optional workaround for [custom order bug](https://issuetracker.google.com/issues/132432317) in Google Tasks API
|
|
||||||
* Include subtasks when moving or deleting Google Tasks
|
|
||||||
* Ignore 404 errors when fetching Google Drive folders
|
|
||||||
* Match tags in search results
|
|
||||||
* Fix stuck 'Generating notifications' notification
|
|
||||||
* Don't display sync indicator when there is no network connection
|
|
||||||
* Don't synchronize immediately after every change
|
|
||||||
* Added Estonian translations - Eraser
|
|
||||||
|
|
||||||
### 6.6.4 (2019-05-21)
|
|
||||||
|
|
||||||
* Handle [breaking change](https://issuetracker.google.com/issues/133254108) in Google Tasks API
|
|
||||||
|
|
||||||
### 6.6.3 (2019-05-08)
|
|
||||||
|
|
||||||
* Fix backup import crash
|
|
||||||
* Fix crash when refreshing purchases
|
|
||||||
* Google Tasks synchronization bug fix
|
|
||||||
|
|
||||||
### 6.6.2 (2019-04-22)
|
|
||||||
|
|
||||||
* Backup and restore preferences
|
|
||||||
* Google Task performance improvements
|
|
||||||
* Google Task and Drive support added to F-Droid and Amazon
|
|
||||||
* Add third-party licenses, changelog, and version info
|
|
||||||
* Fix backup import crash
|
|
||||||
* Fix widget bugs
|
|
||||||
|
|
||||||
### 6.6.1 (2019-04-15)
|
|
||||||
|
|
||||||
* Fix crash on devices running Android 5.1 and below
|
|
||||||
* Fix analytics opt-out
|
|
||||||
|
|
||||||
### 6.6 (2019-04-10)
|
|
||||||
|
|
||||||
* New location picker
|
|
||||||
* Choose Mapbox or Google Maps tiles
|
|
||||||
* Choose Mapbox or Google Places search
|
|
||||||
* Google Places search restricted to subscribers due to new Google Maps pricing
|
|
||||||
* Use Mapbox for reverse geocoding
|
|
||||||
* Select from previously used locations
|
|
||||||
* Dark maps
|
|
||||||
* Enable location picker in F-Droid build
|
|
||||||
* Resume support for Amazon App Store
|
|
||||||
* Fix Android Q background warning
|
|
||||||
|
|
||||||
### 6.5.6 (2019-03-27)
|
|
||||||
|
|
||||||
* Fix crash when clearing completed on a manually sorted Google Task list
|
|
||||||
* Update Ukrainian translations - nathalier
|
|
||||||
|
|
||||||
### 6.5.5 (2019-03-14)
|
|
||||||
|
|
||||||
* Bug fixes
|
|
||||||
|
|
||||||
### 6.5.4 (2019-03-11)
|
|
||||||
|
|
||||||
* Fix black screen issue
|
|
||||||
* Fix crash when task not found
|
|
||||||
|
|
||||||
### 6.5.3 (2019-02-19)
|
|
||||||
|
|
||||||
* Fix crash when upgrading from Android 7 to 8+
|
|
||||||
* Improve OneTask interoperability
|
|
||||||
* Performance improvement
|
|
||||||
|
|
||||||
### 6.5.2 (2019-02-11)
|
|
||||||
|
|
||||||
* Bug fixes
|
|
||||||
|
|
||||||
### 6.5.1 (2019-02-10)
|
|
||||||
|
|
||||||
* Bug fixes
|
|
||||||
|
|
||||||
### 6.5 (2019-02-08)
|
|
||||||
|
|
||||||
* Improve notification accuracy
|
|
||||||
* Performance improvements
|
|
||||||
* Bug fixes
|
|
||||||
* Add Tagalog translations - Topol
|
|
||||||
|
|
||||||
### 6.4.1 (2019-01-16)
|
|
||||||
|
|
||||||
* Limit number of active notifications
|
|
||||||
* Limit rate of notifications
|
|
||||||
* Fix Synology Calendar sync issue
|
|
||||||
* Fix exception when external storage is unavailable
|
|
||||||
|
|
||||||
### 6.4 (2019-01-10)
|
|
||||||
|
|
||||||
* Copy backups to Google Drive
|
|
||||||
* Improved search
|
|
||||||
* Use system file picker (Android 4.4+)
|
|
||||||
* Use system directory picker (Android 5.0+)
|
|
||||||
* Accept 'send' and 'send_multiple' actions with images
|
|
||||||
* File attachment bug fixes
|
|
||||||
|
|
||||||
### 6.3.1 (2018-11-07)
|
|
||||||
|
|
||||||
* New location row in task edit screen
|
|
||||||
* Add location departure notifications
|
|
||||||
* Set CalDAV completion percentage and status
|
|
||||||
* Bug fixes
|
|
||||||
|
|
||||||
### 6.2 (2018-10-29)
|
|
||||||
|
|
||||||
* New white theme color
|
|
||||||
* New icons
|
|
||||||
* New list and tag chips
|
|
||||||
* Linkify text when editing tasks
|
|
||||||
* Option to linkify text on task list
|
|
||||||
* Show description on task list
|
|
||||||
* Move due date next to title
|
|
||||||
* Updated hidden task visualization
|
|
||||||
* No longer require contacts permission (Oreo+)
|
|
||||||
* Dropped support for Android 4.0
|
|
||||||
|
|
||||||
### 6.1.3 (2018-10-22)
|
|
||||||
|
|
||||||
* Fix translation error
|
|
||||||
|
|
||||||
### 6.1.2 (2018-10-18)
|
|
||||||
|
|
||||||
* Remove missed call functionality due to Google Play Developer policy change
|
|
||||||
* Fix manual sort issue affecting Samsung Oreo devices
|
|
||||||
* Fix refresh issue affecting Pure Calendar Widget
|
|
||||||
* Fix memory leak
|
|
||||||
* Schedule jobs with WorkManager instead of android-job
|
|
||||||
|
|
||||||
### 6.1.1 (2018-07-20)
|
|
||||||
|
|
||||||
* Fix notification badge issues
|
|
||||||
* Allow non-SSL connections
|
|
||||||
* Allow user-defined certificate authorities
|
|
||||||
|
|
||||||
### 6.1 (2018-06-30)
|
|
||||||
|
|
||||||
* Customize launcher icon
|
|
||||||
* Customize shortcut widget icon and label
|
|
||||||
* Add custom text selection action (Android 6+)
|
|
||||||
* Target Android P
|
|
||||||
* Remove 'Tasks' from notification body
|
|
||||||
* Fix localization issues - @marmo
|
|
||||||
* Fix crash when calendar permissions are revoked
|
|
||||||
* Fix crash when opening task from widget
|
|
||||||
* Fix crash when recording audio note
|
|
||||||
* Fix crash when dismissing dialogs
|
|
||||||
* Fix crash in backup import
|
|
||||||
* Fix crash on invalid URL during CalDAV setup
|
|
||||||
* Fix crash when editing task
|
|
||||||
|
|
||||||
### 6.0.6 (2018-04-28)
|
|
||||||
|
|
||||||
* Fix crash when creating shortcuts on pre-Oreo devices
|
|
||||||
* Fix crash when Google Task or CalDAV list is missing
|
|
||||||
* Downgrade Play Services for compatibility with MicroG
|
|
||||||
|
|
||||||
### 6.0.5 (2018-04-26)
|
|
||||||
|
|
||||||
* Fix crash when deleting 1000+ tasks at once
|
|
||||||
* Fix hidden dates in date picker
|
|
||||||
* Fix crash on bad response from billing client
|
|
||||||
* Report crash when database fails to open
|
|
||||||
|
|
||||||
### 6.0.4 (2018-04-25)
|
|
||||||
|
|
||||||
* Fix crash caused by leftover Google Analytics campaign tracker
|
|
||||||
|
|
||||||
### 6.0.3 (2018-04-25)
|
|
||||||
|
|
||||||
* Fix crash when manually sorting Google Task lists
|
|
||||||
* Fix multi account Google Task sync issue
|
|
||||||
|
|
||||||
### 6.0.2 (2018-04-25)
|
|
||||||
|
|
||||||
* Fix crash caused by missing tag metadata
|
|
||||||
* Fix crash caused by missing Android System WebView
|
|
||||||
* Replace Google Analytics with Firebase Analytics
|
|
||||||
* Add Crashlytics exception reporting
|
|
||||||
|
|
||||||
### 6.0.1 (2018-04-23)
|
|
||||||
|
|
||||||
* Fix crash caused by missing Google Task metadata
|
|
||||||
|
|
||||||
### 6.0 (2018-04-23)
|
|
||||||
|
|
||||||
* Change to [annual subscription](https://tasks.org/subscribe) pricing
|
|
||||||
* [CalDAV synchronization](https://tasks.org/caldav)
|
|
||||||
* Sync with [multiple Google Task accounts](https://tasks.org/docs/google_tasks_intro.html)
|
|
||||||
* Default theme changed to blue
|
|
||||||
* Display Google Task and CalDAV chips on task list
|
|
||||||
* Display sync error icon in navigation drawer
|
|
||||||
* Move tasks between Google Task and CalDAV lists using multi-select
|
|
||||||
* Add "Don't Sync" option when choosing a Google Task or CalDAV list
|
|
||||||
* Add option to restrict background synchronization to unmetered connections
|
|
||||||
* Custom filters with due date criteria no longer set a due time of 23:59/11:59PM
|
|
||||||
* Internal improvements to notification scheduling should reduce notification delays
|
|
||||||
* Fix list animation bug
|
|
||||||
@ -1,767 +0,0 @@
|
|||||||
### 12.7 (2022-06-18)
|
|
||||||
|
|
||||||
* Android 13 themed icon - Thanks @hanthor!
|
|
||||||
* Fix self-signed SSL certificates on Android 12+
|
|
||||||
* Don't hide empty tags and places in pickers
|
|
||||||
* Update translations
|
|
||||||
* Basque - @Txopi, Sergio Varela, @osoitz
|
|
||||||
* Belarusian - @Prominence, Андрей
|
|
||||||
* Bulgarian - @StoyanDimitrov
|
|
||||||
* Czech - Shimon
|
|
||||||
* Danish - Tntdruid
|
|
||||||
* Dutch - @mm4c
|
|
||||||
* German - @3ole
|
|
||||||
* Hungarian - kaciokos
|
|
||||||
* Indonesian - Cyua Pyua
|
|
||||||
* Italian - @ppasserini
|
|
||||||
* Polish - @wiktor-k
|
|
||||||
* Portuguese (Brazilian) - @LevyMarCiS, @sunflowerskater
|
|
||||||
* Portuguese - @laralem, @alvar0liveira
|
|
||||||
* Swedish - @reportxx
|
|
||||||
* Turkish - @emintufan
|
|
||||||
* Vietnamese - @unbiaseduser
|
|
||||||
|
|
||||||
### 12.6.1 (2022-03-27)
|
|
||||||
|
|
||||||
* Move task list and edit screen options to top level settings
|
|
||||||
* Prompt users to customize edit screen
|
|
||||||
* Fix cancel button for recurring reminder dialog
|
|
||||||
* Update translations
|
|
||||||
* Bulgarian - @StoyanDimitrov
|
|
||||||
* Chinese (Simplified) - Eric, @Geeyun-JY3
|
|
||||||
* Croatian - @milotype
|
|
||||||
* Dutch - @mm4c, @fvbommel
|
|
||||||
* Finnish - J. Lavoie
|
|
||||||
* French - @FlorianLeChat
|
|
||||||
* Galician - @mglbranco, J. Lavoie
|
|
||||||
* German - @qwerty287
|
|
||||||
* Hungarian - kaciokos
|
|
||||||
* Italian - @Fs00
|
|
||||||
* Norwegian Bokmål - @comradekingu
|
|
||||||
* Polish - @wiktor-k
|
|
||||||
* Portuguese (Brazilian) - @tsunamistonefly
|
|
||||||
* Romanian - @simonaiacob
|
|
||||||
* Russian - Nikita Epifanov
|
|
||||||
* Spanish - @FlorianLeChat
|
|
||||||
* Swedish - @reportxx
|
|
||||||
* Turkish - @ersen0, @emintufan
|
|
||||||
* Ukrainian - @IhorHordiichuk
|
|
||||||
* Vietnamese - @unbiaseduser, J. Lavoie
|
|
||||||
|
|
||||||
### 12.6 (2022-03-12)
|
|
||||||
|
|
||||||
* Configure notifications to repeat at custom intervals
|
|
||||||
([#3](https://github.com/tasks/tasks/issues/3))
|
|
||||||
* Notifications can repeat by minute, hour, day, or weekly intervals
|
|
||||||
* Add 'Snoozed' filter ([#1633](https://github.com/tasks/tasks/issues/1633))
|
|
||||||
* Add 'Notifications' filter
|
|
||||||
* CalDAV/DAVx5 server selection setting
|
|
||||||
* This replaces 'Let server schedule recurring tasks'
|
|
||||||
* Synology Calendar users must set this to fix sync
|
|
||||||
([#1802](https://github.com/tasks/tasks/issues/1802))
|
|
||||||
* Mailbox.org and Open-Xchange users must set this to prevent duplicate
|
|
||||||
repeating tasks
|
|
||||||
* Set geofence radius in place settings
|
|
||||||
* Remove DAVx5/EteSync app accounts when native CalDAV/EteSync enabled
|
|
||||||
* Clear reminders when they are dismissed in Thunderbird
|
|
||||||
* Fix reminder synchronization
|
|
||||||
* Fix crash in task edit screen
|
|
||||||
* Fix prompt to discard changes
|
|
||||||
* Fix crash during 12.4 upgrade
|
|
||||||
* Update translations
|
|
||||||
* Bulgarian - @StoyanDimitrov
|
|
||||||
* Chinese (Simplified) - @Crystal-RainSlide, @Geeyun-JY3, Eric
|
|
||||||
* Croatian - @milotype
|
|
||||||
* Dutch - @mm4c, @fvbommel
|
|
||||||
* French - J. Lavoie, @FlorianLeChat
|
|
||||||
* German - @eldiep, J. Lavoie, @qwerty287
|
|
||||||
* Hungarian - kaciokos
|
|
||||||
* Italian - @ppasserini, J. Lavoie
|
|
||||||
* Portuguese (Brazilian) - @hugomg
|
|
||||||
* Romanian - @simonaiacob
|
|
||||||
* Russian - @Allineer
|
|
||||||
* Spanish - @toni-em, @FlorianLeChat, @Romerolweb
|
|
||||||
* Swedish - @reportxx
|
|
||||||
* Turkish - @ersen0
|
|
||||||
* Ukrainian - @IhorHordiichuk
|
|
||||||
* Urdue - @Crystal-RainSlide
|
|
||||||
* Vietnamese - @unbaseduser
|
|
||||||
|
|
||||||
### 12.5 (2022-02-27)
|
|
||||||
|
|
||||||
* Choose custom random reminder period
|
|
||||||
* Add multiple random reminders
|
|
||||||
* Fix sync crash for Tasks.org, CalDAV, and native EteSync
|
|
||||||
* Add Kurdish (Central) translations - @roj1512
|
|
||||||
* Update translations
|
|
||||||
* Bulgarian - @StoyanDimitrov
|
|
||||||
* Chinese (Simplified) - Eric
|
|
||||||
* Croatian - @milotype
|
|
||||||
* Dutch - @mm4c
|
|
||||||
* French - @FlorianLeChat
|
|
||||||
* Portuguese - @laralem
|
|
||||||
* Spanish - @Romerolweb, Jeffree Romero
|
|
||||||
* Turkish - @ersen0
|
|
||||||
* Ukrainian - @IhorHordiichuk
|
|
||||||
|
|
||||||
### 12.4 (2022-02-19)
|
|
||||||
|
|
||||||
* Relative reminder support
|
|
||||||
* Quickly add reminders minutes, hours, days, or weeks before due
|
|
||||||
* Sync reminders with Tasks.org, DAVx5, CalDAV, EteSync, and DecSync CC
|
|
||||||
* Synchronize relative and absolute reminders
|
|
||||||
* Tasks.org, CalDAV, and native EteSync sync improvements
|
|
||||||
* Merge remote changes before pushing local changes
|
|
||||||
* Not applicable to DAVx5, EteSync app, or DecSync CC
|
|
||||||
* View and cancel snoozed reminders in task edit screen
|
|
||||||
* Add 'Has reminder' custom filter criteria
|
|
||||||
* Fix updating calendar entries after editing task
|
|
||||||
* Fix search when using top app bar
|
|
||||||
* Fix task deletion when adding from two devices simultaneously
|
|
||||||
* Update translations
|
|
||||||
* Arabic - @mhmdanas
|
|
||||||
* Basque - Sergio Varela
|
|
||||||
* Brazilian Portuguese - @Luiz-bro
|
|
||||||
* Bulgarian - @StoyanDimitrov
|
|
||||||
* Chinese (Simplified) - Eric
|
|
||||||
* Croatian - @milotype
|
|
||||||
* Dutch - @mm4c
|
|
||||||
* French - @FlorianLeChat, J. Lavoie
|
|
||||||
* German - J. Lavoie, @qwerty287
|
|
||||||
* Hungarian - kaciokos
|
|
||||||
* Italian - @ppasserini, J. Lavoie, @andrearosso
|
|
||||||
* Portuguese - @laralem
|
|
||||||
* Romanian - @simonaiacob
|
|
||||||
* Russian - @NikGreens
|
|
||||||
* Spanish - @FlorianLeChat, Sergio Varela
|
|
||||||
* Turkish - @ersen0, @emintufan
|
|
||||||
* Ukrainian - @IhorHordiichuk
|
|
||||||
* Vietnamese - bruh, @unbaseduser
|
|
||||||
|
|
||||||
### 12.3 (2022-02-04)
|
|
||||||
|
|
||||||
* Add option to disable moving completed tasks to bottom
|
|
||||||
* Add option to disable sorting completed by completion date
|
|
||||||
* Add undo snackbar for task completion
|
|
||||||
* Fix crash when location lookup fails
|
|
||||||
* Fix voice reminders on Android 12
|
|
||||||
* Fix widget due dates in overdue sort group
|
|
||||||
* Add Karelian translations - Olexii Ondrei
|
|
||||||
* Update translations
|
|
||||||
* Basque - Sergio Varela
|
|
||||||
* Catalan - @ivangjxyz
|
|
||||||
* Chinese (Simplified) - Eric
|
|
||||||
* Croatian - @milotype
|
|
||||||
* Dutch - @mm4c
|
|
||||||
* French - @FlorianLeChat
|
|
||||||
* German - @qwerty287
|
|
||||||
* Hungarian - kaciokos
|
|
||||||
* Romanian - @simonaiacob
|
|
||||||
* Russian - @NikGreens
|
|
||||||
* Spanish - @FlorianLeChat
|
|
||||||
* Swedish - @reportxx
|
|
||||||
* Turkish - @emintufan, @ersen0
|
|
||||||
* Vietnamese - @unbaseduser
|
|
||||||
|
|
||||||
### 12.2 (2022-01-16)
|
|
||||||
|
|
||||||
* Move completed tasks to bottom
|
|
||||||
* Add option to disable collapsing app bars
|
|
||||||
* Uncheck parent tasks when subtask is unchecked
|
|
||||||
* Fix crash on completion sound
|
|
||||||
* Update translations
|
|
||||||
* Chinese (Simplified) - Eric
|
|
||||||
* Danish - @Tntdruid
|
|
||||||
* Dutch - @fvbommel, @mm4c
|
|
||||||
* French - @FlorianLeChat
|
|
||||||
* German - @qwerty287
|
|
||||||
* Russian - @NikGreens
|
|
||||||
* Spanish - @FlorianLeChat
|
|
||||||
* Turkish - @ersen0
|
|
||||||
* Ukrainian - @IhorHordiichuk
|
|
||||||
* Vietnamese - @unbaseduser
|
|
||||||
|
|
||||||
### 12.1 (2022-01-09)
|
|
||||||
|
|
||||||
* Group overdue tasks when sorting by due date
|
|
||||||
* Update translations
|
|
||||||
* Basque - Sergio Varela
|
|
||||||
* Chinese (Simplified) - Eric
|
|
||||||
* French - @FlorianLeChat
|
|
||||||
* Norwegian Bokmål - @comradekingu
|
|
||||||
* Spanish - @FlorianLeChat
|
|
||||||
* Vietnamese - @unbaseduser
|
|
||||||
|
|
||||||
### 12.0 (2022-01-08)
|
|
||||||
|
|
||||||
* New bottom app bar
|
|
||||||
* Choose top or bottom app bar in settings
|
|
||||||
* Miscellaneous design updates
|
|
||||||
* Improve privacy and security by removing RECORD_AUDIO and
|
|
||||||
WRITE_EXTERNAL_STORAGE permissions
|
|
||||||
* Attaching an audio note will launch your device's audio recorder
|
|
||||||
* Translation updates
|
|
||||||
* Catalan - @Solatec
|
|
||||||
* Dutch - @mm4c
|
|
||||||
* German - @qwerty287
|
|
||||||
* Italian - @ppasserini, @Fs00
|
|
||||||
* Portuguese - @SantosSi
|
|
||||||
* Romanian - @simonaiacob
|
|
||||||
* Russian - Nikita Epifanov
|
|
||||||
* Ukrainian - @IhorHordiichuk
|
|
||||||
|
|
||||||
### 11.13 (2021-12-31)
|
|
||||||
|
|
||||||
* Add option to play a sound when a task is completed
|
|
||||||
* Accept audio attachments shared from other apps
|
|
||||||
* Removed native EteSync v1 support
|
|
||||||
* EteSync v1 accounts can still be synchronized with the EteSync app
|
|
||||||
* Bug fixes
|
|
||||||
* Translation updates
|
|
||||||
* Bulgarian - @StoyanDimitrov
|
|
||||||
* Chinese (Simplified) - @sr093906
|
|
||||||
* Chinese (Traditional) - @dixon777
|
|
||||||
* Finnish - @CSharpest, Rami Lehtinen
|
|
||||||
* French - @FlorianLeChat
|
|
||||||
* Hungarian - kaciokos
|
|
||||||
* Italian - J. Lavoie, @Fs00
|
|
||||||
* Norwegian Bokmål - @comradekingu
|
|
||||||
* Persian - @Ahmadhosseinbor
|
|
||||||
* Spanish - @aplopez, @FlorianLeChat
|
|
||||||
* Ukrainian - @IhorHordiichuk
|
|
||||||
|
|
||||||
### 11.12.3 (2021-11-22)
|
|
||||||
|
|
||||||
* Fix reminders
|
|
||||||
* Update translations
|
|
||||||
* Indonesian - when we were sober
|
|
||||||
* Kurdish (Northern) - Pêşeroja paşerojê
|
|
||||||
* Romanian - @Steinhagen
|
|
||||||
|
|
||||||
### 11.12.2 (2021-11-13)
|
|
||||||
|
|
||||||
* Fix reminders
|
|
||||||
* Fix reminder preference backup
|
|
||||||
* Update translations
|
|
||||||
* Interlingua - @softinterlingua
|
|
||||||
* Tamil - @balogic
|
|
||||||
|
|
||||||
### 11.12.1 (2021-11-05)
|
|
||||||
|
|
||||||
* Fix reminders
|
|
||||||
* Update translations
|
|
||||||
* Bulgarian - @StoyanDimitrov
|
|
||||||
* Croatian - @milotype
|
|
||||||
* Norwegian Bokmål - @HumanNr4584093104
|
|
||||||
* Romanian - Simona Iacob
|
|
||||||
* Russian - @NikGreens
|
|
||||||
* Tamil - @balogic
|
|
||||||
* Turkish - @ersen0
|
|
||||||
|
|
||||||
### 11.12 (2021-10-26)
|
|
||||||
|
|
||||||
* Add option to notify at start date
|
|
||||||
* Widget tweaks for Android 12
|
|
||||||
* Fix crash when deleting tasks (Thanks @fschrempf!)
|
|
||||||
* Fix truncated calendar picker
|
|
||||||
* Update translations
|
|
||||||
* Basque - Sergio Varela
|
|
||||||
* Brazilian Portuguese - @laralem
|
|
||||||
* Bulgarian - @StoyanDimitrov
|
|
||||||
* Catalan - @Solatec
|
|
||||||
* Dutch - @fvbommel
|
|
||||||
* French - @FlorianLeChat
|
|
||||||
* German - @qwerty287
|
|
||||||
* Hungarian - kaciokos
|
|
||||||
* Lithuanian - @70h
|
|
||||||
* Polish - @dominik-korsa
|
|
||||||
* Simplified Chinese - @sr093906, @Geeyun-JY3
|
|
||||||
* Ukrainian - @IhorHordiichuk
|
|
||||||
* Vietnamese - bruh
|
|
||||||
|
|
||||||
### 11.11 (2021-09-21)
|
|
||||||
|
|
||||||
* Add 'Due now' filter criteria - Thanks @tkterris!
|
|
||||||
* Fix crash on Android 12 - Thanks @tkterris!
|
|
||||||
* Fix preference display issue - Thanks @Groctel!
|
|
||||||
* Target Android 12
|
|
||||||
* Ignore link clicks during multi-select
|
|
||||||
* Update translations
|
|
||||||
* Arabic - @mhmdanas, @machiav3lli
|
|
||||||
* Basque - @Thadah
|
|
||||||
* Brazilian Portuguese - @laralem
|
|
||||||
* Bulgarian - @StoyanDimitrov
|
|
||||||
* Croatian - @milotype
|
|
||||||
* Czech - @vitSkalicky
|
|
||||||
* Danish - @Tntdruid
|
|
||||||
* Dutch - @fvbommel
|
|
||||||
* French - @FlorianLeChat
|
|
||||||
* German - @machiav3lli, J. Lavoie
|
|
||||||
* Greek - @giorgio93p
|
|
||||||
* Indonesian - @erigmac
|
|
||||||
* Italian - J. Lavoie, @Fs00
|
|
||||||
* Japanese - さとうまこと
|
|
||||||
* Lithuanian - @70h
|
|
||||||
* Norwegian Bokmål - @comradekingu
|
|
||||||
* Portuguese - @laralem
|
|
||||||
* Romanian - Simona Iacob
|
|
||||||
* Russian - @tolstovka, @zhelemysh, @ToxesFoxes
|
|
||||||
* Simplified Chinese - @sr093906, @Geeyun-JY3
|
|
||||||
* Sinhala - @Dilshan-H
|
|
||||||
* Spanish - @FlorianLeChat, @Groctel, @berman00
|
|
||||||
* Swedish - @bittin
|
|
||||||
* Turkish - @ersen0
|
|
||||||
* Ukrainian - @IhorHordiichuk
|
|
||||||
* Vietnamese - bruh
|
|
||||||
|
|
||||||
### 11.10.2 (2021-07-15)
|
|
||||||
|
|
||||||
* Fix location-based reminders
|
|
||||||
* Fix preference backup
|
|
||||||
* Update translations
|
|
||||||
* Arabic - git ty, @mhmdanas
|
|
||||||
* Basque - Sergio Varela
|
|
||||||
* Croatian - @milotype
|
|
||||||
* Czech - @vitSkalicky, @p-bo
|
|
||||||
* Dutch - Beardhatcode, @fvbommel
|
|
||||||
* French - @FlorianLeChat
|
|
||||||
* German - K. Herbert, @franconian, @ecxod, @bluedeepimpact
|
|
||||||
* Indonesian - when we were sober
|
|
||||||
* Interlingua - @softinterlingua
|
|
||||||
* Italian - J. Lavoie
|
|
||||||
* Lithuanian - @70h
|
|
||||||
* Norwegian Bokmål - @Jerome2103
|
|
||||||
* Portuguese - @laralem
|
|
||||||
* Russian - @KovalevArtem, @Blueberryy
|
|
||||||
* Simplified Chinese - @sr093906, @Geeyun-JY3
|
|
||||||
* Sinhala - HelaBasa
|
|
||||||
* Spanish - @FlorianLeChat, @fitojb
|
|
||||||
* Turkish - Oğuz Ersen, @emintufan
|
|
||||||
* Ukrainian - @IhorHordiichuk
|
|
||||||
* Urdu - Maaz
|
|
||||||
* Vietnamese - bruh
|
|
||||||
|
|
||||||
### 11.10.1 (2021-05-26)
|
|
||||||
|
|
||||||
* Improve Android 12 compatibility
|
|
||||||
* Update status bar styles
|
|
||||||
* Update translations
|
|
||||||
* Arabic - @mhmdanas
|
|
||||||
* Basque - Sergio Varela
|
|
||||||
* Catalan - @toram
|
|
||||||
* Chinese (Traditional) - @kisaragi-hiu
|
|
||||||
* Croatian - @ggdorman
|
|
||||||
* Czech - @vitSkalicky
|
|
||||||
* Esperanto - @J053Fabi0, @jakubfabijan
|
|
||||||
* French - K. Herbert, J. Lavoie
|
|
||||||
* German - K. Herbert
|
|
||||||
* Greek - Eugenia Russell
|
|
||||||
* Hungarian - @gthrepwood
|
|
||||||
* Indonesian - @andhikapangestu29
|
|
||||||
* Korean - Sunjae Choi
|
|
||||||
* Portuguese (Brazil) - @laralem
|
|
||||||
* Portuguese - @SantosSi, @laralem
|
|
||||||
* Russian - Nikita Epifanov
|
|
||||||
* Sinhala - @Dilshan-H
|
|
||||||
* Spanish - @fitojb
|
|
||||||
* Ukrainian - @IhorHordiichuk
|
|
||||||
* Urdu - Maaz
|
|
||||||
* Vietnamese - bruh
|
|
||||||
|
|
||||||
### 11.10 (2021-04-19)
|
|
||||||
|
|
||||||
* Markdown support ([Documentation](https://tasks.org/docs/markdown))
|
|
||||||
* Samsung DeX support - Thanks @mhmdanas!
|
|
||||||
* Update to Google Play Billing v3
|
|
||||||
* Remove background sync for legacy EteSync v1 accounts
|
|
||||||
* Update translations
|
|
||||||
* Arabic - @mhmdanas
|
|
||||||
* Brazilian Portuguese - @daylightdev
|
|
||||||
* Dutch - @fvbommel
|
|
||||||
* French - @FlorianLeChat, J. Lavoie
|
|
||||||
* German - J. Lavoie
|
|
||||||
* Greek - Michalis, Eugenia Russell
|
|
||||||
* Indonesian - @liimee
|
|
||||||
* Italian - J. Lavoie, @Fs00
|
|
||||||
* Japanese - @kisaragi-hiu
|
|
||||||
* Kannada - @shashank-p
|
|
||||||
* Russian - @zhelemysh, Nikita Epifanov
|
|
||||||
* Simplified Chinese - @sr093906
|
|
||||||
* Spanish - @FlorianLeChat
|
|
||||||
* Turkish - Oğuz Ersen
|
|
||||||
* Ukrainian - @IhorHordiichuk
|
|
||||||
* Urdu - Maaz
|
|
||||||
|
|
||||||
### 11.9.2 (2021-03-29)
|
|
||||||
|
|
||||||
* Fix date translation issue - Thanks @mhmdanas!
|
|
||||||
* Fix misc translation strings - Thanks J. Lavoie!
|
|
||||||
* Update translations
|
|
||||||
* Dutch - @fvbommel
|
|
||||||
* French - @FlorianLeChat
|
|
||||||
* German - @franconian, Achim Schumacher, J. Lavoie
|
|
||||||
* Hungarian - kaciokos
|
|
||||||
* Indonesian - when we were sober
|
|
||||||
* Italian - @Fs00
|
|
||||||
* Simplified Chinese - @sr093906
|
|
||||||
* Spanish - @FlorianLeChat
|
|
||||||
* Turkish - @emintufan
|
|
||||||
* Ukrainian - @IhorHordiichuk
|
|
||||||
|
|
||||||
### 11.9.1 (2021-03-25)
|
|
||||||
|
|
||||||
* Open documentation links in custom tabs
|
|
||||||
* Fix crash in Mapbox reverse geocoder
|
|
||||||
* Increase 'Add subtask' touch target
|
|
||||||
* Update translations
|
|
||||||
* Arabic - @mhmdanas
|
|
||||||
* German - Achim Schumacher
|
|
||||||
* Hungarian - kaciokos
|
|
||||||
* Italian - @Fs00
|
|
||||||
* Turkish - @emintufan
|
|
||||||
|
|
||||||
### 11.9 (2021-03-20)
|
|
||||||
|
|
||||||
* New calendar and clock pickers
|
|
||||||
* New preference to default to text input for date and time
|
|
||||||
* Fix issue causing Tasks to use wrong search provider
|
|
||||||
* Fix crash when Nextcloud/ownCloud don't send list owner
|
|
||||||
* Update translations
|
|
||||||
* Basque - Sergio Varela
|
|
||||||
* Croatian - @milotype
|
|
||||||
* Dutch - @fvbommel
|
|
||||||
* French - @FlorianLeChat
|
|
||||||
* German - Achim Schumacher
|
|
||||||
* Hungarian - kaciokos
|
|
||||||
* Indonesian - when we were sober
|
|
||||||
* Simplified Chinese - @sr093906
|
|
||||||
* Spanish - @FlorianLeChat
|
|
||||||
* Ukrainian - @IhorHordiichuk
|
|
||||||
|
|
||||||
### 11.8 (2021-03-15)
|
|
||||||
|
|
||||||
* CalDAV: Send shared list invites
|
|
||||||
* Compatible with Tasks.org, Nextcloud, ownCloud, and sabre/dav
|
|
||||||
* Show shared list invite status in list settings
|
|
||||||
* Fix drawer count when list is shared with 2+ users
|
|
||||||
* Removed legacy EteSync v1 list management features
|
|
||||||
* Dropped support for Android 6.0
|
|
||||||
* Update translations
|
|
||||||
* Arabic - @mhmdanas
|
|
||||||
* Dutch - @fvbommel
|
|
||||||
* Esperanto - @jakubfabijan
|
|
||||||
* French - @FlorianLeChat
|
|
||||||
* German - @Jerome2103
|
|
||||||
* Hungarian - kaciokos
|
|
||||||
* Indonesian - when we were sober, @andhikapangestu29
|
|
||||||
* Norwegian Bokmål - @comradekingu
|
|
||||||
* Polish - @doegedomita
|
|
||||||
* Portuguese - @Jerome2103
|
|
||||||
* Spanish - @FlorianLeChat
|
|
||||||
* Turkish - Oğuz Ersen
|
|
||||||
* Ukrainian - @IhorHordiichuk
|
|
||||||
|
|
||||||
### 11.7 (2021-03-08)
|
|
||||||
|
|
||||||
* CalDAV: Display shared list members in list settings
|
|
||||||
* Compatible with Tasks.org, Nextcloud, ownCloud, OpenXchange, and sabre/dav
|
|
||||||
* CalDAV: List owners can remove shared list members from list
|
|
||||||
* Compatible with Tasks.org, Nextcloud, ownCloud, and sabre/dav
|
|
||||||
* Fix time zone issue in recurrence picker
|
|
||||||
* Update translations
|
|
||||||
* Arabic - @mhmdanas
|
|
||||||
* Basque - Sergio Varela
|
|
||||||
* Dutch - @fvbommel
|
|
||||||
* French - @FlorianLeChat
|
|
||||||
* Hungarian - kaciokos
|
|
||||||
* Indonesian - @putulopi
|
|
||||||
* Simplified Chinese - @sr093906
|
|
||||||
* Spanish - @FlorianLeChat
|
|
||||||
* Turkish - @emintufan, Oğuz Ersen
|
|
||||||
* Ukrainian - @IhorHordiichuk
|
|
||||||
|
|
||||||
### 11.6.1 (2021-03-11)
|
|
||||||
|
|
||||||
* F-Droid: Fix OpenStreetMap crash
|
|
||||||
|
|
||||||
### 11.6 (2021-03-04)
|
|
||||||
|
|
||||||
* CalDAV: Display indicator in drawer when a list is shared with other users
|
|
||||||
* Compatible with Tasks.org, Nextcloud, ownCloud, OpenXchange, and sabre/dav
|
|
||||||
* CalDAV: Don't upload changes to read-only lists
|
|
||||||
([#931](https://github.com/tasks/tasks/issues/931))
|
|
||||||
* Remove unnecessary icon-mirroring for RTL users
|
|
||||||
([#1385](https://github.com/tasks/tasks/issues/1385) and
|
|
||||||
[#1391](https://github.com/tasks/tasks/pull/1391)) - Thanks to @mhmdanas
|
|
||||||
* Update translations
|
|
||||||
* Arabic - @mhmdanas
|
|
||||||
* Basque - Sergio Varela
|
|
||||||
* Bulgarian - @StoyanDimitrov
|
|
||||||
* Czech - @vitSkalicky
|
|
||||||
* Dutch - @fvbommel
|
|
||||||
* French - @FlorianLeChat
|
|
||||||
* Hungarian - kaciokos
|
|
||||||
* Indonesian - @putulopi
|
|
||||||
* Russian - Nikita Epifanov
|
|
||||||
* Simplified Chinese - @sr093906
|
|
||||||
* Sinhala - HelaBasa
|
|
||||||
* Spanish - @FlorianLeChat
|
|
||||||
* Ukrainian - @IhorHordiichuk
|
|
||||||
|
|
||||||
### 11.5.2 (2021-02-25)
|
|
||||||
|
|
||||||
* Fix CalDAV sync error
|
|
||||||
* Report errors when generating recurrence dates
|
|
||||||
|
|
||||||
### 11.5.1 (2021-02-24)
|
|
||||||
|
|
||||||
* Fix 'repeat until' date
|
|
||||||
* Fix repeat dates for UTC+13
|
|
||||||
([#1374](https://github.com/tasks/tasks/issues/1374))
|
|
||||||
* F-Droid: Handle null name in Nominatim reverse geocoder
|
|
||||||
([#1380](https://github.com/tasks/tasks/issues/1380))
|
|
||||||
* Update translations
|
|
||||||
* Basque - Sergio Varela
|
|
||||||
* Croatian - @ggdorman
|
|
||||||
* Dutch - @fvbommel
|
|
||||||
* French - @FlorianLeChat
|
|
||||||
* Hungarian - kaciokos
|
|
||||||
* Norwegian Bokmål - @comradekingu
|
|
||||||
* Polish - @alex-ter
|
|
||||||
* Russian - Nikita Epifanov
|
|
||||||
* Simplified Chinese - @sr093906
|
|
||||||
* Spanish - @FlorianLeChat
|
|
||||||
* Turkish - Oğuz Ersen
|
|
||||||
* Ukrainian - @IhorHordiichuk
|
|
||||||
* Urdu - Maaz
|
|
||||||
|
|
||||||
### 11.5 (2021-02-17)
|
|
||||||
|
|
||||||
* Sync snooze time with Tasks.org, DAVx⁵, CalDAV, EteSync, and DecSync
|
|
||||||
* Compatible with Thunderbird
|
|
||||||
* New map theme preference
|
|
||||||
* 10 new icons
|
|
||||||
* F-Droid: Use Nominatim for reverse geocoding
|
|
||||||
* Google Play: Use OpenStreetMap tiles when Play Services not available
|
|
||||||
* Google Play: Use Android location services when Play Services not available
|
|
||||||
* Tasks.org accounts: Use Google Places for map search
|
|
||||||
* Update translations
|
|
||||||
* Dutch - @fvbommel
|
|
||||||
* French - @FlorianLeChat
|
|
||||||
* Hungarian - kaciokos
|
|
||||||
* Indonesian - when we were sober
|
|
||||||
* Simplified Chinese - @sr093906
|
|
||||||
* Spanish - @FlorianLeChat
|
|
||||||
* Ukrainian - @IhorHordiichuk
|
|
||||||
|
|
||||||
### 11.4 (2021-02-09)
|
|
||||||
|
|
||||||
* Sync collapsed subtask state with Tasks.org, DAVx⁵, CalDAV, EteSync, and
|
|
||||||
DecSync ([#1339](https://github.com/tasks/tasks/issues/1339))
|
|
||||||
* Compatible with Nextcloud and ownCloud
|
|
||||||
* F-Droid: Add location based reminders ([#770](https://github.com/tasks/tasks/issues/770))
|
|
||||||
* F-Droid: Replace Mapbox tiles with OpenStreetMap tiles ([#922](https://github.com/tasks/tasks/issues/922))
|
|
||||||
* Fix default start date ([#1350](https://github.com/tasks/tasks/issues/1350))
|
|
||||||
|
|
||||||
### 11.3.4 (2021-02-03)
|
|
||||||
|
|
||||||
* Adjust start times by one second during sync
|
|
||||||
([#1326](https://github.com/tasks/tasks/issues/1326))
|
|
||||||
* Can now sync start time = due time with DAVx⁵, EteSync app, and DecSync CC
|
|
||||||
* All day start date must come before all day due date with DAVx⁵, EteSync
|
|
||||||
app, and DecSync CC
|
|
||||||
* 'Show unstarted' toggled on by default
|
|
||||||
|
|
||||||
### 11.3.3 (2021-01-30)
|
|
||||||
|
|
||||||
* Fix all-day due date synchronization
|
|
||||||
([#1325](https://github.com/tasks/tasks/issues/1325))
|
|
||||||
|
|
||||||
### 11.3.2 (2021-01-28)
|
|
||||||
|
|
||||||
* Fix recurrence sync issue
|
|
||||||
([#1323](https://github.com/tasks/tasks/issues/1323))
|
|
||||||
|
|
||||||
### 11.3.1 (2021-01-27)
|
|
||||||
|
|
||||||
* Improve support for recurring tasks with subtasks
|
|
||||||
* Subtasks will be unchecked after completing a recurring task
|
|
||||||
* Clear completed will not delete subtasks of recurring tasks
|
|
||||||
* Improve widget sort header when space is limited
|
|
||||||
* Add option to hide widget title
|
|
||||||
* Fix timezone conversions during synchronization
|
|
||||||
* Add Esperanto translations - @jakubfabijan
|
|
||||||
|
|
||||||
### 11.3 (2021-01-20)
|
|
||||||
|
|
||||||
* 'Hide until' is now 'Start date'
|
|
||||||
* Synchronize start dates with Tasks.org, DAVx⁵, CalDAV, EteSync, and DecSync
|
|
||||||
* New start date picker
|
|
||||||
* New start date custom filter criteria
|
|
||||||
* Add sort 'By start date'
|
|
||||||
* Display start dates as chips
|
|
||||||
* Don't perform background sync when data saver enabled
|
|
||||||
* Preference changes
|
|
||||||
* Add app and widget preferences to disable start date chips
|
|
||||||
* Synchronization accounts displayed on main preference screen
|
|
||||||
* Removed background sync and metered connection options (now respecting data
|
|
||||||
saver mode)
|
|
||||||
* Removed Google Tasks 'Custom order synchronization fix' (automatically
|
|
||||||
performing full sync if 'My order' enabled)
|
|
||||||
* Remove support for legacy XML backup format ([more info](https://github.com/tasks/tasks/issues/1565))
|
|
||||||
* Bug fixes
|
|
||||||
|
|
||||||
### 11.2.2 (2021-01-07)
|
|
||||||
|
|
||||||
* Rename 'Lists' to 'Local lists' to clarify that they are not synchronized
|
|
||||||
* Tasks.org sign in improvements
|
|
||||||
* Miscellaneous improvements - Thanks @mhmdanas!
|
|
||||||
|
|
||||||
### 11.2.1 (2021-01-05)
|
|
||||||
|
|
||||||
* Fix Portuguese translation issue
|
|
||||||
* Report OpenTask sync errors
|
|
||||||
* Report Tasks.org sign in errors
|
|
||||||
* Don't crash on widget configuration error
|
|
||||||
* Purchase dialog changes
|
|
||||||
|
|
||||||
### 11.2 (2020-12-30)
|
|
||||||
|
|
||||||
* [Synchronize your Tasks.org account with third-party task and calendar apps, like Outlook,
|
|
||||||
Thunderbird, or Apple Reminders](https://tasks.org/passwords)
|
|
||||||
* Miscellaneous improvements - Thanks @mhmdanas!
|
|
||||||
|
|
||||||
### 11.1.1 (2020-12-24)
|
|
||||||
|
|
||||||
* Fix compatibility issues with third-party clients
|
|
||||||
* Completed tasks without completion dates
|
|
||||||
([222a34f](https://github.com/tasks/tasks/commit/222a34fc263816bb23f633bc9c79de78aeb3968d))
|
|
||||||
* Tasks with start date but no due date
|
|
||||||
([7a1d566](https://github.com/tasks/tasks/commit/7a1d566bfb613b95d3fe1df46d8fa67200c91021))
|
|
||||||
* Miscellaneous improvements - Thanks @mhmdanas!
|
|
||||||
|
|
||||||
### 11.1 (2020-12-21)
|
|
||||||
|
|
||||||
* Add [DecSync CC synchronization](https://tasks.org/decsync)
|
|
||||||
* Fix rescheduling remotely completed recurring task
|
|
||||||
([5eb9370](https://github.com/tasks/tasks/commit/5eb9370294ef707b3e667c4a42851030419920d8))
|
|
||||||
* Miscellaneous code improvements - Thanks @mhmdanas!
|
|
||||||
|
|
||||||
### 11.0.1 (2020-12-17)
|
|
||||||
|
|
||||||
* Fix EteSync client issue with v2 accounts
|
|
||||||
([b761309](https://github.com/tasks/tasks/commit/b76130902ae0be6e1d580d588798a9ed0d7ff385))
|
|
||||||
* Fix multi-select 'Pick time' crash
|
|
||||||
* Fix default hide until due time
|
|
||||||
([#842](https://github.com/tasks/tasks/issues/842#issuecomment-746358382))
|
|
||||||
* Add Croatian translations - Garden Hose
|
|
||||||
* Add Urdu translations - Maaz
|
|
||||||
|
|
||||||
### 11.0 (2020-12-10)
|
|
||||||
|
|
||||||
* New Tasks.org synchronization service
|
|
||||||
* Multi-select rescheduling
|
|
||||||
* New task default settings
|
|
||||||
* Default tags
|
|
||||||
* Default recurrence
|
|
||||||
* Default location
|
|
||||||
* Hide until due time
|
|
||||||
* New custom filter criteria
|
|
||||||
* Hidden tasks
|
|
||||||
* Completed tasks
|
|
||||||
* Subtasks
|
|
||||||
* Parent tasks
|
|
||||||
* Recurring tasks
|
|
||||||
* Added EteSync v2 support
|
|
||||||
* Deprecated EteSync v1 support
|
|
||||||
* v1 accounts cannot be added to Tasks.org
|
|
||||||
* v1 accounts can be added to the EteSync Android client
|
|
||||||
* Add ability to delete comments (Thanks to @romedius!)
|
|
||||||
* Add option to always display date (Thanks to @T0M0F!)
|
|
||||||
* Copy subtasks when copying tasks (Thanks to @supermzn!)
|
|
||||||
* Fix ring five times cutoff (Thanks to @przemhb!)
|
|
||||||
* Bug fixes
|
|
||||||
* Translation updates
|
|
||||||
* Arabic - @mhmdanas
|
|
||||||
* Basque - @osoitz, @ppasserini
|
|
||||||
* Dutch - @fvbommel
|
|
||||||
* French - @FlorianLeChat
|
|
||||||
* German - @franconian, J. Lavoie, @myabc
|
|
||||||
* Hebrew - @yarons
|
|
||||||
* Hungarian - kaciokos
|
|
||||||
* Indonesian - @andikatuluspangestu
|
|
||||||
* Italian - @ppasserini, @Fs00, @pjammo
|
|
||||||
* Korean - Sunjae Choi, @Hwaro-K
|
|
||||||
* Norwegian Bokmål - @comradekingu
|
|
||||||
* Polish - @alex-ter
|
|
||||||
* Russian - Nikita Epifanov
|
|
||||||
* Simplified Chinese - @sr093906
|
|
||||||
* Spanish - @FlorianLeChat
|
|
||||||
* Traditional Chinese - @realpineapplemilk
|
|
||||||
* Turkish - @emintufan, Oğuz Ersen
|
|
||||||
|
|
||||||
### 10.4.1 (2020-11-09)
|
|
||||||
|
|
||||||
* Fix Mapbox Maps crash on Android 11 (F-Droid only)
|
|
||||||
|
|
||||||
### 10.4 (2020-10-09)
|
|
||||||
|
|
||||||
* New widget configuration options
|
|
||||||
* Sort
|
|
||||||
* Show hidden
|
|
||||||
* Show completed
|
|
||||||
* Header spacing
|
|
||||||
* Bug fixes
|
|
||||||
|
|
||||||
### 10.3 (2020-10-02)
|
|
||||||
|
|
||||||
* Collapsible sort groups in widget
|
|
||||||
* Add 'System default' widget theme
|
|
||||||
* Bug fixes
|
|
||||||
|
|
||||||
### 10.2 (2020-09-25)
|
|
||||||
|
|
||||||
* Display list, tag, and place chips on widgets
|
|
||||||
* Add option to disable list, tag, and place chips on widgets
|
|
||||||
|
|
||||||
### 10.1 (2020-09-23)
|
|
||||||
|
|
||||||
* Android 11 support
|
|
||||||
* Backup improvements
|
|
||||||
* Swipe-to-refresh initiates DAVx5/EteSync sync
|
|
||||||
* Show indicator when DAVx5/EteSync are synchronizing
|
|
||||||
* Bug fixes
|
|
||||||
|
|
||||||
### 10.0.3 (2020-09-16)
|
|
||||||
|
|
||||||
* Fix crash from calendar event snackbar
|
|
||||||
* Fix crash when setting Google Maps markers
|
|
||||||
* Fix invalid calendar entry creation
|
|
||||||
|
|
||||||
### 10.0.2 (2020-09-14)
|
|
||||||
|
|
||||||
* Fix crash from corrupted custom filter
|
|
||||||
* Fix crash in 'Astrid manual sorting' mode
|
|
||||||
* Fix missing 'Calendar event created' snackbar
|
|
||||||
|
|
||||||
### 10.0.1 (2020-09-05)
|
|
||||||
|
|
||||||
* Bug fixes
|
|
||||||
* Translation updates
|
|
||||||
* Czech - @vitSkalicky
|
|
||||||
* Danish - @ChMunk
|
|
||||||
|
|
||||||
### 10.0 (2020-08-31)
|
|
||||||
|
|
||||||
* PRO: DAVx⁵ support (requires [DAVx⁵ beta](https://tasks.org/davx5))
|
|
||||||
* PRO: EteSync client support
|
|
||||||
* [ToDo Agenda](https://play.google.com/store/apps/details?id=org.andstatus.todoagenda) integration
|
|
||||||
* Changed backstack behavior to follow Android conventions
|
|
||||||
* Major internal changes! Please report any bugs!
|
|
||||||
* Remove Mapbox tiles (Google Play only)
|
|
||||||
* Added 'Astrid manual sort' information to backup file
|
|
||||||
* Bug fixes
|
|
||||||
* Performance improvements
|
|
||||||
* Security improvements
|
|
||||||
|
|
||||||
[Older releases](https://github.com/tasks/tasks/blob/main/V06_09_CHANGELOG.md)
|
|
||||||
@ -1,20 +0,0 @@
|
|||||||
{
|
|
||||||
"version": 3,
|
|
||||||
"artifactType": {
|
|
||||||
"type": "APK",
|
|
||||||
"kind": "Directory"
|
|
||||||
},
|
|
||||||
"applicationId": "org.tasks.ak",
|
|
||||||
"variantName": "genericRelease",
|
|
||||||
"elements": [
|
|
||||||
{
|
|
||||||
"type": "SINGLE",
|
|
||||||
"filters": [],
|
|
||||||
"attributes": [],
|
|
||||||
"versionCode": 130804,
|
|
||||||
"versionName": "14.0.6",
|
|
||||||
"outputFile": "app-generic-release.apk"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"elementType": "File"
|
|
||||||
}
|
|
||||||
@ -0,0 +1,263 @@
|
|||||||
|
<?xml version="1.0"?>
|
||||||
|
<!DOCTYPE module PUBLIC
|
||||||
|
"-//Checkstyle//DTD Checkstyle Configuration 1.3//EN"
|
||||||
|
"https://checkstyle.org/dtds/configuration_1_3.dtd">
|
||||||
|
|
||||||
|
<!-- https://raw.githubusercontent.com/checkstyle/checkstyle/checkstyle-8.16/src/main/resources/google_checks.xml -->
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Checkstyle configuration that checks the Google coding conventions from Google Java Style
|
||||||
|
that can be found at https://google.github.io/styleguide/javaguide.html.
|
||||||
|
|
||||||
|
Checkstyle is very configurable. Be sure to read the documentation at
|
||||||
|
http://checkstyle.sf.net (or in your downloaded distribution).
|
||||||
|
|
||||||
|
To completely disable a check, just comment it out or delete it from the file.
|
||||||
|
|
||||||
|
Authors: Max Vetrenko, Ruslan Diachenko, Roman Ivanov.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<module name = "Checker">
|
||||||
|
<property name="charset" value="UTF-8"/>
|
||||||
|
|
||||||
|
<property name="severity" value="warning"/>
|
||||||
|
|
||||||
|
<property name="fileExtensions" value="java, properties, xml"/>
|
||||||
|
<!-- Checks for whitespace -->
|
||||||
|
<!-- See http://checkstyle.sf.net/config_whitespace.html -->
|
||||||
|
<module name="FileTabCharacter">
|
||||||
|
<property name="eachLine" value="true"/>
|
||||||
|
</module>
|
||||||
|
|
||||||
|
<module name="TreeWalker">
|
||||||
|
<module name="OuterTypeFilename"/>
|
||||||
|
<module name="IllegalTokenText">
|
||||||
|
<property name="tokens" value="STRING_LITERAL, CHAR_LITERAL"/>
|
||||||
|
<property name="format"
|
||||||
|
value="\\u00(09|0(a|A)|0(c|C)|0(d|D)|22|27|5(C|c))|\\(0(10|11|12|14|15|42|47)|134)"/>
|
||||||
|
<property name="message"
|
||||||
|
value="Consider using special escape sequence instead of octal value or Unicode escaped value."/>
|
||||||
|
</module>
|
||||||
|
<module name="AvoidEscapedUnicodeCharacters">
|
||||||
|
<property name="allowEscapesForControlCharacters" value="true"/>
|
||||||
|
<property name="allowByTailComment" value="true"/>
|
||||||
|
<property name="allowNonPrintableEscapes" value="true"/>
|
||||||
|
</module>
|
||||||
|
<module name="LineLength">
|
||||||
|
<property name="max" value="100"/>
|
||||||
|
<property name="ignorePattern" value="^package.*|^import.*|a href|href|http://|https://|ftp://"/>
|
||||||
|
</module>
|
||||||
|
<module name="AvoidStarImport"/>
|
||||||
|
<module name="OneTopLevelClass"/>
|
||||||
|
<module name="NoLineWrap"/>
|
||||||
|
<module name="EmptyBlock">
|
||||||
|
<property name="option" value="TEXT"/>
|
||||||
|
<property name="tokens"
|
||||||
|
value="LITERAL_TRY, LITERAL_FINALLY, LITERAL_IF, LITERAL_ELSE, LITERAL_SWITCH"/>
|
||||||
|
</module>
|
||||||
|
<module name="NeedBraces"/>
|
||||||
|
<module name="LeftCurly"/>
|
||||||
|
<module name="RightCurly">
|
||||||
|
<property name="id" value="RightCurlySame"/>
|
||||||
|
<property name="tokens"
|
||||||
|
value="LITERAL_TRY, LITERAL_CATCH, LITERAL_FINALLY, LITERAL_IF, LITERAL_ELSE,
|
||||||
|
LITERAL_DO"/>
|
||||||
|
</module>
|
||||||
|
<module name="RightCurly">
|
||||||
|
<property name="id" value="RightCurlyAlone"/>
|
||||||
|
<property name="option" value="alone"/>
|
||||||
|
<property name="tokens"
|
||||||
|
value="CLASS_DEF, METHOD_DEF, CTOR_DEF, LITERAL_FOR, LITERAL_WHILE, STATIC_INIT,
|
||||||
|
INSTANCE_INIT"/>
|
||||||
|
</module>
|
||||||
|
<module name="WhitespaceAround">
|
||||||
|
<property name="allowEmptyConstructors" value="true"/>
|
||||||
|
<property name="allowEmptyMethods" value="true"/>
|
||||||
|
<property name="allowEmptyTypes" value="true"/>
|
||||||
|
<property name="allowEmptyLoops" value="true"/>
|
||||||
|
<message key="ws.notFollowed"
|
||||||
|
value="WhitespaceAround: ''{0}'' is not followed by whitespace. Empty blocks may only be represented as '{}' when not part of a multi-block statement (4.1.3)"/>
|
||||||
|
<message key="ws.notPreceded"
|
||||||
|
value="WhitespaceAround: ''{0}'' is not preceded with whitespace."/>
|
||||||
|
</module>
|
||||||
|
<module name="OneStatementPerLine"/>
|
||||||
|
<module name="MultipleVariableDeclarations"/>
|
||||||
|
<module name="ArrayTypeStyle"/>
|
||||||
|
<module name="MissingSwitchDefault"/>
|
||||||
|
<module name="FallThrough"/>
|
||||||
|
<module name="UpperEll"/>
|
||||||
|
<module name="ModifierOrder"/>
|
||||||
|
<module name="EmptyLineSeparator">
|
||||||
|
<property name="allowNoEmptyLineBetweenFields" value="true"/>
|
||||||
|
</module>
|
||||||
|
<module name="SeparatorWrap">
|
||||||
|
<property name="id" value="SeparatorWrapDot"/>
|
||||||
|
<property name="tokens" value="DOT"/>
|
||||||
|
<property name="option" value="nl"/>
|
||||||
|
</module>
|
||||||
|
<module name="SeparatorWrap">
|
||||||
|
<property name="id" value="SeparatorWrapComma"/>
|
||||||
|
<property name="tokens" value="COMMA"/>
|
||||||
|
<property name="option" value="EOL"/>
|
||||||
|
</module>
|
||||||
|
<module name="SeparatorWrap">
|
||||||
|
<!-- ELLIPSIS is EOL until https://github.com/google/styleguide/issues/258 -->
|
||||||
|
<property name="id" value="SeparatorWrapEllipsis"/>
|
||||||
|
<property name="tokens" value="ELLIPSIS"/>
|
||||||
|
<property name="option" value="EOL"/>
|
||||||
|
</module>
|
||||||
|
<module name="SeparatorWrap">
|
||||||
|
<!-- ARRAY_DECLARATOR is EOL until https://github.com/google/styleguide/issues/259 -->
|
||||||
|
<property name="id" value="SeparatorWrapArrayDeclarator"/>
|
||||||
|
<property name="tokens" value="ARRAY_DECLARATOR"/>
|
||||||
|
<property name="option" value="EOL"/>
|
||||||
|
</module>
|
||||||
|
<module name="SeparatorWrap">
|
||||||
|
<property name="id" value="SeparatorWrapMethodRef"/>
|
||||||
|
<property name="tokens" value="METHOD_REF"/>
|
||||||
|
<property name="option" value="nl"/>
|
||||||
|
</module>
|
||||||
|
<module name="PackageName">
|
||||||
|
<property name="format" value="^[a-z]+(\.[a-z][a-z0-9]*)*$"/>
|
||||||
|
<message key="name.invalidPattern"
|
||||||
|
value="Package name ''{0}'' must match pattern ''{1}''."/>
|
||||||
|
</module>
|
||||||
|
<module name="TypeName">
|
||||||
|
<message key="name.invalidPattern"
|
||||||
|
value="Type name ''{0}'' must match pattern ''{1}''."/>
|
||||||
|
</module>
|
||||||
|
<module name="MemberName">
|
||||||
|
<property name="format" value="^[a-z][a-z0-9][a-zA-Z0-9]*$"/>
|
||||||
|
<message key="name.invalidPattern"
|
||||||
|
value="Member name ''{0}'' must match pattern ''{1}''."/>
|
||||||
|
</module>
|
||||||
|
<module name="ParameterName">
|
||||||
|
<property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/>
|
||||||
|
<message key="name.invalidPattern"
|
||||||
|
value="Parameter name ''{0}'' must match pattern ''{1}''."/>
|
||||||
|
</module>
|
||||||
|
<module name="LambdaParameterName">
|
||||||
|
<property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/>
|
||||||
|
<message key="name.invalidPattern"
|
||||||
|
value="Lambda parameter name ''{0}'' must match pattern ''{1}''."/>
|
||||||
|
</module>
|
||||||
|
<module name="CatchParameterName">
|
||||||
|
<property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/>
|
||||||
|
<message key="name.invalidPattern"
|
||||||
|
value="Catch parameter name ''{0}'' must match pattern ''{1}''."/>
|
||||||
|
</module>
|
||||||
|
<module name="LocalVariableName">
|
||||||
|
<property name="tokens" value="VARIABLE_DEF"/>
|
||||||
|
<property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/>
|
||||||
|
<message key="name.invalidPattern"
|
||||||
|
value="Local variable name ''{0}'' must match pattern ''{1}''."/>
|
||||||
|
</module>
|
||||||
|
<module name="ClassTypeParameterName">
|
||||||
|
<property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)"/>
|
||||||
|
<message key="name.invalidPattern"
|
||||||
|
value="Class type name ''{0}'' must match pattern ''{1}''."/>
|
||||||
|
</module>
|
||||||
|
<module name="MethodTypeParameterName">
|
||||||
|
<property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)"/>
|
||||||
|
<message key="name.invalidPattern"
|
||||||
|
value="Method type name ''{0}'' must match pattern ''{1}''."/>
|
||||||
|
</module>
|
||||||
|
<module name="InterfaceTypeParameterName">
|
||||||
|
<property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)"/>
|
||||||
|
<message key="name.invalidPattern"
|
||||||
|
value="Interface type name ''{0}'' must match pattern ''{1}''."/>
|
||||||
|
</module>
|
||||||
|
<module name="NoFinalizer"/>
|
||||||
|
<module name="GenericWhitespace">
|
||||||
|
<message key="ws.followed"
|
||||||
|
value="GenericWhitespace ''{0}'' is followed by whitespace."/>
|
||||||
|
<message key="ws.preceded"
|
||||||
|
value="GenericWhitespace ''{0}'' is preceded with whitespace."/>
|
||||||
|
<message key="ws.illegalFollow"
|
||||||
|
value="GenericWhitespace ''{0}'' should followed by whitespace."/>
|
||||||
|
<message key="ws.notPreceded"
|
||||||
|
value="GenericWhitespace ''{0}'' is not preceded with whitespace."/>
|
||||||
|
</module>
|
||||||
|
<module name="Indentation">
|
||||||
|
<property name="basicOffset" value="2"/>
|
||||||
|
<property name="braceAdjustment" value="0"/>
|
||||||
|
<property name="caseIndent" value="2"/>
|
||||||
|
<property name="throwsIndent" value="4"/>
|
||||||
|
<property name="lineWrappingIndentation" value="4"/>
|
||||||
|
<property name="arrayInitIndent" value="2"/>
|
||||||
|
</module>
|
||||||
|
<module name="AbbreviationAsWordInName">
|
||||||
|
<property name="ignoreFinal" value="false"/>
|
||||||
|
<property name="allowedAbbreviationLength" value="1"/>
|
||||||
|
</module>
|
||||||
|
<module name="OverloadMethodsDeclarationOrder"/>
|
||||||
|
<module name="VariableDeclarationUsageDistance"/>
|
||||||
|
<module name="CustomImportOrder">
|
||||||
|
<property name="sortImportsInGroupAlphabetically" value="true"/>
|
||||||
|
<property name="separateLineBetweenGroups" value="true"/>
|
||||||
|
<property name="customImportOrderRules" value="STATIC###THIRD_PARTY_PACKAGE"/>
|
||||||
|
</module>
|
||||||
|
<module name="MethodParamPad"/>
|
||||||
|
<module name="NoWhitespaceBefore">
|
||||||
|
<property name="tokens"
|
||||||
|
value="COMMA, SEMI, POST_INC, POST_DEC, DOT, ELLIPSIS, METHOD_REF"/>
|
||||||
|
<property name="allowLineBreaks" value="true"/>
|
||||||
|
</module>
|
||||||
|
<module name="ParenPad"/>
|
||||||
|
<module name="OperatorWrap">
|
||||||
|
<property name="option" value="NL"/>
|
||||||
|
<property name="tokens"
|
||||||
|
value="BAND, BOR, BSR, BXOR, DIV, EQUAL, GE, GT, LAND, LE, LITERAL_INSTANCEOF, LOR,
|
||||||
|
LT, MINUS, MOD, NOT_EQUAL, PLUS, QUESTION, SL, SR, STAR, METHOD_REF "/>
|
||||||
|
</module>
|
||||||
|
<module name="AnnotationLocation">
|
||||||
|
<property name="id" value="AnnotationLocationMostCases"/>
|
||||||
|
<property name="tokens"
|
||||||
|
value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF, METHOD_DEF, CTOR_DEF"/>
|
||||||
|
</module>
|
||||||
|
<module name="AnnotationLocation">
|
||||||
|
<property name="id" value="AnnotationLocationVariables"/>
|
||||||
|
<property name="tokens" value="VARIABLE_DEF"/>
|
||||||
|
<property name="allowSamelineMultipleAnnotations" value="true"/>
|
||||||
|
</module>
|
||||||
|
<module name="NonEmptyAtclauseDescription"/>
|
||||||
|
<module name="JavadocTagContinuationIndentation">
|
||||||
|
<property name="severity" value="ignore" />
|
||||||
|
</module>
|
||||||
|
<module name="SummaryJavadoc">
|
||||||
|
<property name="severity" value="ignore" />
|
||||||
|
<property name="forbiddenSummaryFragments"
|
||||||
|
value="^@return the *|^This method returns |^A [{]@code [a-zA-Z0-9]+[}]( is a )"/>
|
||||||
|
</module>
|
||||||
|
<module name="JavadocParagraph">
|
||||||
|
<property name="severity" value="ignore" />
|
||||||
|
</module>
|
||||||
|
<module name="AtclauseOrder">
|
||||||
|
<property name="tagOrder" value="@param, @return, @throws, @deprecated"/>
|
||||||
|
<property name="target"
|
||||||
|
value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF, METHOD_DEF, CTOR_DEF, VARIABLE_DEF"/>
|
||||||
|
</module>
|
||||||
|
<module name="JavadocMethod">
|
||||||
|
<property name="severity" value="ignore" />
|
||||||
|
<property name="scope" value="public"/>
|
||||||
|
<property name="allowMissingParamTags" value="true"/>
|
||||||
|
<property name="allowMissingThrowsTags" value="true"/>
|
||||||
|
<property name="allowMissingReturnTag" value="true"/>
|
||||||
|
<property name="minLineCount" value="2"/>
|
||||||
|
<property name="allowedAnnotations" value="Override, Test"/>
|
||||||
|
<property name="allowThrowsTagsForSubclasses" value="true"/>
|
||||||
|
</module>
|
||||||
|
<module name="MethodName">
|
||||||
|
<property name="format" value="^[a-z][a-z0-9][a-zA-Z0-9_]*$"/>
|
||||||
|
<message key="name.invalidPattern"
|
||||||
|
value="Method name ''{0}'' must match pattern ''{1}''."/>
|
||||||
|
</module>
|
||||||
|
<module name="SingleLineJavadoc">
|
||||||
|
<property name="severity" value="ignore"/>
|
||||||
|
</module>
|
||||||
|
<module name="EmptyCatchBlock">
|
||||||
|
<property name="exceptionVariableName" value="expected"/>
|
||||||
|
</module>
|
||||||
|
<module name="CommentsIndentation"/>
|
||||||
|
</module>
|
||||||
|
</module>
|
||||||
@ -0,0 +1,756 @@
|
|||||||
|
- artifact: com.gitlab.bitfireAT:dav4jvm:+
|
||||||
|
name: dav4jvm
|
||||||
|
copyrightHolder: bitfire web engineering (Ricki Hirner, Bernhard Stockmann)
|
||||||
|
license: Mozilla Public License, Version 2.0
|
||||||
|
licenseUrl: https://www.mozilla.org/en-US/MPL/2.0/
|
||||||
|
- artifact: com.gitlab.bitfireAT:ical4android:+
|
||||||
|
name: ical4android
|
||||||
|
copyrightHolder: bitfire web engineering (Ricki Hirner, Bernhard Stockmann)
|
||||||
|
license: GNU General Public License, Version 3.0
|
||||||
|
licenseUrl: https://www.gnu.org/licenses/gpl.txt
|
||||||
|
- artifact: com.gitlab.bitfireAT:cert4android:+
|
||||||
|
name: cert4android
|
||||||
|
copyrightHolder: bitfire web engineering (Ricki Hirner, Bernhard Stockmann)
|
||||||
|
licenseUrl: https://www.gnu.org/licenses/gpl.txt
|
||||||
|
license: GNU General Public License, Version 3.0
|
||||||
|
- artifact: androidx.coordinatorlayout:coordinatorlayout:+
|
||||||
|
name: Android Support Library Coordinator Layout
|
||||||
|
copyrightHolder: Android Open Source Project
|
||||||
|
license: The Apache Software License, Version 2.0
|
||||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||||
|
url: http://developer.android.com/tools/extras/support-library.html
|
||||||
|
- artifact: androidx.constraintlayout:constraintlayout:+
|
||||||
|
name: Android ConstraintLayout
|
||||||
|
copyrightHolder: Android Open Source Project
|
||||||
|
license: The Apache Software License, Version 2.0
|
||||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||||
|
url: http://tools.android.com
|
||||||
|
- artifact: androidx.sqlite:sqlite:+
|
||||||
|
name: Android DB
|
||||||
|
copyrightHolder: Android Open Source Project
|
||||||
|
license: The Apache Software License, Version 2.0
|
||||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||||
|
url: https://developer.android.com/topic/libraries/architecture/index.html
|
||||||
|
- artifact: com.google.apis:google-api-services-drive:+
|
||||||
|
name: Drive API v3-rev136-1.25.0
|
||||||
|
copyrightHolder: Google Inc.
|
||||||
|
license: The Apache Software License, Version 2.0
|
||||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||||
|
- artifact: androidx.fragment:fragment:+
|
||||||
|
name: Android Support Library fragment
|
||||||
|
copyrightHolder: Android Open Source Project
|
||||||
|
license: The Apache Software License, Version 2.0
|
||||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||||
|
url: http://developer.android.com/tools/extras/support-library.html
|
||||||
|
- artifact: androidx.vectordrawable:vectordrawable-animated:+
|
||||||
|
name: Android Support AnimatedVectorDrawable
|
||||||
|
copyrightHolder: Android Open Source Project
|
||||||
|
license: The Apache Software License, Version 2.0
|
||||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||||
|
url: http://developer.android.com/tools/extras/support-library.html
|
||||||
|
- artifact: com.mapbox.mapboxsdk:mapbox-sdk-services:+
|
||||||
|
name: Mapbox services
|
||||||
|
copyrightHolder: Mapbox
|
||||||
|
license: MIT License
|
||||||
|
licenseUrl: http://www.opensource.org/licenses/mit-license.php
|
||||||
|
url: https://github.com/mapbox/mapbox-java
|
||||||
|
forceGenerate: true
|
||||||
|
- artifact: androidx.core:core:+
|
||||||
|
name: Android Support Library compat
|
||||||
|
copyrightHolder: Android Open Source Project
|
||||||
|
license: The Apache Software License, Version 2.0
|
||||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||||
|
url: http://developer.android.com/tools/extras/support-library.html
|
||||||
|
- artifact: androidx.arch.core:core-common:+
|
||||||
|
name: Android Arch-Common
|
||||||
|
copyrightHolder: Android Open Source Project
|
||||||
|
license: The Apache Software License, Version 2.0
|
||||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||||
|
url: https://developer.android.com/topic/libraries/architecture/index.html
|
||||||
|
- artifact: androidx.room:room-common:+
|
||||||
|
name: Android Room-Common
|
||||||
|
copyrightHolder: Android Open Source Project
|
||||||
|
license: The Apache Software License, Version 2.0
|
||||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||||
|
url: https://developer.android.com/topic/libraries/architecture/index.html
|
||||||
|
- artifact: androidx.room:room-runtime:+
|
||||||
|
name: Android Room-Runtime
|
||||||
|
copyrightHolder: Android Open Source Project
|
||||||
|
license: The Apache Software License, Version 2.0
|
||||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||||
|
url: https://developer.android.com/topic/libraries/architecture/index.html
|
||||||
|
- artifact: com.google.code.gson:gson:+
|
||||||
|
name: Gson
|
||||||
|
copyrightHolder: Google Inc.
|
||||||
|
license: The Apache Software License, Version 2.0
|
||||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||||
|
- artifact: me.leolin:ShortcutBadger:+
|
||||||
|
name: ShortcutBadger
|
||||||
|
copyrightHolder: Leo Lin
|
||||||
|
license: The Apache Software License, Version 2.0
|
||||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||||
|
url: https://github.com/leolin310148/ShortcutBadger
|
||||||
|
- artifact: androidx.lifecycle:lifecycle-runtime:+
|
||||||
|
name: Android Lifecycle Runtime
|
||||||
|
copyrightHolder: Android Open Source Project
|
||||||
|
license: The Apache Software License, Version 2.0
|
||||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||||
|
url: https://developer.android.com/topic/libraries/architecture/index.html
|
||||||
|
- artifact: androidx.versionedparcelable:versionedparcelable:+
|
||||||
|
name: VersionedParcelable and friends
|
||||||
|
copyrightHolder: Android Open Source Project
|
||||||
|
license: The Apache Software License, Version 2.0
|
||||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||||
|
url: http://developer.android.com/tools/extras/support-library.html
|
||||||
|
- artifact: com.mapbox.mapboxsdk:mapbox-sdk-turf:+
|
||||||
|
name: Mapbox services-turf
|
||||||
|
copyrightHolder: Mapbox
|
||||||
|
license: MIT License
|
||||||
|
licenseUrl: http://www.opensource.org/licenses/mit-license.php
|
||||||
|
url: https://github.com/mapbox/mapbox-java
|
||||||
|
forceGenerate: true
|
||||||
|
- artifact: androidx.viewpager:viewpager:+
|
||||||
|
name: Android Support Library View Pager
|
||||||
|
copyrightHolder: Android Open Source Project
|
||||||
|
license: The Apache Software License, Version 2.0
|
||||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||||
|
url: http://developer.android.com/tools/extras/support-library.html
|
||||||
|
- artifact: com.mapbox.mapboxsdk:mapbox-android-core:+
|
||||||
|
name: Mapbox Android Core Library
|
||||||
|
copyrightHolder: Mapbox
|
||||||
|
license: MIT License
|
||||||
|
licenseUrl: http://www.opensource.org/licenses/mit-license.php
|
||||||
|
url: https://github.com/mapbox/mapbox-events-android
|
||||||
|
forceGenerate: true
|
||||||
|
- artifact: androidx.lifecycle:lifecycle-livedata:+
|
||||||
|
name: Android Lifecycle LiveData
|
||||||
|
copyrightHolder: Android Open Source Project
|
||||||
|
license: The Apache Software License, Version 2.0
|
||||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||||
|
url: https://developer.android.com/topic/libraries/architecture/index.html
|
||||||
|
- artifact: commons-codec:commons-codec:+
|
||||||
|
name: Apache Commons Codec
|
||||||
|
copyrightHolder: The Apache Software Foundation
|
||||||
|
license: The Apache Software License, Version 2.0
|
||||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||||
|
url: http://commons.apache.org/proper/commons-codec/
|
||||||
|
- artifact: com.mapbox.mapboxsdk:mapbox-android-sdk:+
|
||||||
|
name: Mapbox Maps SDK for Android
|
||||||
|
copyrightHolder: Mapbox
|
||||||
|
license: BSD 2-Clause
|
||||||
|
licenseUrl: https://opensource.org/licenses/BSD-2-Clause
|
||||||
|
url: https://github.com/mapbox/mapbox-gl-native
|
||||||
|
forceGenerate: true
|
||||||
|
- artifact: androidx.annotation:annotation:+
|
||||||
|
name: Android Support Library Annotations
|
||||||
|
copyrightHolder: Android Open Source Project
|
||||||
|
license: The Apache Software License, Version 2.0
|
||||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||||
|
url: http://developer.android.com/tools/extras/support-library.html
|
||||||
|
- artifact: androidx.interpolator:interpolator:+
|
||||||
|
name: Android Support Library Interpolators
|
||||||
|
copyrightHolder: Android Open Source Project
|
||||||
|
license: The Apache Software License, Version 2.0
|
||||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||||
|
url: http://developer.android.com/tools/extras/support-library.html
|
||||||
|
- artifact: javax.inject:javax.inject:+
|
||||||
|
name: javax.inject
|
||||||
|
copyrightHolder: The JSR-330 Expert Group
|
||||||
|
license: The Apache Software License, Version 2.0
|
||||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||||
|
url: http://code.google.com/p/atinject/
|
||||||
|
- artifact: com.twofortyfouram:android-plugin-api-for-locale:+
|
||||||
|
name: android-plugin-api-for-locale
|
||||||
|
copyrightHolder: two forty four a.m. LLC.
|
||||||
|
license: The Apache Software License, Version 2.0
|
||||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||||
|
- artifact: androidx.lifecycle:lifecycle-viewmodel:+
|
||||||
|
name: Android Lifecycle ViewModel
|
||||||
|
copyrightHolder: Android Open Source Project
|
||||||
|
license: The Apache Software License, Version 2.0
|
||||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||||
|
url: https://developer.android.com/topic/libraries/architecture/index.html
|
||||||
|
- artifact: org.scala-saddle:google-rfc-2445:+
|
||||||
|
name: google-rfc-2445
|
||||||
|
copyrightHolder: Google Inc.
|
||||||
|
license: The Apache Software License, Version 2.0
|
||||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||||
|
url: http://code.google.com/p/google-rfc-2445/
|
||||||
|
- artifact: com.google.dagger:dagger:+
|
||||||
|
name: Dagger
|
||||||
|
copyrightHolder: The Dagger Authors
|
||||||
|
license: The Apache Software License, Version 2.0
|
||||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||||
|
url: https://github.com/google/dagger
|
||||||
|
- artifact: com.google.guava:guava:+
|
||||||
|
name: Guava Google Core Libraries for Java
|
||||||
|
copyrightHolder: The Guava Authors
|
||||||
|
license: The Apache Software License, Version 2.0
|
||||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||||
|
- artifact: org.jetbrains:annotations:+
|
||||||
|
name: JetBrains Java Annotations
|
||||||
|
copyrightHolder: JetBrains s.r.o.
|
||||||
|
license: The Apache Software License, Version 2.0
|
||||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||||
|
url: https://github.com/JetBrains/java-annotations
|
||||||
|
- artifact: com.wdullaer:materialdatetimepicker:+
|
||||||
|
name: MaterialDateTimePicker
|
||||||
|
copyrightHolder: Wouter Dullaert
|
||||||
|
license: The Apache Software License, Version 2.0
|
||||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||||
|
url: https://github.com/wdullaer/MaterialDateTimePicker
|
||||||
|
- artifact: org.apache.commons:commons-lang3:+
|
||||||
|
name: Apache Commons Lang
|
||||||
|
copyrightHolder: The Apache Software Foundation
|
||||||
|
license: The Apache Software License, Version 2.0
|
||||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||||
|
url: http://commons.apache.org/proper/commons-lang/
|
||||||
|
- artifact: com.mapbox.mapboxsdk:mapbox-sdk-geojson:+
|
||||||
|
name: Mapbox services-geojson
|
||||||
|
copyrightHolder: Mapbox
|
||||||
|
license: MIT License
|
||||||
|
licenseUrl: http://www.opensource.org/licenses/mit-license.php
|
||||||
|
url: https://github.com/mapbox/mapbox-java
|
||||||
|
forceGenerate: true
|
||||||
|
- artifact: com.mapbox.mapboxsdk:mapbox-android-telemetry:+
|
||||||
|
name: Mapbox Android Telemetry Library
|
||||||
|
copyrightHolder: Mapbox
|
||||||
|
license: BSD 2-Clause
|
||||||
|
licenseUrl: https://opensource.org/licenses/BSD-2-Clause
|
||||||
|
url: https://github.com/mapbox/mapbox-events-android
|
||||||
|
forceGenerate: true
|
||||||
|
- artifact: androidx.loader:loader:+
|
||||||
|
name: Android Support Library loader
|
||||||
|
copyrightHolder: Android Open Source Project
|
||||||
|
license: The Apache Software License, Version 2.0
|
||||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||||
|
url: http://developer.android.com/tools/extras/support-library.html
|
||||||
|
- artifact: androidx.cursoradapter:cursoradapter:+
|
||||||
|
name: Android Support Library Cursor Adapter
|
||||||
|
copyrightHolder: Android Open Source Project
|
||||||
|
license: The Apache Software License, Version 2.0
|
||||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||||
|
url: http://developer.android.com/tools/extras/support-library.html
|
||||||
|
- artifact: androidx.lifecycle:lifecycle-livedata-core:+
|
||||||
|
name: Android Lifecycle LiveData Core
|
||||||
|
copyrightHolder: Android Open Source Project
|
||||||
|
license: The Apache Software License, Version 2.0
|
||||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||||
|
url: https://developer.android.com/topic/libraries/architecture/index.html
|
||||||
|
- artifact: androidx.customview:customview:+
|
||||||
|
name: Android Support Library Custom View
|
||||||
|
copyrightHolder: Android Open Source Project
|
||||||
|
license: The Apache Software License, Version 2.0
|
||||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||||
|
url: http://developer.android.com/tools/extras/support-library.html
|
||||||
|
- artifact: io.reactivex.rxjava2:rxandroid:+
|
||||||
|
name: RxAndroid
|
||||||
|
copyrightHolder: The RxAndroid authors
|
||||||
|
license: The Apache Software License, Version 2.0
|
||||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||||
|
url: https://github.com/ReactiveX/RxAndroid
|
||||||
|
- artifact: androidx.swiperefreshlayout:swiperefreshlayout:+
|
||||||
|
name: Android Support Library Custom View
|
||||||
|
copyrightHolder: Android Open Source Project
|
||||||
|
license: The Apache Software License, Version 2.0
|
||||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||||
|
url: http://developer.android.com/tools/extras/support-library.html
|
||||||
|
- artifact: androidx.documentfile:documentfile:+
|
||||||
|
name: Android Support Library Document File
|
||||||
|
copyrightHolder: Android Open Source Project
|
||||||
|
license: The Apache Software License, Version 2.0
|
||||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||||
|
url: http://developer.android.com/tools/extras/support-library.html
|
||||||
|
- artifact: androidx.lifecycle:lifecycle-extensions:+
|
||||||
|
name: Android Lifecycle Extensions
|
||||||
|
copyrightHolder: Android Open Source Project
|
||||||
|
license: The Apache Software License, Version 2.0
|
||||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||||
|
url: https://developer.android.com/topic/libraries/architecture/index.html
|
||||||
|
- artifact: androidx.legacy:legacy-support-core-utils:+
|
||||||
|
name: Android Support Library core utils
|
||||||
|
copyrightHolder: Android Open Source Project
|
||||||
|
license: The Apache Software License, Version 2.0
|
||||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||||
|
url: http://developer.android.com/tools/extras/support-library.html
|
||||||
|
- artifact: androidx.arch.core:core-runtime:+
|
||||||
|
name: Android Arch-Runtime
|
||||||
|
copyrightHolder: Android Open Source Project
|
||||||
|
license: The Apache Software License, Version 2.0
|
||||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||||
|
url: https://developer.android.com/topic/libraries/architecture/index.html
|
||||||
|
- artifact: org.apache.commons:commons-collections4:+
|
||||||
|
name: Apache Commons Collections
|
||||||
|
copyrightHolder: The Apache Software Foundation
|
||||||
|
license: The Apache Software License, Version 2.0
|
||||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||||
|
url: http://commons.apache.org/proper/commons-collections/
|
||||||
|
- artifact: org.mnode.ical4j:ical4j:+
|
||||||
|
name: ical4j
|
||||||
|
copyrightHolder: Ben Fortuna
|
||||||
|
license: BSD 3-Clause
|
||||||
|
licenseUrl: https://opensource.org/licenses/BSD-3-Clause
|
||||||
|
url: http://ical4j.github.io
|
||||||
|
forceGenerate: true
|
||||||
|
- artifact: androidx.recyclerview:recyclerview:+
|
||||||
|
name: Android Support RecyclerView v7
|
||||||
|
copyrightHolder: Android Open Source Project
|
||||||
|
license: The Apache Software License, Version 2.0
|
||||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||||
|
url: http://developer.android.com/tools/extras/support-library.html
|
||||||
|
- artifact: androidx.collection:collection:+
|
||||||
|
name: Android Support Library collections
|
||||||
|
copyrightHolder: Android Open Source Project
|
||||||
|
license: The Apache Software License, Version 2.0
|
||||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||||
|
url: http://developer.android.com/tools/extras/support-library.html
|
||||||
|
- artifact: com.fasterxml.jackson.core:jackson-core:+
|
||||||
|
name: Jackson-core
|
||||||
|
copyrightHolder: FasterXML
|
||||||
|
license: The Apache Software License, Version 2.0
|
||||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||||
|
url: https://github.com/FasterXML/jackson-core
|
||||||
|
- artifact: androidx.cardview:cardview:+
|
||||||
|
name: Android Support CardView v7
|
||||||
|
copyrightHolder: Android Open Source Project
|
||||||
|
license: The Apache Software License, Version 2.0
|
||||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||||
|
url: http://developer.android.com/tools/extras/support-library.html
|
||||||
|
- artifact: androidx.print:print:+
|
||||||
|
name: Android Support Library Print
|
||||||
|
copyrightHolder: Android Open Source Project
|
||||||
|
license: The Apache Software License, Version 2.0
|
||||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||||
|
url: http://developer.android.com/tools/extras/support-library.html
|
||||||
|
- artifact: com.rubiconproject.oss:jchronic:+
|
||||||
|
name: jchronic
|
||||||
|
copyrightHolder: The jchronic authors
|
||||||
|
license: MIT License
|
||||||
|
licenseUrl: http://www.opensource.org/licenses/mit-license.php
|
||||||
|
url: http://github.com/samtingleff/jchronic
|
||||||
|
- artifact: androidx.sqlite:sqlite-framework:+
|
||||||
|
name: Android Support SQLite - Framework Implementation
|
||||||
|
copyrightHolder: Android Open Source Project
|
||||||
|
license: The Apache Software License, Version 2.0
|
||||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||||
|
url: https://developer.android.com/topic/libraries/architecture/index.html
|
||||||
|
- artifact: com.google.android.material:material:+
|
||||||
|
name: Material Components for Android
|
||||||
|
copyrightHolder: Android Open Source Project
|
||||||
|
license: The Apache Software License, Version 2.0
|
||||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||||
|
url: http://developer.android.com/tools/extras/support-library.html
|
||||||
|
- artifact: androidx.localbroadcastmanager:localbroadcastmanager:+
|
||||||
|
name: Android Support Library Local Broadcast Manager
|
||||||
|
copyrightHolder: Android Open Source Project
|
||||||
|
license: The Apache Software License, Version 2.0
|
||||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||||
|
url: http://developer.android.com/tools/extras/support-library.html
|
||||||
|
- artifact: com.google.android.apps.dashclock:dashclock-api:+
|
||||||
|
name: DashClock API
|
||||||
|
copyrightHolder: Google Inc.
|
||||||
|
license: The Apache Software License, Version 2.0
|
||||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||||
|
url: http://dashclock.com/api
|
||||||
|
- artifact: androidx.vectordrawable:vectordrawable:+
|
||||||
|
name: Android Support VectorDrawable
|
||||||
|
copyrightHolder: Android Open Source Project
|
||||||
|
license: The Apache Software License, Version 2.0
|
||||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||||
|
url: http://developer.android.com/tools/extras/support-library.html
|
||||||
|
- artifact: org.reactivestreams:reactive-streams:+
|
||||||
|
name: reactive-streams
|
||||||
|
copyrightHolder: Public domain
|
||||||
|
license: CC0
|
||||||
|
licenseUrl: http://creativecommons.org/publicdomain/zero/1.0/
|
||||||
|
url: http://www.reactive-streams.org/
|
||||||
|
- artifact: androidx.work:work-runtime:+
|
||||||
|
name: Android WorkManager Runtime
|
||||||
|
copyrightHolder: Android Open Source Project
|
||||||
|
license: The Apache Software License, Version 2.0
|
||||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||||
|
url: https://developer.android.com/topic/libraries/architecture/index.html
|
||||||
|
- artifact: androidx.appcompat:appcompat:+
|
||||||
|
name: Android AppCompat Library v7
|
||||||
|
copyrightHolder: Android Open Source Project
|
||||||
|
license: The Apache Software License, Version 2.0
|
||||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||||
|
url: http://developer.android.com/tools/extras/support-library.html
|
||||||
|
- artifact: androidx.lifecycle:lifecycle-common:+
|
||||||
|
name: Android Lifecycle-Common
|
||||||
|
copyrightHolder: Android Open Source Project
|
||||||
|
license: The Apache Software License, Version 2.0
|
||||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||||
|
url: https://developer.android.com/topic/libraries/architecture/index.html
|
||||||
|
- artifact: com.mapbox.mapboxsdk:mapbox-sdk-core:+
|
||||||
|
name: Mapbox services-core
|
||||||
|
copyrightHolder: Mapbox
|
||||||
|
license: MIT License
|
||||||
|
licenseUrl: http://www.opensource.org/licenses/mit-license.php
|
||||||
|
url: https://github.com/mapbox/mapbox-java
|
||||||
|
forceGenerate: true
|
||||||
|
- artifact: com.mapbox.mapboxsdk:mapbox-android-gestures:+
|
||||||
|
name: Mapbox Android Gestures Library
|
||||||
|
copyrightHolder: Mapbox
|
||||||
|
license: BSD 2-Clause
|
||||||
|
licenseUrl: https://opensource.org/licenses/BSD-2-Clause
|
||||||
|
url: https://github.com/mapbox/mapbox-gestures-android
|
||||||
|
forceGenerate: true
|
||||||
|
- artifact: androidx.lifecycle:lifecycle-process:+
|
||||||
|
name: Android Lifecycle Process
|
||||||
|
copyrightHolder: Android Open Source Project
|
||||||
|
license: The Apache Software License, Version 2.0
|
||||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||||
|
url: https://developer.android.com/topic/libraries/architecture/index.html
|
||||||
|
- artifact: androidx.lifecycle:lifecycle-service:+
|
||||||
|
name: Android Lifecycle Service
|
||||||
|
copyrightHolder: Android Open Source Project
|
||||||
|
license: The Apache Software License, Version 2.0
|
||||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||||
|
url: https://developer.android.com/topic/libraries/architecture/index.html
|
||||||
|
- artifact: androidx.transition:transition:+
|
||||||
|
name: Android Transition Support Library
|
||||||
|
copyrightHolder: Android Open Source Project
|
||||||
|
license: The Apache Software License, Version 2.0
|
||||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||||
|
url: http://developer.android.com/tools/extras/support-library.html
|
||||||
|
- artifact: com.jakewharton.timber:timber:+
|
||||||
|
name: Timber
|
||||||
|
copyrightHolder: Jake Wharton
|
||||||
|
license: The Apache Software License, Version 2.0
|
||||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||||
|
url: https://github.com/JakeWharton/timber
|
||||||
|
- artifact: com.google.oauth-client:google-oauth-client:+
|
||||||
|
name: Google OAuth Client Library for Java
|
||||||
|
copyrightHolder: Google Inc.
|
||||||
|
license: The Apache Software License, Version 2.0
|
||||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||||
|
- artifact: androidx.room:room-rxjava2:+
|
||||||
|
name: Android Room RXJava2
|
||||||
|
copyrightHolder: Android Open Source Project
|
||||||
|
license: The Apache Software License, Version 2.0
|
||||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||||
|
url: https://developer.android.com/topic/libraries/architecture/index.html
|
||||||
|
- artifact: androidx.drawerlayout:drawerlayout:+
|
||||||
|
name: Android Support Library Drawer Layout
|
||||||
|
copyrightHolder: Android Open Source Project
|
||||||
|
license: The Apache Software License, Version 2.0
|
||||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||||
|
url: http://developer.android.com/tools/extras/support-library.html
|
||||||
|
- artifact: io.reactivex.rxjava2:rxjava:+
|
||||||
|
name: RxJava
|
||||||
|
copyrightHolder: RxJava Contributors
|
||||||
|
license: The Apache Software License, Version 2.0
|
||||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||||
|
url: https://github.com/ReactiveX/RxJava
|
||||||
|
- artifact: com.google.apis:google-api-services-tasks:+
|
||||||
|
name: Tasks API v1-rev55-1.25.0
|
||||||
|
copyrightHolder: Google Inc.
|
||||||
|
license: The Apache Software License, Version 2.0
|
||||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||||
|
- artifact: com.google.api-client:google-api-client:+
|
||||||
|
name: Google APIs Client Library for Java
|
||||||
|
copyrightHolder: Google Inc.
|
||||||
|
license: The Apache Software License, Version 2.0
|
||||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||||
|
- artifact: androidx.constraintlayout:constraintlayout-solver:+
|
||||||
|
name: Android ConstraintLayout Solver
|
||||||
|
copyrightHolder: Android Open Source Project
|
||||||
|
license: The Apache Software License, Version 2.0
|
||||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||||
|
url: http://tools.android.com
|
||||||
|
- artifact: org.jetbrains.kotlin:kotlin-stdlib:+
|
||||||
|
name: org.jetbrains.kotlin:kotlin-stdlib
|
||||||
|
copyrightHolder: JetBrains s.r.o.
|
||||||
|
license: The Apache Software License, Version 2.0
|
||||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||||
|
url: https://kotlinlang.org/
|
||||||
|
- artifact: com.google.http-client:google-http-client:+
|
||||||
|
name: Google HTTP Client Library for Java
|
||||||
|
copyrightHolder: Google Inc.
|
||||||
|
license: The Apache Software License, Version 2.0
|
||||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||||
|
- artifact: org.slf4j:slf4j-jdk14:+
|
||||||
|
name: SLF4J JDK14 Binding
|
||||||
|
copyrightHolder: QOS.ch
|
||||||
|
license: MIT License
|
||||||
|
licenseUrl: http://www.opensource.org/licenses/mit-license.php
|
||||||
|
url: http://www.slf4j.org
|
||||||
|
- artifact: com.squareup.okhttp3:okhttp:+
|
||||||
|
name: OkHttp
|
||||||
|
copyrightHolder: Square, Inc.
|
||||||
|
license: The Apache Software License, Version 2.0
|
||||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||||
|
- artifact: com.squareup.retrofit2:retrofit:+
|
||||||
|
name: Retrofit
|
||||||
|
copyrightHolder: Square, Inc.
|
||||||
|
license: The Apache Software License, Version 2.0
|
||||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||||
|
- artifact: org.slf4j:slf4j-api:+
|
||||||
|
name: SLF4J API Module
|
||||||
|
copyrightHolder: QOS.ch
|
||||||
|
license: MIT License
|
||||||
|
licenseUrl: http://www.opensource.org/licenses/mit-license.php
|
||||||
|
url: http://www.slf4j.org
|
||||||
|
- artifact: org.jetbrains.kotlin:kotlin-stdlib-common:+
|
||||||
|
name: org.jetbrains.kotlin:kotlin-stdlib-common
|
||||||
|
copyrightHolder: JetBrains s.r.o.
|
||||||
|
license: The Apache Software License, Version 2.0
|
||||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||||
|
url: https://kotlinlang.org/
|
||||||
|
- artifact: com.google.http-client:google-http-client-jackson2:+
|
||||||
|
name: Jackson 2 extensions to the Google HTTP Client Library for Java.
|
||||||
|
copyrightHolder: Google Inc.
|
||||||
|
license: The Apache Software License, Version 2.0
|
||||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||||
|
- artifact: com.jakewharton:butterknife:+
|
||||||
|
name: ButterKnife
|
||||||
|
copyrightHolder: Jake Wharton
|
||||||
|
license: The Apache Software License, Version 2.0
|
||||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||||
|
url: https://github.com/JakeWharton/butterknife/
|
||||||
|
- artifact: com.jakewharton:butterknife-annotations:+
|
||||||
|
name: ButterKnife Annotations
|
||||||
|
copyrightHolder: Jake Wharton
|
||||||
|
license: The Apache Software License, Version 2.0
|
||||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||||
|
url: https://github.com/JakeWharton/butterknife/
|
||||||
|
- artifact: com.squareup.retrofit2:converter-gson:+
|
||||||
|
name: "Converter: Gson"
|
||||||
|
copyrightHolder: Square, Inc.
|
||||||
|
license: The Apache Software License, Version 2.0
|
||||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||||
|
- artifact: com.squareup.okhttp3:logging-interceptor:+
|
||||||
|
name: OkHttp Logging Interceptor
|
||||||
|
copyrightHolder: Square, Inc.
|
||||||
|
license: The Apache Software License, Version 2.0
|
||||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||||
|
- artifact: org.jetbrains.kotlin:kotlin-stdlib-jdk7:+
|
||||||
|
name: org.jetbrains.kotlin:kotlin-stdlib-jdk7
|
||||||
|
copyrightHolder: JetBrains s.r.o.
|
||||||
|
license: The Apache Software License, Version 2.0
|
||||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||||
|
url: https://kotlinlang.org/
|
||||||
|
- artifact: com.jakewharton:butterknife-runtime:+
|
||||||
|
name: ButterKnife Runtime
|
||||||
|
copyrightHolder: Jake Wharton
|
||||||
|
license: The Apache Software License, Version 2.0
|
||||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||||
|
url: https://github.com/JakeWharton/butterknife/
|
||||||
|
- artifact: io.grpc:grpc-context:+
|
||||||
|
name: io.grpc:grpc-context
|
||||||
|
copyrightHolder: The gRPC Authors
|
||||||
|
license: The Apache Software License, Version 2.0
|
||||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||||
|
url: https://github.com/grpc/grpc-java
|
||||||
|
- artifact: com.google.guava:listenablefuture:+
|
||||||
|
name: Guava ListenableFuture only
|
||||||
|
copyrightHolder: The Guava Authors
|
||||||
|
license: The Apache Software License, Version 2.0
|
||||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||||
|
- artifact: io.opencensus:opencensus-api:+
|
||||||
|
name: OpenCensus API
|
||||||
|
copyrightHolder: OpenCensus Authors
|
||||||
|
license: The Apache Software License, Version 2.0
|
||||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||||
|
url: https://github.com/census-instrumentation/opencensus-java
|
||||||
|
- artifact: com.google.guava:failureaccess:+
|
||||||
|
name: Guava InternalFutureFailureAccess and InternalFutures
|
||||||
|
copyrightHolder: The Guava Authors
|
||||||
|
license: The Apache Software License, Version 2.0
|
||||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||||
|
- artifact: io.opencensus:opencensus-contrib-http-util:+
|
||||||
|
name: OpenCensus contrib-http-util
|
||||||
|
copyrightHolder: OpenCensus Authors
|
||||||
|
license: The Apache Software License, Version 2.0
|
||||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||||
|
url: https://github.com/census-instrumentation/opencensus-java
|
||||||
|
- artifact: androidx.core:core-ktx:+
|
||||||
|
name: Core Kotlin Extensions
|
||||||
|
copyrightHolder: Android Open Source Project
|
||||||
|
license: The Apache Software License, Version 2.0
|
||||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||||
|
url: http://developer.android.com/tools/extras/support-library.html
|
||||||
|
- artifact: androidx.appcompat:appcompat-resources:+
|
||||||
|
name: Android Resources Library
|
||||||
|
copyrightHolder: Android Open Source Project
|
||||||
|
license: The Apache Software License, Version 2.0
|
||||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||||
|
url: http://developer.android.com/tools/extras/support-library.html
|
||||||
|
- artifact: androidx.viewpager2:viewpager2:+
|
||||||
|
name: AndroidX Widget ViewPager2
|
||||||
|
copyrightHolder: Android Open Source Project
|
||||||
|
license: The Apache Software License, Version 2.0
|
||||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||||
|
url: http://developer.android.com/tools/extras/support-library.html
|
||||||
|
- artifact: androidx.savedstate:savedstate:+
|
||||||
|
name: Activity
|
||||||
|
copyrightHolder: Android Open Source Project
|
||||||
|
license: The Apache Software License, Version 2.0
|
||||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||||
|
url: http://developer.android.com/tools/extras/support-library.html
|
||||||
|
- artifact: androidx.activity:activity:+
|
||||||
|
name: Activity
|
||||||
|
copyrightHolder: Android Open Source Project
|
||||||
|
license: The Apache Software License, Version 2.0
|
||||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||||
|
url: http://developer.android.com/tools/extras/support-library.html
|
||||||
|
- artifact: androidx.paging:paging-runtime:+
|
||||||
|
name: Android Paging-Runtime
|
||||||
|
copyrightHolder: Android Open Source Project
|
||||||
|
license: The Apache Software License, Version 2.0
|
||||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||||
|
url: https://developer.android.com/topic/libraries/architecture/index.html
|
||||||
|
- artifact: androidx.paging:paging-common:+
|
||||||
|
name: Android Paging-Common
|
||||||
|
copyrightHolder: Android Open Source Project
|
||||||
|
license: The Apache Software License, Version 2.0
|
||||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||||
|
url: https://developer.android.com/topic/libraries/architecture/index.html
|
||||||
|
- artifact: org.jetbrains.kotlinx:kotlinx-coroutines-core-common:+
|
||||||
|
name: kotlinx-coroutines-core-common
|
||||||
|
copyrightHolder: JetBrains s.r.o.
|
||||||
|
license: The Apache Software License, Version 2.0
|
||||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||||
|
url: https://github.com/Kotlin/kotlinx.coroutines
|
||||||
|
- artifact: org.conscrypt:conscrypt-android:+
|
||||||
|
name: org.conscrypt:conscrypt-android
|
||||||
|
copyrightHolder: Android Open Source Project
|
||||||
|
license: The Apache Software License, Version 2.0
|
||||||
|
licenseUrl: https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
url: https://conscrypt.org/
|
||||||
|
- artifact: org.jetbrains.kotlin:kotlin-stdlib-jdk8:+
|
||||||
|
name: org.jetbrains.kotlin:kotlin-stdlib-jdk8
|
||||||
|
copyrightHolder: JetBrains s.r.o.
|
||||||
|
license: The Apache Software License, Version 2.0
|
||||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||||
|
url: https://kotlinlang.org/
|
||||||
|
- artifact: org.jetbrains.kotlinx:kotlinx-coroutines-android:+
|
||||||
|
name: kotlinx-coroutines-android
|
||||||
|
copyrightHolder: JetBrains s.r.o.
|
||||||
|
license: The Apache Software License, Version 2.0
|
||||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||||
|
url: https://github.com/Kotlin/kotlinx.coroutines
|
||||||
|
- artifact: androidx.databinding:databinding-adapters:+
|
||||||
|
name: databinding-adapters
|
||||||
|
copyrightHolder: Android Open Source Project
|
||||||
|
license: The Apache Software License, Version 2.0
|
||||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||||
|
- artifact: androidx.lifecycle:lifecycle-viewmodel-ktx:+
|
||||||
|
name: Android Lifecycle ViewModel Kotlin Extensions
|
||||||
|
copyrightHolder: Android Open Source Project
|
||||||
|
license: The Apache Software License, Version 2.0
|
||||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||||
|
url: http://developer.android.com/tools/extras/support-library.html
|
||||||
|
- artifact: androidx.annotation:annotation-experimental:+
|
||||||
|
name: Experimental annotation
|
||||||
|
copyrightHolder: Android Open Source Project
|
||||||
|
license: The Apache Software License, Version 2.0
|
||||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||||
|
url: https://developer.android.com/jetpack/androidx
|
||||||
|
- artifact: org.jetbrains.kotlinx:kotlinx-coroutines-core:+
|
||||||
|
name: kotlinx-coroutines-core
|
||||||
|
copyrightHolder: JetBrains s.r.o.
|
||||||
|
license: The Apache Software License, Version 2.0
|
||||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||||
|
url: https://github.com/Kotlin/kotlinx.coroutines
|
||||||
|
- artifact: androidx.databinding:databinding-common:+
|
||||||
|
name: Data Binding Base Library
|
||||||
|
copyrightHolder: Android Open Source Project
|
||||||
|
license: The Apache Software License, Version 2.0
|
||||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||||
|
url: https://developer.android.com/studio
|
||||||
|
- artifact: androidx.databinding:databinding-runtime:+
|
||||||
|
name: databinding-runtime
|
||||||
|
copyrightHolder: Android Open Source Project
|
||||||
|
license: The Apache Software License, Version 2.0
|
||||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||||
|
- artifact: org.apache.httpcomponents:httpcore:+
|
||||||
|
name: Apache HttpCore
|
||||||
|
copyrightHolder: The Apache Software Foundation
|
||||||
|
license: The Apache Software License, Version 2.0
|
||||||
|
url: http://hc.apache.org/httpcomponents-core-ga
|
||||||
|
- artifact: androidx.databinding:viewbinding:+
|
||||||
|
name: viewbinding
|
||||||
|
copyrightHolder: Android Open Source Project
|
||||||
|
license: The Apache Software License, Version 2.0
|
||||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||||
|
- artifact: com.madgag.spongycastle:core:+
|
||||||
|
name: Spongy Castle Core
|
||||||
|
copyrightHolder: The Legion of the Bouncy Castle Inc.
|
||||||
|
license: Bouncy Castle Licence
|
||||||
|
licenseUrl: http://www.bouncycastle.org/licence.html
|
||||||
|
url: http://rtyley.github.io/spongycastle/
|
||||||
|
- artifact: com.etesync:journalmanager:+
|
||||||
|
name: EteSync JVM
|
||||||
|
copyrightHolder: Tom Hacohen
|
||||||
|
license: LGPL-3.0-only
|
||||||
|
licenseUrl: https://spdx.org/licenses/LGPL-3.0-only.html
|
||||||
|
url: https://www.etesync.com
|
||||||
|
- artifact: com.madgag.spongycastle:prov:+
|
||||||
|
name: Spongy Castle
|
||||||
|
copyrightHolder: The Legion of the Bouncy Castle Inc.
|
||||||
|
license: Bouncy Castle Licence
|
||||||
|
licenseUrl: http://www.bouncycastle.org/licence.html
|
||||||
|
url: http://rtyley.github.io/spongycastle/
|
||||||
|
- artifact: androidx.preference:preference:+
|
||||||
|
name: AndroidX Preference
|
||||||
|
copyrightHolder: Android Open Source Project
|
||||||
|
license: The Apache Software License, Version 2.0
|
||||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||||
|
url: https://developer.android.com/jetpack/androidx
|
||||||
|
- artifact: androidx.lifecycle:lifecycle-viewmodel-savedstate:+
|
||||||
|
name: Android Lifecycle ViewModel with SavedState
|
||||||
|
copyrightHolder: Android Open Source Project
|
||||||
|
license: The Apache Software License, Version 2.0
|
||||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||||
|
url: https://developer.android.com/topic/libraries/architecture/index.html
|
||||||
|
- artifact: com.github.QuadFlask:colorpicker:+
|
||||||
|
name: QuadFlask/colorpicker
|
||||||
|
copyrightHolder: QuadFlask
|
||||||
|
license: The Apache Software License, Version 2.0
|
||||||
|
url: https://github.com/QuadFlask/colorpicker
|
||||||
|
- artifact: com.google.auth:google-auth-library-credentials:+
|
||||||
|
name: Google Auth Library for Java - Credentials
|
||||||
|
copyrightHolder: Google Inc.
|
||||||
|
license: BSD 3-Clause
|
||||||
|
- artifact: com.google.auth:google-auth-library-oauth2-http:+
|
||||||
|
name: Google Auth Library for Java - OAuth2 HTTP
|
||||||
|
copyrightHolder: Google Inc.
|
||||||
|
license: BSD 3-Clause
|
||||||
|
- artifact: com.google.auto.value:auto-value-annotations:+
|
||||||
|
name: AutoValue Annotations
|
||||||
|
copyrightHolder: Google LLC
|
||||||
|
license: The Apache Software License, Version 2.0
|
||||||
|
url: https://github.com/google/auto/tree/master/value
|
||||||
|
- artifact: com.mapbox.mapboxsdk:mapbox-sdk-directions-refresh-models:+
|
||||||
|
name: mapbox-sdk-directions-refresh-models
|
||||||
|
copyrightHolder: Mapbox
|
||||||
|
license: The Apache Software License, Version 2.0
|
||||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||||
|
url: https://github.com/mapbox/mapbox-java
|
||||||
|
- artifact: com.mapbox.mapboxsdk:mapbox-sdk-directions-models:+
|
||||||
|
name: mapbox-sdk-directions-models
|
||||||
|
copyrightHolder: Mapbox
|
||||||
|
license: The Apache Software License, Version 2.0
|
||||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||||
|
url: https://github.com/mapbox/mapbox-java
|
||||||
|
- artifact: com.mapbox.mapboxsdk:mapbox-android-accounts:+
|
||||||
|
name: mapbox-android-accounts
|
||||||
|
copyrightHolder: Mapbox
|
||||||
|
license: Mapbox Terms of Service
|
||||||
|
licenseUrl: https://www.mapbox.com/tos/
|
||||||
|
url: https://github.com/mapbox/mapbox-accounts-android
|
||||||
|
- artifact: com.sun.mail:android-mail:+
|
||||||
|
name: android-mail
|
||||||
|
copyrightHolder: Oracle and/or its affiliates
|
||||||
|
license: Eclipse Public License, Version 2.0
|
||||||
|
- artifact: commons-io:commons-io:+
|
||||||
|
name: commons-io
|
||||||
|
copyrightHolder: The Apache Software Foundation
|
||||||
|
license: The Apache Software License, Version 2.0
|
||||||
|
url: http://commons.apache.org/proper/commons-io/
|
||||||
|
- artifact: com.sun.mail:android-activation:+
|
||||||
|
name: android-activation
|
||||||
|
copyrightHolder: Oracle and/or its affiliates
|
||||||
|
license: Eclipse Public License, Version 2.0
|
||||||
@ -1,32 +0,0 @@
|
|||||||
package com.todoroo.astrid.activity
|
|
||||||
|
|
||||||
import android.content.Intent
|
|
||||||
import android.content.Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY
|
|
||||||
import android.content.Intent.FLAG_ACTIVITY_NEW_TASK
|
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
|
||||||
import org.junit.Assert.assertFalse
|
|
||||||
import org.junit.Assert.assertTrue
|
|
||||||
import org.junit.Test
|
|
||||||
import org.junit.runner.RunWith
|
|
||||||
import org.tasks.extensions.isFromHistory
|
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4::class)
|
|
||||||
class MainActivityTest {
|
|
||||||
@Test
|
|
||||||
fun newTaskIsNotFromHistory() {
|
|
||||||
assertFalse(Intent().setFlags(FLAG_ACTIVITY_NEW_TASK).isFromHistory)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun oldTaskIsNotFromHistory() {
|
|
||||||
assertFalse(Intent().setFlags(FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY).isFromHistory)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun newTaskIsFromHistory() {
|
|
||||||
assertTrue(
|
|
||||||
Intent()
|
|
||||||
.setFlags(FLAG_ACTIVITY_NEW_TASK or FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY)
|
|
||||||
.isFromHistory)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -0,0 +1,79 @@
|
|||||||
|
package com.todoroo.astrid.adapter
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import androidx.test.core.app.ApplicationProvider
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
|
import com.natpryce.makeiteasy.MakeItEasy.with
|
||||||
|
import com.natpryce.makeiteasy.PropertyValue
|
||||||
|
import com.todoroo.astrid.core.BuiltInFilterExposer
|
||||||
|
import com.todoroo.astrid.dao.TaskDao
|
||||||
|
import com.todoroo.astrid.data.Task
|
||||||
|
import org.junit.Assert.assertEquals
|
||||||
|
import org.junit.Before
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
import org.tasks.LocalBroadcastManager
|
||||||
|
import org.tasks.R
|
||||||
|
import org.tasks.data.CaldavDao
|
||||||
|
import org.tasks.data.GoogleTaskDao
|
||||||
|
import org.tasks.data.TaskContainer
|
||||||
|
import org.tasks.data.TaskListQuery.getQuery
|
||||||
|
import org.tasks.injection.InjectingTestCase
|
||||||
|
import org.tasks.injection.TestComponent
|
||||||
|
import org.tasks.makers.TaskMaker.PARENT
|
||||||
|
import org.tasks.makers.TaskMaker.newTask
|
||||||
|
import org.tasks.preferences.Preferences
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@RunWith(AndroidJUnit4::class)
|
||||||
|
class NonRecursiveQueryTest : InjectingTestCase() {
|
||||||
|
|
||||||
|
@Inject lateinit var googleTaskDao: GoogleTaskDao
|
||||||
|
@Inject lateinit var caldavDao: CaldavDao
|
||||||
|
@Inject lateinit var taskDao: TaskDao
|
||||||
|
@Inject lateinit var preferences: Preferences
|
||||||
|
@Inject lateinit var localBroadcastManager: LocalBroadcastManager
|
||||||
|
|
||||||
|
private lateinit var adapter: TaskAdapter
|
||||||
|
private val tasks = ArrayList<TaskContainer>()
|
||||||
|
private val filter = BuiltInFilterExposer.getMyTasksFilter(ApplicationProvider.getApplicationContext<Context>().resources)
|
||||||
|
private val dataSource = object : TaskAdapterDataSource {
|
||||||
|
override fun getItem(position: Int) = tasks[position]
|
||||||
|
|
||||||
|
override fun getTaskCount() = tasks.size
|
||||||
|
}
|
||||||
|
|
||||||
|
@Before
|
||||||
|
override fun setUp() {
|
||||||
|
super.setUp()
|
||||||
|
preferences.clear()
|
||||||
|
preferences.setBoolean(R.string.p_use_paged_queries, true)
|
||||||
|
tasks.clear()
|
||||||
|
adapter = TaskAdapter(false, googleTaskDao, caldavDao, taskDao, localBroadcastManager)
|
||||||
|
adapter.setDataSource(dataSource)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun ignoreSubtasks() {
|
||||||
|
val parent = addTask()
|
||||||
|
val child = addTask(with(PARENT, parent))
|
||||||
|
|
||||||
|
query()
|
||||||
|
|
||||||
|
assertEquals(child, tasks[1].id)
|
||||||
|
assertEquals(parent, tasks[1].parent)
|
||||||
|
assertEquals(0, tasks[1].indent)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun addTask(vararg properties: PropertyValue<in Task?, *>): Long {
|
||||||
|
val task = newTask(*properties)
|
||||||
|
taskDao.createNew(task)
|
||||||
|
return task.id
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun query() {
|
||||||
|
tasks.addAll(taskDao.fetchTasks { getQuery(preferences, filter, it) })
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun inject(component: TestComponent) = component.inject(this)
|
||||||
|
}
|
||||||
@ -1,91 +0,0 @@
|
|||||||
package com.todoroo.astrid.adapter
|
|
||||||
|
|
||||||
import com.natpryce.makeiteasy.MakeItEasy.with
|
|
||||||
import com.natpryce.makeiteasy.PropertyValue
|
|
||||||
import com.todoroo.astrid.dao.TaskDao
|
|
||||||
import dagger.hilt.android.testing.HiltAndroidTest
|
|
||||||
import kotlinx.coroutines.runBlocking
|
|
||||||
import org.junit.Assert.assertEquals
|
|
||||||
import org.junit.Before
|
|
||||||
import org.junit.Test
|
|
||||||
import org.tasks.data.TaskListQuery.getQuery
|
|
||||||
import org.tasks.data.entity.Task
|
|
||||||
import org.tasks.date.DateTimeUtils.newDateTime
|
|
||||||
import org.tasks.filters.TodayFilter
|
|
||||||
import org.tasks.injection.InjectingTestCase
|
|
||||||
import org.tasks.makers.TaskMaker.DUE_DATE
|
|
||||||
import org.tasks.makers.TaskMaker.PARENT
|
|
||||||
import org.tasks.makers.TaskMaker.newTask
|
|
||||||
import org.tasks.preferences.Preferences
|
|
||||||
import javax.inject.Inject
|
|
||||||
|
|
||||||
@HiltAndroidTest
|
|
||||||
class RecursiveLoopTest : InjectingTestCase() {
|
|
||||||
@Inject lateinit var taskDao: TaskDao
|
|
||||||
@Inject lateinit var preferences: Preferences
|
|
||||||
|
|
||||||
@Before
|
|
||||||
override fun setUp() {
|
|
||||||
super.setUp()
|
|
||||||
preferences.clear()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun handleSelfLoop() = runBlocking {
|
|
||||||
addTask(with(DUE_DATE, newDateTime()), with(PARENT, 1L))
|
|
||||||
|
|
||||||
val tasks = getTasks()
|
|
||||||
|
|
||||||
assertEquals(1, tasks.size)
|
|
||||||
assertEquals(1L, tasks[0].id)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun handleSingleLevelLoop() = runBlocking {
|
|
||||||
val parent = addTask(with(DUE_DATE, newDateTime()))
|
|
||||||
val child = addTask(with(PARENT, parent))
|
|
||||||
|
|
||||||
taskDao.setParent(child, listOf(parent))
|
|
||||||
|
|
||||||
val tasks = getTasks()
|
|
||||||
assertEquals(2, tasks.size)
|
|
||||||
assertEquals(parent, tasks[0].id)
|
|
||||||
assertEquals(child, tasks[1].id)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun handleMultiLevelLoop() = runBlocking {
|
|
||||||
val parent = addTask(with(DUE_DATE, newDateTime()))
|
|
||||||
val child = addTask(with(PARENT, parent))
|
|
||||||
val grandchild = addTask(with(PARENT, child))
|
|
||||||
|
|
||||||
taskDao.setParent(grandchild, listOf(parent))
|
|
||||||
|
|
||||||
val tasks = getTasks()
|
|
||||||
assertEquals(3, tasks.size)
|
|
||||||
assertEquals(parent, tasks[0].id)
|
|
||||||
assertEquals(child, tasks[1].id)
|
|
||||||
assertEquals(grandchild, tasks[2].id)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun descendantsRecursiveLoopBothMatchFilter() = runBlocking {
|
|
||||||
val parent = addTask(with(DUE_DATE, newDateTime()))
|
|
||||||
val child = addTask(with(DUE_DATE, newDateTime()), with(PARENT, parent))
|
|
||||||
|
|
||||||
taskDao.setParent(child, listOf(parent))
|
|
||||||
|
|
||||||
val tasks = getTasks()
|
|
||||||
assertEquals(2, tasks.size)
|
|
||||||
}
|
|
||||||
|
|
||||||
private suspend fun getTasks() = taskDao.fetchTasks(
|
|
||||||
getQuery(preferences, TodayFilter.create())
|
|
||||||
)
|
|
||||||
|
|
||||||
private suspend fun addTask(vararg properties: PropertyValue<in Task?, *>): Long {
|
|
||||||
val task = newTask(*properties)
|
|
||||||
taskDao.createNew(task)
|
|
||||||
return task.id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,287 +1,52 @@
|
|||||||
package com.todoroo.astrid.alarms
|
package com.todoroo.astrid.alarms
|
||||||
|
|
||||||
import dagger.hilt.android.testing.HiltAndroidTest
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
import kotlinx.coroutines.runBlocking
|
import com.natpryce.makeiteasy.MakeItEasy.with
|
||||||
|
import com.todoroo.astrid.dao.TaskDao
|
||||||
import org.junit.Assert.assertEquals
|
import org.junit.Assert.assertEquals
|
||||||
|
import org.junit.Assert.assertTrue
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.tasks.SuspendFreeze.Companion.freezeAt
|
import org.junit.runner.RunWith
|
||||||
import org.tasks.data.createDueDate
|
import org.tasks.data.Alarm
|
||||||
import org.tasks.data.dao.TaskDao
|
import org.tasks.data.AlarmDao
|
||||||
import org.tasks.data.entity.Alarm
|
|
||||||
import org.tasks.data.entity.Notification
|
|
||||||
import org.tasks.data.entity.Task
|
|
||||||
import org.tasks.injection.InjectingTestCase
|
import org.tasks.injection.InjectingTestCase
|
||||||
|
import org.tasks.injection.TestComponent
|
||||||
|
import org.tasks.jobs.AlarmEntry
|
||||||
|
import org.tasks.jobs.NotificationQueue
|
||||||
|
import org.tasks.makers.TaskMaker.REMINDER_LAST
|
||||||
|
import org.tasks.makers.TaskMaker.newTask
|
||||||
import org.tasks.time.DateTime
|
import org.tasks.time.DateTime
|
||||||
import org.tasks.time.DateTimeUtils2
|
|
||||||
import java.util.concurrent.TimeUnit
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@HiltAndroidTest
|
@RunWith(AndroidJUnit4::class)
|
||||||
class AlarmJobServiceTest : InjectingTestCase() {
|
class AlarmJobServiceTest : InjectingTestCase() {
|
||||||
|
@Inject lateinit var alarmDao: AlarmDao
|
||||||
@Inject lateinit var taskDao: TaskDao
|
@Inject lateinit var taskDao: TaskDao
|
||||||
|
@Inject lateinit var jobs: NotificationQueue
|
||||||
@Inject lateinit var alarmService: AlarmService
|
@Inject lateinit var alarmService: AlarmService
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testNoAlarms() = runBlocking {
|
fun scheduleAlarm() {
|
||||||
testResults(emptyList(), 0)
|
val task = newTask()
|
||||||
}
|
taskDao.createNew(task)
|
||||||
|
val alarmTime = DateTime(2017, 9, 24, 19, 57)
|
||||||
@Test
|
val alarm = Alarm(task.id, alarmTime.millis)
|
||||||
fun futureAlarmWithNoPastAlarm() = runBlocking {
|
alarm.id = alarmDao.insert(alarm)
|
||||||
freezeAt(DateTime(2024, 5, 17, 23, 20)) {
|
alarmService.scheduleAllAlarms()
|
||||||
taskDao.insert(
|
|
||||||
Task(
|
|
||||||
dueDate = createDueDate(
|
|
||||||
Task.URGENCY_SPECIFIC_DAY,
|
|
||||||
DateTime(2024, 5, 18).millis
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
alarmService.synchronizeAlarms(1, mutableSetOf(Alarm(type = Alarm.TYPE_REL_END)))
|
|
||||||
|
|
||||||
testResults(emptyList(), DateTime(2024, 5, 18, 18, 0).millis)
|
assertEquals(listOf(AlarmEntry(alarm)), jobs.getJobs())
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun pastAlarmWithNoFutureAlarm() = runBlocking {
|
fun ignoreStaleAlarm() {
|
||||||
freezeAt(DateTime(2024, 5, 17, 23, 20)) {
|
val alarmTime = DateTime(2017, 9, 24, 19, 57)
|
||||||
taskDao.insert(
|
val task = newTask(with(REMINDER_LAST, alarmTime.endOfMinute()))
|
||||||
Task(
|
taskDao.createNew(task)
|
||||||
dueDate = createDueDate(
|
alarmDao.insert(Alarm(task.id, alarmTime.millis))
|
||||||
Task.URGENCY_SPECIFIC_DAY,
|
alarmService.scheduleAllAlarms()
|
||||||
DateTime(2024, 5, 17).millis
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
alarmService.synchronizeAlarms(1, mutableSetOf(Alarm(type = Alarm.TYPE_REL_END)))
|
|
||||||
|
|
||||||
testResults(
|
assertTrue(jobs.getJobs().isEmpty())
|
||||||
listOf(
|
|
||||||
Notification(
|
|
||||||
taskId = 1L,
|
|
||||||
timestamp = DateTimeUtils2.currentTimeMillis(),
|
|
||||||
type = Alarm.TYPE_REL_END
|
|
||||||
)
|
|
||||||
),
|
|
||||||
0
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
override fun inject(component: TestComponent) = component.inject(this)
|
||||||
fun pastRecurringAlarmWithFutureRecurrence() = runBlocking {
|
|
||||||
freezeAt(DateTime(2024, 5, 17, 23, 20)) {
|
|
||||||
taskDao.insert(
|
|
||||||
Task(
|
|
||||||
dueDate = createDueDate(
|
|
||||||
Task.URGENCY_SPECIFIC_DAY,
|
|
||||||
DateTime(2024, 5, 17).millis
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
alarmService.synchronizeAlarms(
|
|
||||||
1,
|
|
||||||
mutableSetOf(
|
|
||||||
Alarm(
|
|
||||||
type = Alarm.TYPE_REL_END,
|
|
||||||
repeat = 1,
|
|
||||||
interval = TimeUnit.HOURS.toMillis(6)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
testResults(
|
|
||||||
listOf(
|
|
||||||
Notification(
|
|
||||||
taskId = 1L,
|
|
||||||
timestamp = DateTimeUtils2.currentTimeMillis(),
|
|
||||||
type = Alarm.TYPE_REL_END
|
|
||||||
)
|
|
||||||
),
|
|
||||||
DateTime(2024, 5, 18, 0, 0).millis
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun pastAlarmsRemoveSnoozed() = runBlocking {
|
|
||||||
freezeAt(DateTime(2024, 5, 17, 23, 20)) {
|
|
||||||
taskDao.insert(
|
|
||||||
Task(
|
|
||||||
dueDate = createDueDate(
|
|
||||||
Task.URGENCY_SPECIFIC_DAY,
|
|
||||||
DateTime(2024, 5, 17).millis
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
alarmService.synchronizeAlarms(
|
|
||||||
1,
|
|
||||||
mutableSetOf(
|
|
||||||
Alarm(type = Alarm.TYPE_REL_END),
|
|
||||||
Alarm(time = DateTimeUtils2.currentTimeMillis(), type = Alarm.TYPE_SNOOZE)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
testResults(
|
|
||||||
listOf(
|
|
||||||
Notification(
|
|
||||||
taskId = 1L,
|
|
||||||
timestamp = DateTimeUtils2.currentTimeMillis(),
|
|
||||||
type = Alarm.TYPE_REL_END
|
|
||||||
)
|
|
||||||
),
|
|
||||||
0
|
|
||||||
)
|
|
||||||
|
|
||||||
assertEquals(
|
|
||||||
listOf(Alarm(id = 1, task = 1, time = 0, type = Alarm.TYPE_REL_END)),
|
|
||||||
alarmService.getAlarms(1)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun alarmsOneMinuteApart() = runBlocking {
|
|
||||||
freezeAt(DateTime(2024, 5, 17, 23, 20)) {
|
|
||||||
taskDao.insert(
|
|
||||||
Task(
|
|
||||||
dueDate = createDueDate(
|
|
||||||
Task.URGENCY_SPECIFIC_DAY_TIME,
|
|
||||||
DateTime(2024, 5, 17, 23, 20).millis
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
alarmService.synchronizeAlarms(1, mutableSetOf(Alarm(type = Alarm.TYPE_REL_END)))
|
|
||||||
taskDao.insert(Task())
|
|
||||||
alarmService.synchronizeAlarms(
|
|
||||||
taskId = 2,
|
|
||||||
alarms = mutableSetOf(
|
|
||||||
Alarm(
|
|
||||||
type = Alarm.TYPE_SNOOZE,
|
|
||||||
time = DateTime(2024, 5, 17, 23, 21).millis)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
testResults(
|
|
||||||
listOf(
|
|
||||||
Notification(
|
|
||||||
taskId = 1L,
|
|
||||||
timestamp = DateTimeUtils2.currentTimeMillis(),
|
|
||||||
type = Alarm.TYPE_REL_END
|
|
||||||
)
|
|
||||||
),
|
|
||||||
DateTime(2024, 5, 17, 23, 21).millis
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun futureSnoozeOverrideOverdue() = runBlocking {
|
|
||||||
freezeAt(DateTime(2024, 5, 17, 23, 20)) {
|
|
||||||
taskDao.insert(
|
|
||||||
Task(
|
|
||||||
dueDate = createDueDate(
|
|
||||||
Task.URGENCY_SPECIFIC_DAY,
|
|
||||||
DateTime(2024, 5, 17).millis
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
alarmService.synchronizeAlarms(
|
|
||||||
1,
|
|
||||||
mutableSetOf(
|
|
||||||
Alarm(type = Alarm.TYPE_REL_END),
|
|
||||||
Alarm(
|
|
||||||
time = DateTimeUtils2.currentTimeMillis() + TimeUnit.MINUTES.toMillis(5),
|
|
||||||
type = Alarm.TYPE_SNOOZE
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
testResults(
|
|
||||||
emptyList(),
|
|
||||||
DateTimeUtils2.currentTimeMillis() + TimeUnit.MINUTES.toMillis(5)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun ignoreStaleAlarm() = runBlocking {
|
|
||||||
freezeAt(DateTime(2024, 5, 17, 23, 20)) {
|
|
||||||
taskDao.insert(
|
|
||||||
Task(
|
|
||||||
dueDate = createDueDate(
|
|
||||||
Task.URGENCY_SPECIFIC_DAY,
|
|
||||||
DateTime(2024, 5, 17).millis
|
|
||||||
),
|
|
||||||
reminderLast = DateTime(2024, 5, 17, 18, 0).millis,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
alarmService.synchronizeAlarms(
|
|
||||||
1,
|
|
||||||
mutableSetOf(Alarm(type = Alarm.TYPE_REL_END))
|
|
||||||
)
|
|
||||||
|
|
||||||
testResults(
|
|
||||||
emptyList(),
|
|
||||||
0
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun dontScheduleForCompletedTask() = runBlocking {
|
|
||||||
freezeAt(DateTime(2024, 5, 17, 23, 20)) {
|
|
||||||
taskDao.insert(
|
|
||||||
Task(
|
|
||||||
dueDate = createDueDate(
|
|
||||||
Task.URGENCY_SPECIFIC_DAY,
|
|
||||||
DateTime(2024, 5, 17).millis
|
|
||||||
),
|
|
||||||
completionDate = DateTime(2024, 5, 17, 14, 0).millis,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
alarmService.synchronizeAlarms(
|
|
||||||
1,
|
|
||||||
mutableSetOf(Alarm(type = Alarm.TYPE_REL_END))
|
|
||||||
)
|
|
||||||
|
|
||||||
testResults(
|
|
||||||
emptyList(),
|
|
||||||
0
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun dontScheduleForDeletedTask() = runBlocking {
|
|
||||||
freezeAt(DateTime(2024, 5, 17, 23, 20)) {
|
|
||||||
taskDao.insert(
|
|
||||||
Task(
|
|
||||||
dueDate = createDueDate(
|
|
||||||
Task.URGENCY_SPECIFIC_DAY,
|
|
||||||
DateTime(2024, 5, 17).millis
|
|
||||||
),
|
|
||||||
deletionDate = DateTime(2024, 5, 17, 14, 0).millis,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
alarmService.synchronizeAlarms(
|
|
||||||
1,
|
|
||||||
mutableSetOf(Alarm(type = Alarm.TYPE_REL_END))
|
|
||||||
)
|
|
||||||
|
|
||||||
testResults(
|
|
||||||
emptyList(),
|
|
||||||
0
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private suspend fun testResults(notifications: List<Notification>, nextAlarm: Long) {
|
|
||||||
val actualNextAlarm = alarmService.triggerAlarms {
|
|
||||||
assertEquals(notifications, it)
|
|
||||||
it.forEach { taskDao.setLastNotified(it.taskId, DateTimeUtils2.currentTimeMillis()) }
|
|
||||||
}
|
|
||||||
assertEquals(nextAlarm, actualNextAlarm)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@ -1,142 +0,0 @@
|
|||||||
package com.todoroo.astrid.gcal
|
|
||||||
|
|
||||||
import android.Manifest
|
|
||||||
import android.content.ContentUris
|
|
||||||
import android.content.ContentValues
|
|
||||||
import android.content.Context
|
|
||||||
import android.provider.CalendarContract
|
|
||||||
import android.provider.CalendarContract.Calendars
|
|
||||||
import android.provider.CalendarContract.Events
|
|
||||||
import androidx.core.net.toUri
|
|
||||||
import androidx.test.core.app.ApplicationProvider
|
|
||||||
import androidx.test.rule.GrantPermissionRule
|
|
||||||
import dagger.hilt.android.testing.HiltAndroidTest
|
|
||||||
import org.junit.After
|
|
||||||
import org.junit.Assert.assertEquals
|
|
||||||
import org.junit.Before
|
|
||||||
import org.junit.Rule
|
|
||||||
import org.junit.Test
|
|
||||||
import org.tasks.TestUtilities.withTZ
|
|
||||||
import org.tasks.data.entity.Task
|
|
||||||
import org.tasks.injection.InjectingTestCase
|
|
||||||
import org.tasks.time.DateTime
|
|
||||||
import timber.log.Timber
|
|
||||||
import javax.inject.Inject
|
|
||||||
|
|
||||||
@HiltAndroidTest
|
|
||||||
class GCalHelperTest : InjectingTestCase() {
|
|
||||||
|
|
||||||
@get:Rule
|
|
||||||
val grantPermissionRule: GrantPermissionRule = GrantPermissionRule.grant(
|
|
||||||
Manifest.permission.READ_CALENDAR,
|
|
||||||
Manifest.permission.WRITE_CALENDAR
|
|
||||||
)
|
|
||||||
|
|
||||||
@Inject lateinit var gcalHelper: GCalHelper
|
|
||||||
|
|
||||||
private var testCalendarId: Long = -1
|
|
||||||
|
|
||||||
@Before
|
|
||||||
override fun setUp() {
|
|
||||||
super.setUp()
|
|
||||||
testCalendarId = createTestCalendar()
|
|
||||||
}
|
|
||||||
|
|
||||||
@After
|
|
||||||
fun tearDown() {
|
|
||||||
if (testCalendarId > 0) {
|
|
||||||
try {
|
|
||||||
val context = ApplicationProvider.getApplicationContext<Context>()
|
|
||||||
context.contentResolver.delete(
|
|
||||||
ContentUris.withAppendedId(Calendars.CONTENT_URI, testCalendarId),
|
|
||||||
null,
|
|
||||||
null
|
|
||||||
)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Timber.e(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test fun allDayEventInNewYork() = assertAllDayEvent("America/New_York") // UTC-5
|
|
||||||
@Test fun allDayEventInBerlin() = assertAllDayEvent("Europe/Berlin") // UTC+1
|
|
||||||
@Test fun allDayEventInAuckland() = assertAllDayEvent("Pacific/Auckland") // UTC+13
|
|
||||||
@Test fun allDayEventInTokyo() = assertAllDayEvent("Asia/Tokyo") // UTC+9
|
|
||||||
@Test fun allDayEventInHonolulu() = assertAllDayEvent("Pacific/Honolulu") // UTC-10
|
|
||||||
@Test fun allDayEventInChatham() = assertAllDayEvent("Pacific/Chatham") // UTC+13:45
|
|
||||||
|
|
||||||
private fun assertAllDayEvent(timezone: String) = withTZ(timezone) {
|
|
||||||
val task = Task(dueDate = DateTime(2024, 12, 20).millis)
|
|
||||||
|
|
||||||
val eventUri = gcalHelper.createTaskEvent(task, testCalendarId.toString())
|
|
||||||
?: throw RuntimeException("Event not created")
|
|
||||||
|
|
||||||
val event = queryEvent(eventUri.toString()) ?: throw RuntimeException("Event not found")
|
|
||||||
|
|
||||||
assertEquals(
|
|
||||||
"DTSTART should be Dec 20 00:00 UTC",
|
|
||||||
DateTime(2024, 12, 20, timeZone = DateTime.UTC).millis,
|
|
||||||
event.dtStart
|
|
||||||
)
|
|
||||||
assertEquals(
|
|
||||||
"DTEND should be Dec 21 00:00 UTC",
|
|
||||||
DateTime(2024, 12, 21, timeZone = DateTime.UTC).millis,
|
|
||||||
event.dtEnd
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun createTestCalendar(): Long {
|
|
||||||
val context = ApplicationProvider.getApplicationContext<Context>()
|
|
||||||
val values = ContentValues().apply {
|
|
||||||
put(Calendars.ACCOUNT_NAME, "test@test.com")
|
|
||||||
put(Calendars.ACCOUNT_TYPE, CalendarContract.ACCOUNT_TYPE_LOCAL)
|
|
||||||
put(Calendars.NAME, "Test Calendar")
|
|
||||||
put(Calendars.CALENDAR_DISPLAY_NAME, "Test Calendar")
|
|
||||||
put(Calendars.CALENDAR_COLOR, 0xFF0000)
|
|
||||||
put(Calendars.CALENDAR_ACCESS_LEVEL, Calendars.CAL_ACCESS_OWNER)
|
|
||||||
put(Calendars.OWNER_ACCOUNT, "test@test.com")
|
|
||||||
put(Calendars.VISIBLE, 1)
|
|
||||||
put(Calendars.SYNC_EVENTS, 1)
|
|
||||||
}
|
|
||||||
val uri = Calendars.CONTENT_URI.buildUpon()
|
|
||||||
.appendQueryParameter(CalendarContract.CALLER_IS_SYNCADAPTER, "true")
|
|
||||||
.appendQueryParameter(Calendars.ACCOUNT_NAME, "test@test.com")
|
|
||||||
.appendQueryParameter(Calendars.ACCOUNT_TYPE, CalendarContract.ACCOUNT_TYPE_LOCAL)
|
|
||||||
.build()
|
|
||||||
val calendarUri = context.contentResolver.insert(uri, values)
|
|
||||||
return ContentUris.parseId(calendarUri!!)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun queryEvent(eventUri: String): CalendarEvent? {
|
|
||||||
val context = ApplicationProvider.getApplicationContext<Context>()
|
|
||||||
val cursor = context.contentResolver.query(
|
|
||||||
eventUri.toUri(),
|
|
||||||
arrayOf(
|
|
||||||
Events.DTSTART,
|
|
||||||
Events.DTEND,
|
|
||||||
Events.ALL_DAY,
|
|
||||||
Events.EVENT_TIMEZONE
|
|
||||||
),
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
null
|
|
||||||
)
|
|
||||||
return cursor?.use {
|
|
||||||
if (it.moveToFirst()) {
|
|
||||||
CalendarEvent(
|
|
||||||
dtStart = it.getLong(0),
|
|
||||||
dtEnd = it.getLong(1),
|
|
||||||
allDay = it.getInt(2) == 1,
|
|
||||||
timezone = it.getString(3)
|
|
||||||
)
|
|
||||||
} else null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private data class CalendarEvent(
|
|
||||||
val dtStart: Long,
|
|
||||||
val dtEnd: Long,
|
|
||||||
val allDay: Boolean,
|
|
||||||
val timezone: String?
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@ -1,93 +1,92 @@
|
|||||||
package com.todoroo.astrid.gtasks
|
package com.todoroo.astrid.gtasks
|
||||||
|
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
import com.google.api.services.tasks.model.TaskList
|
import com.google.api.services.tasks.model.TaskList
|
||||||
import com.natpryce.makeiteasy.MakeItEasy.with
|
import com.natpryce.makeiteasy.MakeItEasy.with
|
||||||
import com.todoroo.astrid.service.TaskDeleter
|
import com.todoroo.astrid.service.TaskDeleter
|
||||||
import dagger.hilt.android.testing.HiltAndroidTest
|
|
||||||
import kotlinx.coroutines.runBlocking
|
|
||||||
import org.junit.Assert.assertEquals
|
import org.junit.Assert.assertEquals
|
||||||
import org.junit.Assert.assertNull
|
import org.junit.Assert.assertNull
|
||||||
import org.junit.Before
|
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
import org.tasks.LocalBroadcastManager
|
import org.tasks.LocalBroadcastManager
|
||||||
import org.tasks.data.dao.CaldavDao
|
import org.tasks.data.GoogleTaskAccount
|
||||||
import org.tasks.data.entity.CaldavAccount
|
import org.tasks.data.GoogleTaskListDao
|
||||||
import org.tasks.data.entity.CaldavCalendar
|
|
||||||
import org.tasks.injection.InjectingTestCase
|
import org.tasks.injection.InjectingTestCase
|
||||||
|
import org.tasks.injection.TestComponent
|
||||||
|
import org.tasks.makers.GtaskListMaker.ID
|
||||||
|
import org.tasks.makers.GtaskListMaker.NAME
|
||||||
|
import org.tasks.makers.GtaskListMaker.REMOTE_ID
|
||||||
|
import org.tasks.makers.GtaskListMaker.newGtaskList
|
||||||
import org.tasks.makers.RemoteGtaskListMaker
|
import org.tasks.makers.RemoteGtaskListMaker
|
||||||
import org.tasks.makers.RemoteGtaskListMaker.newRemoteList
|
import org.tasks.makers.RemoteGtaskListMaker.newRemoteList
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@HiltAndroidTest
|
@RunWith(AndroidJUnit4::class)
|
||||||
class GtasksListServiceTest : InjectingTestCase() {
|
class GtasksListServiceTest : InjectingTestCase() {
|
||||||
@Inject lateinit var taskDeleter: TaskDeleter
|
@Inject lateinit var taskDeleter: TaskDeleter
|
||||||
@Inject lateinit var localBroadcastManager: LocalBroadcastManager
|
@Inject lateinit var localBroadcastManager: LocalBroadcastManager
|
||||||
@Inject lateinit var caldavDao: CaldavDao
|
@Inject lateinit var googleTaskListDao: GoogleTaskListDao
|
||||||
|
|
||||||
private lateinit var gtasksListService: GtasksListService
|
private lateinit var gtasksListService: GtasksListService
|
||||||
|
|
||||||
@Before
|
|
||||||
override fun setUp() {
|
override fun setUp() {
|
||||||
super.setUp()
|
super.setUp()
|
||||||
gtasksListService = GtasksListService(caldavDao, taskDeleter, localBroadcastManager)
|
gtasksListService = GtasksListService(googleTaskListDao, taskDeleter, localBroadcastManager)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun inject(component: TestComponent) = component.inject(this)
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testCreateNewList() = runBlocking {
|
fun testCreateNewList() {
|
||||||
setLists(
|
setLists(
|
||||||
newRemoteList(
|
newRemoteList(
|
||||||
with(RemoteGtaskListMaker.REMOTE_ID, "1"), with(RemoteGtaskListMaker.NAME, "Default")))
|
with(RemoteGtaskListMaker.REMOTE_ID, "1"), with(RemoteGtaskListMaker.NAME, "Default")))
|
||||||
assertEquals(
|
assertEquals(
|
||||||
CaldavCalendar(id = 1, account = "account", uuid = "1", name = "Default"),
|
newGtaskList(with(ID, 1L), with(REMOTE_ID, "1"), with(NAME, "Default")),
|
||||||
caldavDao.getCalendarById(1L)
|
googleTaskListDao.getById(1L))
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testGetListByRemoteId() = runBlocking {
|
fun testGetListByRemoteId() {
|
||||||
val list = CaldavCalendar(uuid = "1")
|
val list = newGtaskList(with(REMOTE_ID, "1"))
|
||||||
caldavDao.insert(list)
|
list.id = googleTaskListDao.insertOrReplace(list)
|
||||||
assertEquals(list, caldavDao.getCalendarByUuid("1"))
|
assertEquals(list, gtasksListService.getList("1"))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testGetListReturnsNullWhenNotFound() = runBlocking {
|
fun testGetListReturnsNullWhenNotFound() {
|
||||||
assertNull(caldavDao.getCalendarByUuid("1"))
|
assertNull(gtasksListService.getList("1"))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testDeleteMissingList() = runBlocking {
|
fun testDeleteMissingList() {
|
||||||
caldavDao.insert(CaldavCalendar(account = "account", uuid = "1"))
|
googleTaskListDao.insertOrReplace(newGtaskList(with(ID, 1L), with(REMOTE_ID, "1")))
|
||||||
val taskList = newRemoteList(with(RemoteGtaskListMaker.REMOTE_ID, "2"))
|
val taskList = newRemoteList(with(RemoteGtaskListMaker.REMOTE_ID, "2"))
|
||||||
setLists(taskList)
|
setLists(taskList)
|
||||||
assertEquals(
|
assertEquals(
|
||||||
listOf(CaldavCalendar(id = 2, account = "account", uuid = "2", name = "Default")),
|
listOf(newGtaskList(with(ID, 2L), with(REMOTE_ID, "2"))),
|
||||||
caldavDao.getCalendarsByAccount("account")
|
googleTaskListDao.getLists("account"))
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testUpdateListName() = runBlocking {
|
fun testUpdateListName() {
|
||||||
val calendar = CaldavCalendar(uuid = "1", name = "oldName", account = "account")
|
googleTaskListDao.insertOrReplace(
|
||||||
caldavDao.insert(calendar)
|
newGtaskList(with(ID, 1L), with(REMOTE_ID, "1"), with(NAME, "oldName")))
|
||||||
setLists(
|
setLists(
|
||||||
newRemoteList(
|
newRemoteList(
|
||||||
with(RemoteGtaskListMaker.REMOTE_ID, "1"), with(RemoteGtaskListMaker.NAME, "newName")))
|
with(RemoteGtaskListMaker.REMOTE_ID, "1"), with(RemoteGtaskListMaker.NAME, "newName")))
|
||||||
assertEquals("newName", caldavDao.getCalendarById(calendar.id)!!.name)
|
assertEquals("newName", googleTaskListDao.getById(1)!!.title)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testNewListLastSyncIsZero() = runBlocking {
|
fun testNewListLastSyncIsZero() {
|
||||||
setLists(TaskList().setId("1"))
|
setLists(TaskList().setId("1"))
|
||||||
assertEquals(0L, caldavDao.getCalendarByUuid("1")!!.lastSync)
|
assertEquals(0L, gtasksListService.getList("1").lastSync)
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun setLists(vararg list: TaskList) {
|
private fun setLists(vararg list: TaskList) {
|
||||||
val account = CaldavAccount(
|
val account = GoogleTaskAccount("account")
|
||||||
username = "account",
|
googleTaskListDao.insert(account)
|
||||||
uuid = "account",
|
|
||||||
)
|
|
||||||
caldavDao.insert(account)
|
|
||||||
gtasksListService.updateLists(account, listOf(*list))
|
gtasksListService.updateLists(account, listOf(*list))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -0,0 +1,81 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2012 Todoroo Inc
|
||||||
|
*
|
||||||
|
* See the file "LICENSE" for the full license governing this code.
|
||||||
|
*/
|
||||||
|
package com.todoroo.astrid.gtasks
|
||||||
|
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
|
import com.todoroo.astrid.dao.TaskDao
|
||||||
|
import com.todoroo.astrid.data.Task
|
||||||
|
import org.junit.Assert.assertNotNull
|
||||||
|
import org.junit.Assert.assertNull
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
import org.tasks.data.GoogleTask
|
||||||
|
import org.tasks.data.GoogleTaskDao
|
||||||
|
import org.tasks.injection.InjectingTestCase
|
||||||
|
import org.tasks.injection.TestComponent
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@RunWith(AndroidJUnit4::class)
|
||||||
|
class GtasksMetadataServiceTest : InjectingTestCase() {
|
||||||
|
@Inject lateinit var taskDao: TaskDao
|
||||||
|
@Inject lateinit var googleTaskDao: GoogleTaskDao
|
||||||
|
|
||||||
|
private var task: Task? = null
|
||||||
|
private var metadata: GoogleTask? = null
|
||||||
|
|
||||||
|
override fun inject(component: TestComponent) = component.inject(this)
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testMetadataFound() {
|
||||||
|
givenTask(taskWithMetadata(null))
|
||||||
|
whenSearchForMetadata()
|
||||||
|
thenExpectMetadataFound()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testMetadataDoesntExist() {
|
||||||
|
givenTask(taskWithoutMetadata())
|
||||||
|
whenSearchForMetadata()
|
||||||
|
thenExpectNoMetadataFound()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun thenExpectNoMetadataFound() {
|
||||||
|
assertNull(metadata)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun thenExpectMetadataFound() {
|
||||||
|
assertNotNull(metadata)
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- helpers
|
||||||
|
private fun whenSearchForMetadata() {
|
||||||
|
metadata = googleTaskDao.getByTaskId(task!!.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun taskWithMetadata(id: String?): Task {
|
||||||
|
val task = Task()
|
||||||
|
task.title = "cats"
|
||||||
|
taskDao.createNew(task)
|
||||||
|
val metadata = GoogleTask(task.id, "")
|
||||||
|
if (id != null) {
|
||||||
|
metadata.remoteId = id
|
||||||
|
}
|
||||||
|
metadata.task = task.id
|
||||||
|
googleTaskDao.insert(metadata)
|
||||||
|
return task
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun givenTask(taskToTest: Task) {
|
||||||
|
task = taskToTest
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun taskWithoutMetadata(): Task {
|
||||||
|
val task = Task()
|
||||||
|
task.title = "dogs"
|
||||||
|
taskDao.createNew(task)
|
||||||
|
return task
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,34 +1,37 @@
|
|||||||
package com.todoroo.astrid.model
|
package com.todoroo.astrid.model
|
||||||
|
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
import com.todoroo.astrid.dao.TaskDao
|
import com.todoroo.astrid.dao.TaskDao
|
||||||
import org.tasks.data.entity.Task
|
import com.todoroo.astrid.data.Task
|
||||||
import dagger.hilt.android.testing.HiltAndroidTest
|
|
||||||
import kotlinx.coroutines.runBlocking
|
|
||||||
import org.junit.Assert.assertEquals
|
import org.junit.Assert.assertEquals
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.tasks.SuspendFreeze.Companion.freezeClock
|
import org.junit.runner.RunWith
|
||||||
|
import org.tasks.Freeze
|
||||||
import org.tasks.injection.InjectingTestCase
|
import org.tasks.injection.InjectingTestCase
|
||||||
import org.tasks.time.DateTimeUtils2.currentTimeMillis
|
import org.tasks.injection.TestComponent
|
||||||
|
import org.tasks.time.DateTimeUtils
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@HiltAndroidTest
|
@RunWith(AndroidJUnit4::class)
|
||||||
class TaskTest : InjectingTestCase() {
|
class TaskTest : InjectingTestCase() {
|
||||||
@Inject lateinit var taskDao: TaskDao
|
@Inject lateinit var taskDao: TaskDao
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testSavedTaskHasCreationDate() = runBlocking {
|
fun testSavedTaskHasCreationDate() {
|
||||||
freezeClock {
|
Freeze.freezeClock {
|
||||||
val task = Task()
|
val task = Task()
|
||||||
taskDao.createNew(task)
|
taskDao.createNew(task)
|
||||||
assertEquals(currentTimeMillis(), task.creationDate)
|
assertEquals(DateTimeUtils.currentTimeMillis(), task.creationDate)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testReadTaskFromDb() = runBlocking {
|
fun testReadTaskFromDb() {
|
||||||
val task = Task()
|
val task = Task()
|
||||||
taskDao.createNew(task)
|
taskDao.createNew(task)
|
||||||
val fromDb = taskDao.fetch(task.id)
|
val fromDb = taskDao.fetch(task.id)
|
||||||
assertEquals(task, fromDb)
|
assertEquals(task, fromDb)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun inject(component: TestComponent) = component.inject(this)
|
||||||
}
|
}
|
||||||
@ -0,0 +1,326 @@
|
|||||||
|
package com.todoroo.astrid.reminders
|
||||||
|
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
|
import com.natpryce.makeiteasy.MakeItEasy.with
|
||||||
|
import com.todoroo.andlib.utility.DateUtilities
|
||||||
|
import com.todoroo.astrid.dao.TaskDao
|
||||||
|
import com.todoroo.astrid.data.Task
|
||||||
|
import org.junit.Assert.assertEquals
|
||||||
|
import org.junit.Assert.assertTrue
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
import org.tasks.Freeze
|
||||||
|
import org.tasks.R
|
||||||
|
import org.tasks.date.DateTimeUtils
|
||||||
|
import org.tasks.injection.InjectingTestCase
|
||||||
|
import org.tasks.injection.TestComponent
|
||||||
|
import org.tasks.jobs.NotificationQueue
|
||||||
|
import org.tasks.jobs.ReminderEntry
|
||||||
|
import org.tasks.makers.TaskMaker.COMPLETION_TIME
|
||||||
|
import org.tasks.makers.TaskMaker.CREATION_TIME
|
||||||
|
import org.tasks.makers.TaskMaker.DELETION_TIME
|
||||||
|
import org.tasks.makers.TaskMaker.DUE_DATE
|
||||||
|
import org.tasks.makers.TaskMaker.DUE_TIME
|
||||||
|
import org.tasks.makers.TaskMaker.ID
|
||||||
|
import org.tasks.makers.TaskMaker.RANDOM_REMINDER_PERIOD
|
||||||
|
import org.tasks.makers.TaskMaker.REMINDERS
|
||||||
|
import org.tasks.makers.TaskMaker.REMINDER_LAST
|
||||||
|
import org.tasks.makers.TaskMaker.SNOOZE_TIME
|
||||||
|
import org.tasks.makers.TaskMaker.newTask
|
||||||
|
import org.tasks.preferences.Preferences
|
||||||
|
import org.tasks.reminders.Random
|
||||||
|
import org.tasks.time.DateTime
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@RunWith(AndroidJUnit4::class)
|
||||||
|
class ReminderServiceTest : InjectingTestCase() {
|
||||||
|
@Inject lateinit var preferences: Preferences
|
||||||
|
@Inject lateinit var taskDao: TaskDao
|
||||||
|
@Inject lateinit var jobs: NotificationQueue
|
||||||
|
|
||||||
|
private lateinit var service: ReminderService
|
||||||
|
private lateinit var random: RandomStub
|
||||||
|
|
||||||
|
override fun setUp() {
|
||||||
|
super.setUp()
|
||||||
|
random = RandomStub()
|
||||||
|
preferences.clear()
|
||||||
|
service = ReminderService(preferences, jobs, random, taskDao)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun inject(component: TestComponent) = component.inject(this)
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun dontScheduleDueDateReminderWhenFlagNotSet() {
|
||||||
|
service.scheduleAlarm(newTask(with(ID, 1L), with(DUE_TIME, DateTimeUtils.newDateTime())))
|
||||||
|
|
||||||
|
assertTrue(jobs.isEmpty())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun dontScheduleDueDateReminderWhenTimeNotSet() {
|
||||||
|
service.scheduleAlarm(newTask(with(ID, 1L), with(REMINDERS, Task.NOTIFY_AT_DEADLINE)))
|
||||||
|
|
||||||
|
assertTrue(jobs.isEmpty())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun schedulePastDueDate() {
|
||||||
|
val task = newTask(
|
||||||
|
with(ID, 1L),
|
||||||
|
with(DUE_TIME, DateTimeUtils.newDateTime().minusDays(1)),
|
||||||
|
with(REMINDERS, Task.NOTIFY_AT_DEADLINE))
|
||||||
|
|
||||||
|
service.scheduleAlarm(task)
|
||||||
|
|
||||||
|
verify(ReminderEntry(1, task.dueDate, ReminderService.TYPE_DUE))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun scheduleFutureDueDate() {
|
||||||
|
val task = newTask(
|
||||||
|
with(ID, 1L),
|
||||||
|
with(DUE_TIME, DateTimeUtils.newDateTime().plusDays(1)),
|
||||||
|
with(REMINDERS, Task.NOTIFY_AT_DEADLINE))
|
||||||
|
|
||||||
|
service.scheduleAlarm(task)
|
||||||
|
|
||||||
|
verify(ReminderEntry(1, task.dueDate, ReminderService.TYPE_DUE))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun scheduleReminderAtDefaultDueTime() {
|
||||||
|
val now = DateTimeUtils.newDateTime()
|
||||||
|
val task = newTask(with(ID, 1L), with(DUE_DATE, now), with(REMINDERS, Task.NOTIFY_AT_DEADLINE))
|
||||||
|
|
||||||
|
service.scheduleAlarm(task)
|
||||||
|
|
||||||
|
verify(ReminderEntry(1, now.startOfDay().withHourOfDay(18).millis, ReminderService.TYPE_DUE))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun dontScheduleReminderForCompletedTask() {
|
||||||
|
val task = newTask(
|
||||||
|
with(ID, 1L),
|
||||||
|
with(DUE_TIME, DateTimeUtils.newDateTime().plusDays(1)),
|
||||||
|
with(COMPLETION_TIME, DateTimeUtils.newDateTime()),
|
||||||
|
with(REMINDERS, Task.NOTIFY_AT_DEADLINE))
|
||||||
|
|
||||||
|
service.scheduleAlarm(task)
|
||||||
|
|
||||||
|
assertTrue(jobs.isEmpty())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun dontScheduleReminderForDeletedTask() {
|
||||||
|
val task = newTask(
|
||||||
|
with(ID, 1L),
|
||||||
|
with(DUE_TIME, DateTimeUtils.newDateTime().plusDays(1)),
|
||||||
|
with(DELETION_TIME, DateTimeUtils.newDateTime()),
|
||||||
|
with(REMINDERS, Task.NOTIFY_AT_DEADLINE))
|
||||||
|
|
||||||
|
service.scheduleAlarm(task)
|
||||||
|
|
||||||
|
assertTrue(jobs.isEmpty())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun dontScheduleDueDateReminderWhenAlreadyReminded() {
|
||||||
|
val now = DateTimeUtils.newDateTime()
|
||||||
|
val task = newTask(
|
||||||
|
with(ID, 1L),
|
||||||
|
with(DUE_TIME, now),
|
||||||
|
with(REMINDER_LAST, now.plusSeconds(1)),
|
||||||
|
with(REMINDERS, Task.NOTIFY_AT_DEADLINE))
|
||||||
|
|
||||||
|
service.scheduleAlarm(task)
|
||||||
|
|
||||||
|
assertTrue(jobs.isEmpty())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun ignoreStaleSnoozeTime() {
|
||||||
|
val task = newTask(
|
||||||
|
with(ID, 1L),
|
||||||
|
with(DUE_TIME, DateTimeUtils.newDateTime()),
|
||||||
|
with(SNOOZE_TIME, DateTimeUtils.newDateTime().minusMinutes(5)),
|
||||||
|
with(REMINDER_LAST, DateTimeUtils.newDateTime().minusMinutes(4)),
|
||||||
|
with(REMINDERS, Task.NOTIFY_AT_DEADLINE))
|
||||||
|
|
||||||
|
service.scheduleAlarm(task)
|
||||||
|
|
||||||
|
verify(ReminderEntry(1, task.dueDate, ReminderService.TYPE_DUE))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun dontIgnoreMissedSnoozeTime() {
|
||||||
|
val dueDate = DateTimeUtils.newDateTime()
|
||||||
|
val task = newTask(
|
||||||
|
with(ID, 1L),
|
||||||
|
with(DUE_TIME, dueDate),
|
||||||
|
with(SNOOZE_TIME, dueDate.minusMinutes(4)),
|
||||||
|
with(REMINDER_LAST, dueDate.minusMinutes(5)),
|
||||||
|
with(REMINDERS, Task.NOTIFY_AT_DEADLINE))
|
||||||
|
|
||||||
|
service.scheduleAlarm(task)
|
||||||
|
|
||||||
|
verify(ReminderEntry(1, task.reminderSnooze, ReminderService.TYPE_SNOOZE))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun scheduleInitialRandomReminder() {
|
||||||
|
random.seed = 0.3865f
|
||||||
|
|
||||||
|
Freeze.freezeClock {
|
||||||
|
val now = DateTimeUtils.newDateTime()
|
||||||
|
val task = newTask(
|
||||||
|
with(ID, 1L),
|
||||||
|
with(REMINDER_LAST, null as DateTime?),
|
||||||
|
with(CREATION_TIME, now.minusDays(1)),
|
||||||
|
with(RANDOM_REMINDER_PERIOD, DateUtilities.ONE_WEEK))
|
||||||
|
|
||||||
|
service.scheduleAlarm(task)
|
||||||
|
|
||||||
|
verify(ReminderEntry(1L, now.minusDays(1).millis + 584206592, ReminderService.TYPE_RANDOM))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun scheduleNextRandomReminder() {
|
||||||
|
random.seed = 0.3865f
|
||||||
|
|
||||||
|
Freeze.freezeClock {
|
||||||
|
val now = DateTimeUtils.newDateTime()
|
||||||
|
val task = newTask(
|
||||||
|
with(ID, 1L),
|
||||||
|
with(REMINDER_LAST, now.minusDays(1)),
|
||||||
|
with(CREATION_TIME, now.minusDays(30)),
|
||||||
|
with(RANDOM_REMINDER_PERIOD, DateUtilities.ONE_WEEK))
|
||||||
|
|
||||||
|
service.scheduleAlarm(task)
|
||||||
|
|
||||||
|
verify(ReminderEntry(1L, now.minusDays(1).millis + 584206592, ReminderService.TYPE_RANDOM))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun scheduleOverdueRandomReminder() {
|
||||||
|
random.seed = 0.3865f
|
||||||
|
|
||||||
|
Freeze.freezeClock {
|
||||||
|
val now = DateTimeUtils.newDateTime()
|
||||||
|
val task = newTask(
|
||||||
|
with(ID, 1L),
|
||||||
|
with(REMINDER_LAST, now.minusDays(14)),
|
||||||
|
with(CREATION_TIME, now.minusDays(30)),
|
||||||
|
with(RANDOM_REMINDER_PERIOD, DateUtilities.ONE_WEEK))
|
||||||
|
|
||||||
|
service.scheduleAlarm(task)
|
||||||
|
|
||||||
|
verify(ReminderEntry(1L, now.millis + 10148400, ReminderService.TYPE_RANDOM))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun scheduleOverdueNoLastReminder() {
|
||||||
|
val task = newTask(
|
||||||
|
with(ID, 1L),
|
||||||
|
with(DUE_TIME, DateTime(2017, 9, 22, 15, 30)),
|
||||||
|
with(REMINDER_LAST, null as DateTime?),
|
||||||
|
with(REMINDERS, Task.NOTIFY_AFTER_DEADLINE))
|
||||||
|
|
||||||
|
service.scheduleAlarm(task)
|
||||||
|
|
||||||
|
verify(ReminderEntry(1L, DateTime(2017, 9, 23, 15, 30, 1, 0).millis, ReminderService.TYPE_OVERDUE))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun scheduleOverduePastLastReminder() {
|
||||||
|
val task = newTask(
|
||||||
|
with(ID, 1L),
|
||||||
|
with(DUE_TIME, DateTime(2017, 9, 22, 15, 30)),
|
||||||
|
with(REMINDER_LAST, DateTime(2017, 9, 24, 12, 0)),
|
||||||
|
with(REMINDERS, Task.NOTIFY_AFTER_DEADLINE))
|
||||||
|
|
||||||
|
service.scheduleAlarm(task)
|
||||||
|
|
||||||
|
verify(ReminderEntry(1L, DateTime(2017, 9, 24, 15, 30, 1, 0).millis, ReminderService.TYPE_OVERDUE))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun scheduleOverdueBeforeLastReminder() {
|
||||||
|
val task = newTask(
|
||||||
|
with(ID, 1L),
|
||||||
|
with(DUE_TIME, DateTime(2017, 9, 22, 12, 30)),
|
||||||
|
with(REMINDER_LAST, DateTime(2017, 9, 24, 15, 0)),
|
||||||
|
with(REMINDERS, Task.NOTIFY_AFTER_DEADLINE))
|
||||||
|
|
||||||
|
service.scheduleAlarm(task)
|
||||||
|
|
||||||
|
verify(ReminderEntry(1L, DateTime(2017, 9, 25, 12, 30, 1, 0).millis, ReminderService.TYPE_OVERDUE))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun scheduleOverdueWithNoDueTime() {
|
||||||
|
preferences.setInt(R.string.p_rmd_time, TimeUnit.HOURS.toMillis(15).toInt())
|
||||||
|
val task = newTask(
|
||||||
|
with(ID, 1L),
|
||||||
|
with(DUE_DATE, DateTime(2017, 9, 22)),
|
||||||
|
with(REMINDER_LAST, DateTime(2017, 9, 23, 12, 17, 59, 999)),
|
||||||
|
with(REMINDERS, Task.NOTIFY_AFTER_DEADLINE))
|
||||||
|
|
||||||
|
service.scheduleAlarm(task)
|
||||||
|
|
||||||
|
verify(ReminderEntry(1L, DateTime(2017, 9, 23, 15, 0, 0, 0).millis, ReminderService.TYPE_OVERDUE))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun scheduleSubsequentOverdueReminder() {
|
||||||
|
val task = newTask(
|
||||||
|
with(ID, 1L),
|
||||||
|
with(DUE_TIME, DateTime(2017, 9, 22, 15, 30)),
|
||||||
|
with(REMINDER_LAST, DateTime(2017, 9, 23, 15, 30, 59, 999)),
|
||||||
|
with(REMINDERS, Task.NOTIFY_AFTER_DEADLINE))
|
||||||
|
|
||||||
|
service.scheduleAlarm(task)
|
||||||
|
|
||||||
|
verify(ReminderEntry(1L, DateTime(2017, 9, 24, 15, 30, 1, 0).millis, ReminderService.TYPE_OVERDUE))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun scheduleOverdueAfterLastReminder() {
|
||||||
|
val task = newTask(
|
||||||
|
with(ID, 1L),
|
||||||
|
with(DUE_TIME, DateTime(2017, 9, 22, 15, 30)),
|
||||||
|
with(REMINDER_LAST, DateTime(2017, 9, 23, 12, 17, 59, 999)),
|
||||||
|
with(REMINDERS, Task.NOTIFY_AFTER_DEADLINE))
|
||||||
|
|
||||||
|
service.scheduleAlarm(task)
|
||||||
|
|
||||||
|
verify(ReminderEntry(1L, DateTime(2017, 9, 23, 15, 30, 1, 0).millis, ReminderService.TYPE_OVERDUE))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun snoozeOverridesAll() {
|
||||||
|
val now = DateTimeUtils.newDateTime()
|
||||||
|
val task = newTask(
|
||||||
|
with(ID, 1L),
|
||||||
|
with(DUE_TIME, now),
|
||||||
|
with(SNOOZE_TIME, now.plusMonths(12)),
|
||||||
|
with(REMINDERS, Task.NOTIFY_AT_DEADLINE or Task.NOTIFY_AFTER_DEADLINE),
|
||||||
|
with(RANDOM_REMINDER_PERIOD, DateUtilities.ONE_HOUR))
|
||||||
|
|
||||||
|
service.scheduleAlarm(task)
|
||||||
|
|
||||||
|
verify(ReminderEntry(1, now.plusMonths(12).millis, ReminderService.TYPE_SNOOZE))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun verify(vararg reminders: ReminderEntry) = assertEquals(reminders.toList(), jobs.getJobs())
|
||||||
|
|
||||||
|
internal class RandomStub : Random() {
|
||||||
|
var seed = 1.0f
|
||||||
|
|
||||||
|
override fun nextFloat() = seed
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,193 @@
|
|||||||
|
package com.todoroo.astrid.repeats
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
|
import com.google.ical.values.RRule
|
||||||
|
import com.natpryce.makeiteasy.MakeItEasy.with
|
||||||
|
import com.todoroo.astrid.alarms.AlarmService
|
||||||
|
import com.todoroo.astrid.dao.TaskDao
|
||||||
|
import com.todoroo.astrid.data.Task
|
||||||
|
import com.todoroo.astrid.gcal.GCalHelper
|
||||||
|
import org.junit.After
|
||||||
|
import org.junit.Assert.assertEquals
|
||||||
|
import org.junit.Before
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
import org.mockito.InOrder
|
||||||
|
import org.mockito.Mockito
|
||||||
|
import org.tasks.LocalBroadcastManager
|
||||||
|
import org.tasks.injection.InjectingTestCase
|
||||||
|
import org.tasks.injection.TestComponent
|
||||||
|
import org.tasks.makers.TaskMaker.AFTER_COMPLETE
|
||||||
|
import org.tasks.makers.TaskMaker.COMPLETION_TIME
|
||||||
|
import org.tasks.makers.TaskMaker.DUE_TIME
|
||||||
|
import org.tasks.makers.TaskMaker.ID
|
||||||
|
import org.tasks.makers.TaskMaker.RRULE
|
||||||
|
import org.tasks.makers.TaskMaker.newTask
|
||||||
|
import org.tasks.time.DateTime
|
||||||
|
import java.text.ParseException
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@SuppressLint("NewApi")
|
||||||
|
@RunWith(AndroidJUnit4::class)
|
||||||
|
class RepeatTaskHelperTest : InjectingTestCase() {
|
||||||
|
@Inject lateinit var taskDao: TaskDao
|
||||||
|
private lateinit var localBroadcastManager: LocalBroadcastManager
|
||||||
|
private lateinit var alarmService: AlarmService
|
||||||
|
private lateinit var gCalHelper: GCalHelper
|
||||||
|
private lateinit var helper: RepeatTaskHelper
|
||||||
|
private lateinit var mocks: InOrder
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun before() {
|
||||||
|
alarmService = Mockito.mock(AlarmService::class.java)
|
||||||
|
gCalHelper = Mockito.mock(GCalHelper::class.java)
|
||||||
|
localBroadcastManager = Mockito.mock(LocalBroadcastManager::class.java)
|
||||||
|
mocks = Mockito.inOrder(alarmService, gCalHelper, localBroadcastManager)
|
||||||
|
helper = RepeatTaskHelper(gCalHelper, alarmService, taskDao, localBroadcastManager)
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
fun after() {
|
||||||
|
Mockito.verifyNoMoreInteractions(localBroadcastManager, gCalHelper, alarmService)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun noRepeat() {
|
||||||
|
helper.handleRepeat(newTask(with(DUE_TIME, DateTime(2017, 10, 4, 13, 30))))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Throws(ParseException::class)
|
||||||
|
fun testMinutelyRepeat() {
|
||||||
|
val task = newTask(
|
||||||
|
with(ID, 1L),
|
||||||
|
with(DUE_TIME, DateTime(2017, 10, 4, 13, 30)),
|
||||||
|
with(RRULE, RRule("RRULE:FREQ=MINUTELY;INTERVAL=30")))
|
||||||
|
repeatAndVerify(
|
||||||
|
task, DateTime(2017, 10, 4, 13, 30, 1), DateTime(2017, 10, 4, 14, 0, 1))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Throws(ParseException::class)
|
||||||
|
fun testMinutelyRepeatAfterCompletion() {
|
||||||
|
val task = newTask(
|
||||||
|
with(ID, 1L),
|
||||||
|
with(DUE_TIME, DateTime(2017, 10, 4, 13, 30)),
|
||||||
|
with(COMPLETION_TIME, DateTime(2017, 10, 4, 13, 17, 45, 340)),
|
||||||
|
with(RRULE, RRule("RRULE:FREQ=MINUTELY;INTERVAL=30")),
|
||||||
|
with(AFTER_COMPLETE, true))
|
||||||
|
repeatAndVerify(
|
||||||
|
task, DateTime(2017, 10, 4, 13, 30, 1), DateTime(2017, 10, 4, 13, 47, 1))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Throws(ParseException::class)
|
||||||
|
fun testMinutelyDecrementCount() {
|
||||||
|
val task = newTask(
|
||||||
|
with(ID, 1L),
|
||||||
|
with(DUE_TIME, DateTime(2017, 10, 4, 13, 30)),
|
||||||
|
with(RRULE, RRule("RRULE:FREQ=MINUTELY;COUNT=2;INTERVAL=30")))
|
||||||
|
repeatAndVerify(
|
||||||
|
task, DateTime(2017, 10, 4, 13, 30, 1), DateTime(2017, 10, 4, 14, 0, 1))
|
||||||
|
assertEquals(1, RRule(task.getRecurrenceWithoutFrom()).count)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Throws(ParseException::class)
|
||||||
|
fun testMinutelyLastOccurrence() {
|
||||||
|
val task = newTask(
|
||||||
|
with(ID, 1L),
|
||||||
|
with(DUE_TIME, DateTime(2017, 10, 4, 13, 30)),
|
||||||
|
with(RRULE, RRule("RRULE:FREQ=MINUTELY;COUNT=1;INTERVAL=30")))
|
||||||
|
helper.handleRepeat(task)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Throws(ParseException::class)
|
||||||
|
fun testHourlyRepeat() {
|
||||||
|
val task = newTask(
|
||||||
|
with(ID, 1L),
|
||||||
|
with(DUE_TIME, DateTime(2017, 10, 4, 13, 30)),
|
||||||
|
with(RRULE, RRule("RRULE:FREQ=HOURLY;INTERVAL=6")))
|
||||||
|
repeatAndVerify(
|
||||||
|
task, DateTime(2017, 10, 4, 13, 30, 1), DateTime(2017, 10, 4, 19, 30, 1))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Throws(ParseException::class)
|
||||||
|
fun testHourlyRepeatAfterCompletion() {
|
||||||
|
val task = newTask(
|
||||||
|
with(ID, 1L),
|
||||||
|
with(DUE_TIME, DateTime(2017, 10, 4, 13, 30)),
|
||||||
|
with(COMPLETION_TIME, DateTime(2017, 10, 4, 13, 17, 45, 340)),
|
||||||
|
with(RRULE, RRule("RRULE:FREQ=HOURLY;INTERVAL=6")),
|
||||||
|
with(AFTER_COMPLETE, true))
|
||||||
|
repeatAndVerify(
|
||||||
|
task, DateTime(2017, 10, 4, 13, 30, 1), DateTime(2017, 10, 4, 19, 17, 1))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Throws(ParseException::class)
|
||||||
|
fun testDailyRepeat() {
|
||||||
|
val task = newTask(
|
||||||
|
with(ID, 1L),
|
||||||
|
with(DUE_TIME, DateTime(2017, 10, 4, 13, 30)),
|
||||||
|
with(RRULE, RRule("RRULE:FREQ=DAILY;INTERVAL=6")))
|
||||||
|
repeatAndVerify(
|
||||||
|
task, DateTime(2017, 10, 4, 13, 30, 1), DateTime(2017, 10, 10, 13, 30, 1))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Throws(ParseException::class)
|
||||||
|
fun testRepeatWeeklyNoDays() {
|
||||||
|
val task = newTask(
|
||||||
|
with(ID, 1L),
|
||||||
|
with(DUE_TIME, DateTime(2017, 10, 4, 13, 30)),
|
||||||
|
with(RRULE, RRule("RRULE:FREQ=WEEKLY;INTERVAL=2")))
|
||||||
|
repeatAndVerify(
|
||||||
|
task, DateTime(2017, 10, 4, 13, 30, 1), DateTime(2017, 10, 18, 13, 30, 1))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Throws(ParseException::class)
|
||||||
|
fun testYearly() {
|
||||||
|
val task = newTask(
|
||||||
|
with(ID, 1L),
|
||||||
|
with(DUE_TIME, DateTime(2017, 10, 4, 13, 30)),
|
||||||
|
with(RRULE, RRule("RRULE:FREQ=YEARLY;INTERVAL=3")))
|
||||||
|
repeatAndVerify(
|
||||||
|
task, DateTime(2017, 10, 4, 13, 30, 1), DateTime(2020, 10, 4, 13, 30, 1))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Throws(ParseException::class)
|
||||||
|
fun testMonthlyRepeat() {
|
||||||
|
val task = newTask(
|
||||||
|
with(ID, 1L),
|
||||||
|
with(DUE_TIME, DateTime(2017, 10, 4, 13, 30)),
|
||||||
|
with(RRULE, RRule("RRULE:FREQ=MONTHLY;INTERVAL=3")))
|
||||||
|
repeatAndVerify(
|
||||||
|
task, DateTime(2017, 10, 4, 13, 30, 1), DateTime(2018, 1, 4, 13, 30, 1))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Throws(ParseException::class)
|
||||||
|
fun testMonthlyRepeatAtEndOfMonth() {
|
||||||
|
val task = newTask(
|
||||||
|
with(ID, 1L),
|
||||||
|
with(DUE_TIME, DateTime(2017, 1, 31, 13, 30)),
|
||||||
|
with(RRULE, RRule("RRULE:FREQ=MONTHLY;INTERVAL=1")))
|
||||||
|
repeatAndVerify(
|
||||||
|
task, DateTime(2017, 1, 31, 13, 30, 1), DateTime(2017, 2, 28, 13, 30, 1))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun repeatAndVerify(task: Task, oldDueDate: DateTime, newDueDate: DateTime) {
|
||||||
|
helper.handleRepeat(task)
|
||||||
|
mocks.verify(gCalHelper).rescheduleRepeatingTask(task)
|
||||||
|
mocks.verify(alarmService).rescheduleAlarms(1, oldDueDate.millis, newDueDate.millis)
|
||||||
|
mocks.verify(localBroadcastManager).broadcastRepeat(1, oldDueDate.millis, newDueDate.millis)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun inject(component: TestComponent) = component.inject(this)
|
||||||
|
}
|
||||||
@ -1,66 +0,0 @@
|
|||||||
package com.todoroo.astrid.repeats
|
|
||||||
|
|
||||||
import org.tasks.data.entity.Task
|
|
||||||
import com.todoroo.astrid.service.TaskCompleter
|
|
||||||
import dagger.hilt.android.testing.HiltAndroidTest
|
|
||||||
import kotlinx.coroutines.runBlocking
|
|
||||||
import org.junit.Assert.assertFalse
|
|
||||||
import org.junit.Assert.assertTrue
|
|
||||||
import org.junit.Test
|
|
||||||
import org.tasks.data.dao.TaskDao
|
|
||||||
import org.tasks.injection.InjectingTestCase
|
|
||||||
import org.tasks.time.DateTimeUtils2.currentTimeMillis
|
|
||||||
import javax.inject.Inject
|
|
||||||
|
|
||||||
@HiltAndroidTest
|
|
||||||
class RepeatWithSubtasksTests : InjectingTestCase() {
|
|
||||||
@Inject lateinit var taskDao: TaskDao
|
|
||||||
@Inject lateinit var taskCompleter: TaskCompleter
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun uncompleteGrandchildren() = runBlocking {
|
|
||||||
val grandparent = taskDao.createNew(
|
|
||||||
Task(
|
|
||||||
recurrence = "RRULE:FREQ=DAILY"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
val parent = taskDao.createNew(
|
|
||||||
Task(
|
|
||||||
parent = grandparent
|
|
||||||
)
|
|
||||||
)
|
|
||||||
val child = taskDao.createNew(
|
|
||||||
Task(
|
|
||||||
parent = parent,
|
|
||||||
completionDate = currentTimeMillis(),
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
assertTrue(taskDao.fetch(child)!!.isCompleted)
|
|
||||||
|
|
||||||
taskCompleter.setComplete(grandparent)
|
|
||||||
|
|
||||||
assertFalse(taskDao.fetch(child)!!.isCompleted)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun uncompleteGoogleTaskChildren() = runBlocking {
|
|
||||||
val parent = taskDao.createNew(
|
|
||||||
Task(
|
|
||||||
recurrence = "RRULE:FREQ=DAILY"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
val child = taskDao.createNew(
|
|
||||||
Task(
|
|
||||||
parent = parent,
|
|
||||||
completionDate = currentTimeMillis(),
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
assertTrue(taskDao.fetch(child)!!.isCompleted)
|
|
||||||
|
|
||||||
taskCompleter.setComplete(parent)
|
|
||||||
|
|
||||||
assertFalse(taskDao.fetch(child)!!.isCompleted)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,100 +0,0 @@
|
|||||||
package com.todoroo.astrid.service
|
|
||||||
|
|
||||||
import com.todoroo.astrid.api.PermaSql.VALUE_EOD
|
|
||||||
import com.todoroo.astrid.api.PermaSql.VALUE_EOD_NEXT_WEEK
|
|
||||||
import com.todoroo.astrid.api.PermaSql.VALUE_EOD_TOMORROW
|
|
||||||
import org.tasks.data.entity.Task
|
|
||||||
import org.tasks.data.entity.Task.Companion.DUE_DATE
|
|
||||||
import org.tasks.data.entity.Task.Companion.HIDE_UNTIL
|
|
||||||
import org.tasks.data.entity.Task.Companion.URGENCY_SPECIFIC_DAY
|
|
||||||
import dagger.hilt.android.testing.HiltAndroidTest
|
|
||||||
import kotlinx.coroutines.runBlocking
|
|
||||||
import org.junit.Assert.assertEquals
|
|
||||||
import org.junit.Test
|
|
||||||
import org.tasks.R
|
|
||||||
import org.tasks.SuspendFreeze.Companion.freezeAt
|
|
||||||
import org.tasks.data.createDueDate
|
|
||||||
import org.tasks.injection.InjectingTestCase
|
|
||||||
import org.tasks.preferences.Preferences
|
|
||||||
import org.tasks.time.DateTime
|
|
||||||
import javax.inject.Inject
|
|
||||||
|
|
||||||
@HiltAndroidTest
|
|
||||||
class TaskCreatorTest : InjectingTestCase() {
|
|
||||||
@Inject lateinit var preferences: Preferences
|
|
||||||
@Inject lateinit var taskCreator: TaskCreator
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun setStartAndDueFromFilter() = runBlocking {
|
|
||||||
val task = freezeAt(DateTime(2021, 2, 4, 14, 56, 34, 126)) {
|
|
||||||
taskCreator.create(mapOf(
|
|
||||||
HIDE_UNTIL.name!! to VALUE_EOD,
|
|
||||||
DUE_DATE.name!! to VALUE_EOD_TOMORROW
|
|
||||||
), null)
|
|
||||||
}
|
|
||||||
|
|
||||||
assertEquals(DateTime(2021, 2, 4).millis, task.hideUntil)
|
|
||||||
assertEquals(
|
|
||||||
createDueDate(URGENCY_SPECIFIC_DAY, DateTime(2021, 2, 5).millis),
|
|
||||||
task.dueDate
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun setDefaultStartWithFilterDue() = runBlocking {
|
|
||||||
preferences.setString(R.string.p_default_hideUntil_key, Task.HIDE_UNTIL_DUE.toString())
|
|
||||||
val task = freezeAt(DateTime(2021, 2, 4, 14, 56, 34, 126)) {
|
|
||||||
taskCreator.create(mapOf(
|
|
||||||
DUE_DATE.name!! to VALUE_EOD
|
|
||||||
), null)
|
|
||||||
}
|
|
||||||
|
|
||||||
assertEquals(DateTime(2021, 2, 4).millis, task.hideUntil)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun setStartAndDueFromPreferences() = runBlocking {
|
|
||||||
preferences.setString(R.string.p_default_urgency_key, Task.URGENCY_TODAY.toString())
|
|
||||||
preferences.setString(R.string.p_default_hideUntil_key, Task.HIDE_UNTIL_DUE.toString())
|
|
||||||
|
|
||||||
val task = freezeAt(DateTime(2021, 2, 4, 14, 56, 34, 126)) {
|
|
||||||
taskCreator.create(null, "test")
|
|
||||||
}
|
|
||||||
|
|
||||||
assertEquals(DateTime(2021, 2, 4).millis, task.hideUntil)
|
|
||||||
assertEquals(
|
|
||||||
createDueDate(URGENCY_SPECIFIC_DAY, DateTime(2021, 2, 4).millis),
|
|
||||||
task.dueDate
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun filterStartOverridesDefaultStart() = runBlocking {
|
|
||||||
preferences.setString(R.string.p_default_urgency_key, Task.URGENCY_TODAY.toString())
|
|
||||||
preferences.setString(R.string.p_default_hideUntil_key, Task.HIDE_UNTIL_DUE.toString())
|
|
||||||
|
|
||||||
val task = freezeAt(DateTime(2021, 2, 4, 14, 56, 34, 126)) {
|
|
||||||
taskCreator.create(mapOf(
|
|
||||||
HIDE_UNTIL.name!! to VALUE_EOD_NEXT_WEEK
|
|
||||||
), null)
|
|
||||||
}
|
|
||||||
|
|
||||||
assertEquals(DateTime(2021, 2, 11).millis, task.hideUntil)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun filterDueOverridesDefaultDue() = runBlocking {
|
|
||||||
preferences.setString(R.string.p_default_urgency_key, Task.URGENCY_TODAY.toString())
|
|
||||||
|
|
||||||
val task = freezeAt(DateTime(2021, 2, 4, 14, 56, 34, 126)) {
|
|
||||||
taskCreator.create(mapOf(
|
|
||||||
DUE_DATE.name!! to VALUE_EOD_TOMORROW
|
|
||||||
), null)
|
|
||||||
}
|
|
||||||
|
|
||||||
assertEquals(
|
|
||||||
createDueDate(URGENCY_SPECIFIC_DAY, DateTime(2021, 2, 5).millis),
|
|
||||||
task.dueDate
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,39 +0,0 @@
|
|||||||
package com.todoroo.astrid.service
|
|
||||||
|
|
||||||
import org.tasks.data.entity.Task
|
|
||||||
import dagger.hilt.android.testing.HiltAndroidTest
|
|
||||||
import kotlinx.coroutines.runBlocking
|
|
||||||
import org.junit.Assert.assertFalse
|
|
||||||
import org.junit.Assert.assertTrue
|
|
||||||
import org.junit.Test
|
|
||||||
import org.tasks.data.dao.TaskDao
|
|
||||||
import org.tasks.injection.InjectingTestCase
|
|
||||||
import javax.inject.Inject
|
|
||||||
|
|
||||||
@HiltAndroidTest
|
|
||||||
class TaskDeleterTest : InjectingTestCase() {
|
|
||||||
@Inject lateinit var taskDao: TaskDao
|
|
||||||
@Inject lateinit var taskDeleter: TaskDeleter
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun markTaskAsDeleted() = runBlocking {
|
|
||||||
val task = Task()
|
|
||||||
taskDao.createNew(task)
|
|
||||||
|
|
||||||
taskDeleter.markDeleted(task)
|
|
||||||
|
|
||||||
assertTrue(taskDao.fetch(task.id)!!.isDeleted)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun dontDeleteReadOnlyTasks() = runBlocking {
|
|
||||||
val task = Task(
|
|
||||||
readOnly = true
|
|
||||||
)
|
|
||||||
taskDao.createNew(task)
|
|
||||||
|
|
||||||
taskDeleter.markDeleted(task)
|
|
||||||
|
|
||||||
assertFalse(taskDao.fetch(task.id)!!.isDeleted)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,62 +0,0 @@
|
|||||||
@file:Suppress("ClassName")
|
|
||||||
|
|
||||||
package com.todoroo.astrid.service
|
|
||||||
|
|
||||||
import androidx.test.core.app.ApplicationProvider
|
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
|
||||||
import com.todoroo.astrid.service.Upgrade_11_12_3.Companion.LEGACY_PREFERENCE
|
|
||||||
import org.junit.Assert.assertEquals
|
|
||||||
import org.junit.Before
|
|
||||||
import org.junit.Test
|
|
||||||
import org.junit.runner.RunWith
|
|
||||||
import org.tasks.TestUtilities.newPreferences
|
|
||||||
import org.tasks.preferences.Preferences
|
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4::class)
|
|
||||||
class Upgrade_11_12_3_Test {
|
|
||||||
private lateinit var preferences: Preferences
|
|
||||||
private lateinit var upgrader: Upgrade_11_12_3
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun migrateNoDefaultReminders() {
|
|
||||||
preferences.setString(LEGACY_PREFERENCE, "0")
|
|
||||||
upgrader.migrateDefaultReminderPreference()
|
|
||||||
|
|
||||||
assertEquals(emptySet<String>(), preferences.defaultRemindersSet)
|
|
||||||
assertEquals(0, preferences.defaultReminders)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun migrateWhenDue() {
|
|
||||||
preferences.setString(LEGACY_PREFERENCE, "2")
|
|
||||||
upgrader.migrateDefaultReminderPreference()
|
|
||||||
|
|
||||||
assertEquals(setOf("2"), preferences.defaultRemindersSet)
|
|
||||||
assertEquals(2, preferences.defaultReminders)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun migrateWhenOverdue() {
|
|
||||||
preferences.setString(LEGACY_PREFERENCE, "4")
|
|
||||||
upgrader.migrateDefaultReminderPreference()
|
|
||||||
|
|
||||||
assertEquals(setOf("4"), preferences.defaultRemindersSet)
|
|
||||||
assertEquals(4, preferences.defaultReminders)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun migrateWhenDueAndOverdue() {
|
|
||||||
preferences.setString(LEGACY_PREFERENCE, "6")
|
|
||||||
upgrader.migrateDefaultReminderPreference()
|
|
||||||
|
|
||||||
assertEquals(setOf("2", "4"), preferences.defaultRemindersSet)
|
|
||||||
assertEquals(6, preferences.defaultReminders)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Before
|
|
||||||
fun setUp() {
|
|
||||||
preferences = newPreferences(ApplicationProvider.getApplicationContext())
|
|
||||||
preferences.clear()
|
|
||||||
upgrader = Upgrade_11_12_3(preferences)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,217 +0,0 @@
|
|||||||
@file:Suppress("ClassName")
|
|
||||||
|
|
||||||
package com.todoroo.astrid.service
|
|
||||||
|
|
||||||
import com.natpryce.makeiteasy.MakeItEasy.with
|
|
||||||
import dagger.hilt.android.testing.HiltAndroidTest
|
|
||||||
import kotlinx.coroutines.runBlocking
|
|
||||||
import org.junit.Before
|
|
||||||
import org.junit.Test
|
|
||||||
import org.tasks.SuspendFreeze.Companion.freezeAt
|
|
||||||
import org.tasks.TestUtilities.assertEquals
|
|
||||||
import org.tasks.caldav.VtodoCache
|
|
||||||
import org.tasks.data.dao.CaldavDao
|
|
||||||
import org.tasks.data.dao.TaskDao
|
|
||||||
import org.tasks.data.entity.CaldavCalendar
|
|
||||||
import org.tasks.data.entity.Task
|
|
||||||
import org.tasks.injection.InjectingTestCase
|
|
||||||
import org.tasks.makers.CaldavTaskMaker.CALENDAR
|
|
||||||
import org.tasks.makers.CaldavTaskMaker.REMOTE_ID
|
|
||||||
import org.tasks.makers.CaldavTaskMaker.TASK
|
|
||||||
import org.tasks.makers.CaldavTaskMaker.newCaldavTask
|
|
||||||
import org.tasks.makers.TaskMaker.DUE_DATE
|
|
||||||
import org.tasks.makers.TaskMaker.HIDE_TYPE
|
|
||||||
import org.tasks.makers.TaskMaker.MODIFICATION_TIME
|
|
||||||
import org.tasks.makers.TaskMaker.newTask
|
|
||||||
import org.tasks.opentasks.TestOpenTaskDao
|
|
||||||
import org.tasks.time.DateTime
|
|
||||||
import javax.inject.Inject
|
|
||||||
|
|
||||||
@HiltAndroidTest
|
|
||||||
class Upgrade_11_3_Test : InjectingTestCase() {
|
|
||||||
@Inject lateinit var taskDao: TaskDao
|
|
||||||
@Inject lateinit var caldavDao: CaldavDao
|
|
||||||
@Inject lateinit var openTaskDao: TestOpenTaskDao
|
|
||||||
@Inject lateinit var upgrader: Upgrade_11_3
|
|
||||||
@Inject lateinit var vtodoCache: VtodoCache
|
|
||||||
|
|
||||||
private lateinit var calendar: CaldavCalendar
|
|
||||||
|
|
||||||
@Before
|
|
||||||
override fun setUp() {
|
|
||||||
super.setUp()
|
|
||||||
calendar = CaldavCalendar()
|
|
||||||
runBlocking {
|
|
||||||
caldavDao.insert(calendar)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun applyRemoteiCalendarStartDate() = runBlocking {
|
|
||||||
val taskId = taskDao.insert(newTask())
|
|
||||||
val caldavTask = newCaldavTask(with(TASK, taskId), with(CALENDAR, calendar.uuid))
|
|
||||||
caldavDao.insert(caldavTask)
|
|
||||||
vtodoCache.putVtodo(calendar, caldavTask, VTODO_WITH_START_DATE)
|
|
||||||
|
|
||||||
upgrader.applyiCalendarStartDates()
|
|
||||||
|
|
||||||
assertEquals(DateTime(2021, 1, 21), taskDao.fetch(taskId)?.hideUntil)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun ignoreRemoteiCalendarStartDate() = runBlocking {
|
|
||||||
val taskId = taskDao.insert(newTask(
|
|
||||||
with(DUE_DATE, DateTime(2021, 1, 20)),
|
|
||||||
with(HIDE_TYPE, Task.HIDE_UNTIL_DUE)
|
|
||||||
))
|
|
||||||
val caldavTask = newCaldavTask(with(TASK, taskId), with(CALENDAR, calendar.uuid))
|
|
||||||
caldavDao.insert(caldavTask)
|
|
||||||
vtodoCache.putVtodo(calendar, caldavTask, VTODO_WITH_START_DATE)
|
|
||||||
|
|
||||||
upgrader.applyiCalendarStartDates()
|
|
||||||
|
|
||||||
assertEquals(DateTime(2021, 1, 20), taskDao.fetch(taskId)?.hideUntil)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun touchTaskWithLocaliCalendarStartDate() = runBlocking {
|
|
||||||
val upgradeTime = DateTime(2021, 1, 21, 11, 47, 32, 450)
|
|
||||||
val taskId = taskDao.insert(newTask(
|
|
||||||
with(DUE_DATE, DateTime(2021, 1, 20)),
|
|
||||||
with(HIDE_TYPE, Task.HIDE_UNTIL_DUE),
|
|
||||||
with(MODIFICATION_TIME, DateTime(2021, 1, 21, 9, 50, 4, 348))
|
|
||||||
))
|
|
||||||
val caldavTask = newCaldavTask(with(TASK, taskId), with(CALENDAR, calendar.uuid))
|
|
||||||
caldavDao.insert(caldavTask)
|
|
||||||
vtodoCache.putVtodo(calendar, caldavTask, VTODO_WITH_START_DATE)
|
|
||||||
|
|
||||||
freezeAt(upgradeTime) {
|
|
||||||
upgrader.applyiCalendarStartDates()
|
|
||||||
}
|
|
||||||
|
|
||||||
assertEquals(upgradeTime, taskDao.fetch(taskId)?.modificationDate)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun dontTouchWhenNoiCalendarStartDate() = runBlocking {
|
|
||||||
val modificationTime = DateTime(2021, 1, 21, 9, 50, 4, 348)
|
|
||||||
val taskId = taskDao.insert(newTask(with(MODIFICATION_TIME, modificationTime)))
|
|
||||||
val caldavTask = newCaldavTask(with(TASK, taskId), with(CALENDAR, calendar.uuid))
|
|
||||||
caldavDao.insert(caldavTask)
|
|
||||||
vtodoCache.putVtodo(calendar, caldavTask, VTODO_NO_START_DATE)
|
|
||||||
|
|
||||||
upgrader.applyiCalendarStartDates()
|
|
||||||
|
|
||||||
assertEquals(modificationTime, taskDao.fetch(taskId)?.modificationDate)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun applyRemoteOpenTaskStartDate() = runBlocking {
|
|
||||||
val (listId, list) = openTaskDao.insertList()
|
|
||||||
openTaskDao.insertTask(listId, VTODO_WITH_START_DATE)
|
|
||||||
val taskId = taskDao.insert(newTask())
|
|
||||||
caldavDao.insert(newCaldavTask(
|
|
||||||
with(CALENDAR, list.uuid),
|
|
||||||
with(REMOTE_ID, "4586964443060640060"),
|
|
||||||
with(TASK, taskId)
|
|
||||||
))
|
|
||||||
|
|
||||||
upgrader.applyOpenTaskStartDates()
|
|
||||||
|
|
||||||
assertEquals(DateTime(2021, 1, 21), taskDao.fetch(taskId)?.hideUntil)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun ignoreRemoteOpenTaskStartDate() = runBlocking {
|
|
||||||
val (listId, list) = openTaskDao.insertList()
|
|
||||||
openTaskDao.insertTask(listId, VTODO_WITH_START_DATE)
|
|
||||||
val taskId = taskDao.insert(newTask(
|
|
||||||
with(DUE_DATE, DateTime(2021, 1, 20)),
|
|
||||||
with(HIDE_TYPE, Task.HIDE_UNTIL_DUE)
|
|
||||||
))
|
|
||||||
caldavDao.insert(newCaldavTask(
|
|
||||||
with(CALENDAR, list.uuid),
|
|
||||||
with(REMOTE_ID, "4586964443060640060"),
|
|
||||||
with(TASK, taskId)
|
|
||||||
))
|
|
||||||
|
|
||||||
upgrader.applyOpenTaskStartDates()
|
|
||||||
|
|
||||||
assertEquals(DateTime(2021, 1, 20), taskDao.fetch(taskId)?.hideUntil)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun touchWithOpenTaskStartDate() = runBlocking {
|
|
||||||
val upgradeTime = DateTime(2021, 1, 21, 11, 47, 32, 450)
|
|
||||||
val (listId, list) = openTaskDao.insertList()
|
|
||||||
openTaskDao.insertTask(listId, VTODO_WITH_START_DATE)
|
|
||||||
val taskId = taskDao.insert(newTask(
|
|
||||||
with(DUE_DATE, DateTime(2021, 1, 20)),
|
|
||||||
with(HIDE_TYPE, Task.HIDE_UNTIL_DUE),
|
|
||||||
with(MODIFICATION_TIME, DateTime(2021, 1, 21, 9, 50, 4, 348))
|
|
||||||
))
|
|
||||||
caldavDao.insert(newCaldavTask(
|
|
||||||
with(CALENDAR, list.uuid),
|
|
||||||
with(REMOTE_ID, "4586964443060640060"),
|
|
||||||
with(TASK, taskId)
|
|
||||||
))
|
|
||||||
|
|
||||||
freezeAt(upgradeTime) {
|
|
||||||
upgrader.applyOpenTaskStartDates()
|
|
||||||
}
|
|
||||||
|
|
||||||
assertEquals(upgradeTime, taskDao.fetch(taskId)?.modificationDate)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun dontTouchNoOpenTaskStartDate() = runBlocking {
|
|
||||||
val modificationTime = DateTime(2021, 1, 21, 9, 50, 4, 348)
|
|
||||||
val (listId, list) = openTaskDao.insertList()
|
|
||||||
openTaskDao.insertTask(listId, VTODO_NO_START_DATE)
|
|
||||||
val taskId = taskDao.insert(newTask(with(MODIFICATION_TIME, modificationTime)))
|
|
||||||
caldavDao.insert(newCaldavTask(
|
|
||||||
with(CALENDAR, list.uuid),
|
|
||||||
with(REMOTE_ID, "4586964443060640060"),
|
|
||||||
with(TASK, taskId)
|
|
||||||
))
|
|
||||||
|
|
||||||
upgrader.applyOpenTaskStartDates()
|
|
||||||
|
|
||||||
assertEquals(modificationTime, taskDao.fetch(taskId)?.modificationDate)
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
val VTODO_WITH_START_DATE = """
|
|
||||||
BEGIN:VCALENDAR
|
|
||||||
VERSION:2.0
|
|
||||||
PRODID:+//IDN tasks.org//android-110301//EN
|
|
||||||
BEGIN:VTODO
|
|
||||||
DTSTAMP:20210121T153032Z
|
|
||||||
UID:4586964443060640060
|
|
||||||
CREATED:20210121T153000Z
|
|
||||||
LAST-MODIFIED:20210121T153029Z
|
|
||||||
SUMMARY:Test
|
|
||||||
PRIORITY:9
|
|
||||||
X-APPLE-SORT-ORDER:-27
|
|
||||||
DTSTART;VALUE=DATE:20210121
|
|
||||||
END:VTODO
|
|
||||||
END:VCALENDAR
|
|
||||||
""".trimIndent()
|
|
||||||
|
|
||||||
val VTODO_NO_START_DATE = """
|
|
||||||
BEGIN:VCALENDAR
|
|
||||||
VERSION:2.0
|
|
||||||
PRODID:+//IDN tasks.org//android-110301//EN
|
|
||||||
BEGIN:VTODO
|
|
||||||
DTSTAMP:20210121T153032Z
|
|
||||||
UID:4586964443060640060
|
|
||||||
CREATED:20210121T153000Z
|
|
||||||
LAST-MODIFIED:20210121T153029Z
|
|
||||||
SUMMARY:Test
|
|
||||||
PRIORITY:9
|
|
||||||
X-APPLE-SORT-ORDER:-27
|
|
||||||
END:VTODO
|
|
||||||
END:VCALENDAR
|
|
||||||
""".trimIndent()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,22 +1,21 @@
|
|||||||
package com.todoroo.astrid.sync
|
package com.todoroo.astrid.sync
|
||||||
|
|
||||||
import org.tasks.data.entity.Task
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
import dagger.hilt.android.testing.HiltAndroidTest
|
import com.todoroo.astrid.data.Task
|
||||||
import kotlinx.coroutines.runBlocking
|
|
||||||
import org.junit.Assert.assertNotEquals
|
import org.junit.Assert.assertNotEquals
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
|
||||||
@HiltAndroidTest
|
@RunWith(AndroidJUnit4::class)
|
||||||
class SyncModelTest : NewSyncTestCase() {
|
class SyncModelTest : NewSyncTestCase() {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testCreateTaskMakesUuid() = runBlocking{
|
fun testCreateTaskMakesUuid() {
|
||||||
val task = createTask()
|
val task = createTask()
|
||||||
assertNotEquals(Task.NO_UUID, task.uuid)
|
assertNotEquals(Task.NO_UUID, task.uuid)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testCreateTagMakesUuid() = runBlocking{
|
fun testCreateTagMakesUuid() {
|
||||||
val tag = createTagData()
|
val tag = createTagData()
|
||||||
assertNotEquals(Task.NO_UUID, tag.remoteId)
|
assertNotEquals(Task.NO_UUID, tag.remoteId)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1 +0,0 @@
|
|||||||
../../../../test/java/org/tasks/Freeze.kt
|
|
||||||
@ -1 +0,0 @@
|
|||||||
../../../../test/java/org/tasks/SuspendFreeze.kt
|
|
||||||
@ -1,13 +0,0 @@
|
|||||||
package org.tasks
|
|
||||||
|
|
||||||
import android.app.Application
|
|
||||||
import android.content.Context
|
|
||||||
import androidx.test.runner.AndroidJUnitRunner
|
|
||||||
import dagger.hilt.android.testing.HiltTestApplication
|
|
||||||
|
|
||||||
@Suppress("unused")
|
|
||||||
class TestRunner : AndroidJUnitRunner() {
|
|
||||||
override fun newApplication(cl: ClassLoader?, name: String?, context: Context?): Application {
|
|
||||||
return super.newApplication(cl, HiltTestApplication::class.java.name, context)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1 +0,0 @@
|
|||||||
../../../../test/java/org/tasks/TestUtilities.kt
|
|
||||||
@ -1,18 +1,20 @@
|
|||||||
package org.tasks.caldav
|
package org.tasks.caldav
|
||||||
|
|
||||||
import dagger.hilt.android.testing.HiltAndroidTest
|
import android.content.Context
|
||||||
import kotlinx.coroutines.runBlocking
|
import androidx.test.core.app.ApplicationProvider
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.tasks.injection.InjectingTestCase
|
import org.junit.runner.RunWith
|
||||||
import javax.inject.Inject
|
import org.tasks.TestUtilities.newPreferences
|
||||||
|
import java.security.KeyManagementException
|
||||||
@HiltAndroidTest
|
import java.security.NoSuchAlgorithmException
|
||||||
class CaldavClientTest : InjectingTestCase() {
|
|
||||||
|
|
||||||
@Inject lateinit var clientProvider: CaldavClientProvider
|
|
||||||
|
|
||||||
|
@RunWith(AndroidJUnit4::class)
|
||||||
|
class CaldavClientTest {
|
||||||
@Test
|
@Test
|
||||||
fun dontCrashOnSpaceInUrl(): Unit = runBlocking {
|
fun dontCrashOnSpaceInUrl() {
|
||||||
clientProvider.forUrl("https://example.com/remote.php/a space/", "username", "password")
|
val context = ApplicationProvider.getApplicationContext<Context>()
|
||||||
|
CaldavClient(context, null, newPreferences(context), null)
|
||||||
|
.forUrl("https://example.com/remote.php/a space/", "username", "password")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,220 +0,0 @@
|
|||||||
package org.tasks.caldav
|
|
||||||
|
|
||||||
import com.natpryce.makeiteasy.MakeItEasy.with
|
|
||||||
import org.tasks.data.UUIDHelper
|
|
||||||
import dagger.hilt.android.testing.HiltAndroidTest
|
|
||||||
import kotlinx.coroutines.runBlocking
|
|
||||||
import org.junit.Assert.assertEquals
|
|
||||||
import org.junit.Before
|
|
||||||
import org.junit.Test
|
|
||||||
import org.tasks.data.entity.CaldavAccount
|
|
||||||
import org.tasks.data.entity.CaldavCalendar
|
|
||||||
import org.tasks.makers.CaldavTaskMaker.CALENDAR
|
|
||||||
import org.tasks.makers.CaldavTaskMaker.ETAG
|
|
||||||
import org.tasks.makers.CaldavTaskMaker.OBJECT
|
|
||||||
import org.tasks.makers.CaldavTaskMaker.TASK
|
|
||||||
import org.tasks.makers.CaldavTaskMaker.newCaldavTask
|
|
||||||
import org.tasks.makers.TaskMaker.newTask
|
|
||||||
|
|
||||||
@HiltAndroidTest
|
|
||||||
class CaldavSynchronizerTest : CaldavTest() {
|
|
||||||
|
|
||||||
@Before
|
|
||||||
override fun setUp() = runBlocking {
|
|
||||||
super.setUp()
|
|
||||||
account = CaldavAccount(
|
|
||||||
uuid = UUIDHelper.newUUID(),
|
|
||||||
username = "username",
|
|
||||||
password = encryption.encrypt("password"),
|
|
||||||
url = server.url("/remote.php/dav/calendars/user1/").toString(),
|
|
||||||
).let {
|
|
||||||
it.copy(id = caldavDao.insert(it))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun setMessageOnError() = runBlocking {
|
|
||||||
enqueue()
|
|
||||||
|
|
||||||
synchronizer.sync(account)
|
|
||||||
|
|
||||||
assertEquals("HTTP 500 Server Error", caldavDao.getAccounts().first().error)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun dontFetchCalendarIfCtagMatches() = runBlocking {
|
|
||||||
caldavDao.insert(
|
|
||||||
CaldavCalendar(
|
|
||||||
account = this@CaldavSynchronizerTest.account.uuid,
|
|
||||||
ctag = "http://sabre.io/ns/sync/1",
|
|
||||||
url = "${this@CaldavSynchronizerTest.account.url}test-shared/",
|
|
||||||
)
|
|
||||||
)
|
|
||||||
enqueue(OC_SHARE_PROPFIND)
|
|
||||||
|
|
||||||
sync()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun dontFetchTaskIfEtagMatches() = runBlocking {
|
|
||||||
val calendar = CaldavCalendar(
|
|
||||||
account = this@CaldavSynchronizerTest.account.uuid,
|
|
||||||
uuid = UUIDHelper.newUUID(),
|
|
||||||
url = "${this@CaldavSynchronizerTest.account.url}test-shared/",
|
|
||||||
)
|
|
||||||
caldavDao.insert(calendar)
|
|
||||||
caldavDao.insert(newCaldavTask(
|
|
||||||
with(TASK, taskDao.insert(newTask())),
|
|
||||||
with(OBJECT, "3164728546640386952.ics"),
|
|
||||||
with(ETAG, "43b3ffaac5131880e4dd07a79adba82a"),
|
|
||||||
with(CALENDAR, calendar.uuid)
|
|
||||||
))
|
|
||||||
enqueue(OC_SHARE_PROPFIND, OC_SHARE_REPORT)
|
|
||||||
|
|
||||||
sync()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun syncNewTask() = runBlocking {
|
|
||||||
enqueue(OC_SHARE_PROPFIND, OC_SHARE_REPORT, OC_SHARE_TASK)
|
|
||||||
|
|
||||||
sync()
|
|
||||||
|
|
||||||
val calendar = caldavDao.getCalendars().takeIf { it.size == 1 }!!.first()
|
|
||||||
val caldavTask = caldavDao.getTaskByRemoteId(calendar.uuid!!, "3164728546640386952")!!
|
|
||||||
assertEquals("Test task", taskDao.fetch(caldavTask.task)!!.title)
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
private val OC_SHARE_PROPFIND = """
|
|
||||||
<?xml version="1.0"?>
|
|
||||||
<d:multistatus xmlns:d="DAV:" xmlns:cal="urn:ietf:params:xml:ns:caldav"
|
|
||||||
xmlns:cs="http://calendarserver.org/ns/" xmlns:oc="http://owncloud.org/ns">
|
|
||||||
<d:response>
|
|
||||||
<d:href>/remote.php/dav/calendars/user1/test-shared/</d:href>
|
|
||||||
<d:propstat>
|
|
||||||
<d:prop>
|
|
||||||
<d:resourcetype>
|
|
||||||
<d:collection />
|
|
||||||
<cal:calendar />
|
|
||||||
</d:resourcetype>
|
|
||||||
<d:displayname>Test shared</d:displayname>
|
|
||||||
<cal:supported-calendar-component-set>
|
|
||||||
<cal:comp name="VTODO" />
|
|
||||||
</cal:supported-calendar-component-set>
|
|
||||||
<cs:getctag>http://sabre.io/ns/sync/1</cs:getctag>
|
|
||||||
<x1:calendar-color xmlns:x1="http://apple.com/ns/ical/">#0082c9</x1:calendar-color>
|
|
||||||
<d:sync-token>http://sabre.io/ns/sync/1</d:sync-token>
|
|
||||||
<oc:owner-principal>principals/users/user1</oc:owner-principal>
|
|
||||||
<oc:invite>
|
|
||||||
<oc:user>
|
|
||||||
<d:href>principal:principals/users/user2</d:href>
|
|
||||||
<oc:common-name>user2</oc:common-name>
|
|
||||||
<oc:invite-accepted />
|
|
||||||
<oc:access>
|
|
||||||
<oc:read />
|
|
||||||
</oc:access>
|
|
||||||
</oc:user>
|
|
||||||
</oc:invite>
|
|
||||||
<d:current-user-privilege-set>
|
|
||||||
<d:privilege>
|
|
||||||
<d:write />
|
|
||||||
</d:privilege>
|
|
||||||
<d:privilege>
|
|
||||||
<d:write-properties />
|
|
||||||
</d:privilege>
|
|
||||||
<d:privilege>
|
|
||||||
<d:write-content />
|
|
||||||
</d:privilege>
|
|
||||||
<d:privilege>
|
|
||||||
<d:unlock />
|
|
||||||
</d:privilege>
|
|
||||||
<d:privilege>
|
|
||||||
<d:bind />
|
|
||||||
</d:privilege>
|
|
||||||
<d:privilege>
|
|
||||||
<d:unbind />
|
|
||||||
</d:privilege>
|
|
||||||
<d:privilege>
|
|
||||||
<d:write-acl />
|
|
||||||
</d:privilege>
|
|
||||||
<d:privilege>
|
|
||||||
<d:read />
|
|
||||||
</d:privilege>
|
|
||||||
<d:privilege>
|
|
||||||
<d:read-acl />
|
|
||||||
</d:privilege>
|
|
||||||
<d:privilege>
|
|
||||||
<d:read-current-user-privilege-set />
|
|
||||||
</d:privilege>
|
|
||||||
<d:privilege>
|
|
||||||
<cal:read-free-busy />
|
|
||||||
</d:privilege>
|
|
||||||
</d:current-user-privilege-set>
|
|
||||||
<d:current-user-principal>
|
|
||||||
<d:href>/remote.php/dav/principals/users/user1/</d:href>
|
|
||||||
</d:current-user-principal>
|
|
||||||
</d:prop>
|
|
||||||
<d:status>HTTP/1.1 200 OK</d:status>
|
|
||||||
</d:propstat>
|
|
||||||
<d:propstat>
|
|
||||||
<d:prop>
|
|
||||||
<d:share-access />
|
|
||||||
<d:invite />
|
|
||||||
</d:prop>
|
|
||||||
<d:status>HTTP/1.1 404 Not Found</d:status>
|
|
||||||
</d:propstat>
|
|
||||||
</d:response>
|
|
||||||
</d:multistatus>
|
|
||||||
""".trimIndent()
|
|
||||||
|
|
||||||
private val OC_SHARE_REPORT = """
|
|
||||||
<?xml version="1.0"?>
|
|
||||||
<d:multistatus xmlns:d="DAV:">
|
|
||||||
<d:response>
|
|
||||||
<d:href>/remote.php/dav/calendars/user1/test-shared/3164728546640386952.ics</d:href>
|
|
||||||
<d:propstat>
|
|
||||||
<d:prop>
|
|
||||||
<d:getetag>"43b3ffaac5131880e4dd07a79adba82a"</d:getetag>
|
|
||||||
</d:prop>
|
|
||||||
<d:status>HTTP/1.1 200 OK</d:status>
|
|
||||||
</d:propstat>
|
|
||||||
</d:response>
|
|
||||||
</d:multistatus>
|
|
||||||
""".trimIndent()
|
|
||||||
|
|
||||||
private val OC_SHARE_TASK = """
|
|
||||||
<?xml version="1.0"?>
|
|
||||||
<d:multistatus xmlns:d="DAV:" xmlns:cal="urn:ietf:params:xml:ns:caldav">
|
|
||||||
<d:response>
|
|
||||||
<d:href>/remote.php/dav/calendars/user1/test-shared/3164728546640386952.ics</d:href>
|
|
||||||
<d:propstat>
|
|
||||||
<d:prop>
|
|
||||||
<d:getcontenttype>text/calendar; charset=utf-8; component=vtodo</d:getcontenttype>
|
|
||||||
<d:getetag>"43b3ffaac5131880e4dd07a79adba82a"</d:getetag>
|
|
||||||
<cal:calendar-data>BEGIN:VCALENDAR
|
|
||||||
VERSION:2.0
|
|
||||||
PRODID:+//IDN tasks.org//android-110500//EN
|
|
||||||
BEGIN:VTODO
|
|
||||||
DTSTAMP:20210223T154147Z
|
|
||||||
UID:3164728546640386952
|
|
||||||
CREATED:20210223T154134Z
|
|
||||||
LAST-MODIFIED:20210223T154140Z
|
|
||||||
SUMMARY:Test task
|
|
||||||
PRIORITY:9
|
|
||||||
END:VTODO
|
|
||||||
END:VCALENDAR</cal:calendar-data>
|
|
||||||
</d:prop>
|
|
||||||
<d:status>HTTP/1.1 200 OK</d:status>
|
|
||||||
</d:propstat>
|
|
||||||
<d:propstat>
|
|
||||||
<d:prop>
|
|
||||||
<cal:schedule-tag />
|
|
||||||
</d:prop>
|
|
||||||
<d:status>HTTP/1.1 404 Not Found</d:status>
|
|
||||||
</d:propstat>
|
|
||||||
</d:response>
|
|
||||||
</d:multistatus>
|
|
||||||
""".trimIndent()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,68 +0,0 @@
|
|||||||
package org.tasks.caldav
|
|
||||||
|
|
||||||
import com.todoroo.astrid.dao.TaskDao
|
|
||||||
import junit.framework.Assert.assertFalse
|
|
||||||
import okhttp3.mockwebserver.MockResponse
|
|
||||||
import okhttp3.mockwebserver.MockWebServer
|
|
||||||
import org.junit.After
|
|
||||||
import org.junit.Before
|
|
||||||
import org.junit.Rule
|
|
||||||
import org.junit.rules.Timeout
|
|
||||||
import org.tasks.R
|
|
||||||
import org.tasks.data.entity.CaldavAccount
|
|
||||||
import org.tasks.data.dao.CaldavDao
|
|
||||||
import org.tasks.injection.InjectingTestCase
|
|
||||||
import org.tasks.preferences.Preferences
|
|
||||||
import org.tasks.security.KeyStoreEncryption
|
|
||||||
import javax.inject.Inject
|
|
||||||
|
|
||||||
abstract class CaldavTest : InjectingTestCase() {
|
|
||||||
@Inject lateinit var synchronizer: CaldavSynchronizer
|
|
||||||
@Inject lateinit var encryption: KeyStoreEncryption
|
|
||||||
@Inject lateinit var preferences: Preferences
|
|
||||||
@Inject lateinit var caldavDao: CaldavDao
|
|
||||||
@Inject lateinit var taskDao: TaskDao
|
|
||||||
protected val server = MockWebServer()
|
|
||||||
protected lateinit var account: CaldavAccount
|
|
||||||
|
|
||||||
@get:Rule
|
|
||||||
val globalTimeout: Timeout = Timeout.seconds(30)
|
|
||||||
|
|
||||||
@Before
|
|
||||||
override fun setUp() {
|
|
||||||
super.setUp()
|
|
||||||
|
|
||||||
preferences.setBoolean(R.string.p_debug_pro, true)
|
|
||||||
server.start()
|
|
||||||
headers.clear()
|
|
||||||
}
|
|
||||||
|
|
||||||
@After
|
|
||||||
fun after() = server.shutdown()
|
|
||||||
|
|
||||||
protected suspend fun sync(account: CaldavAccount = this.account) {
|
|
||||||
synchronizer.sync(account)
|
|
||||||
|
|
||||||
assertFalse(caldavDao.getAccountByUuid(account.uuid!!)!!.hasError)
|
|
||||||
}
|
|
||||||
|
|
||||||
val headers = HashMap<String, String>()
|
|
||||||
|
|
||||||
protected fun enqueue(vararg responses: String) {
|
|
||||||
responses.forEach {
|
|
||||||
server.enqueue(
|
|
||||||
MockResponse()
|
|
||||||
.setResponseCode(207)
|
|
||||||
.setHeader("Content-Type", "text/xml; charset=\"utf-8\"")
|
|
||||||
.apply { this@CaldavTest.headers.forEach { (k, v) -> setHeader(k, v) } }
|
|
||||||
.setBody(it))
|
|
||||||
}
|
|
||||||
server.enqueue(MockResponse().setResponseCode(500))
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
init {
|
|
||||||
CaldavSynchronizer.registerFactories()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue